REMORA
Regional Modeling of Oceans Refined Adaptively
Loading...
Searching...
No Matches
REMORA_fennel_roms.F
Go to the documentation of this file.
1#include "remora_bio_cppdefs.h"
2!
3!=======================================================================
4! REMORA Fennel Fortran bridge (Path A oracle).
5!
6! This single translation unit contains, in order:
7!
8! 1. Minimal stand-ins for the ROMS modules that fennel.h and
9! fennel_mod.h reference (mod_kinds, mod_param, mod_scalars,
10! mod_iounits, mod_ncparam, mod_grid, mod_forces, mod_ocean,
11! mod_stepping, dateclock_mod). These exist only to let the tracked
12! ROMS sources compile and run outside ROMS. They are NOT faithful
13! reproductions of the ROMS modules; they carry exactly the symbols
14! the two included files touch, and nothing more.
15!
16! 2. MODULE mod_biology, whose body is the tracked copy
17! REMORA_fennel_mod.h, included verbatim, plus the two bridge
18! diagnostic emitters appended inside its CONTAINS section.
19!
20! 3. MODULE biology_mod, whose body is the tracked copy
21! REMORA_fennel.h, included verbatim.
22!
23! 4. MODULE remora_fennel_isohelper, the iso_c_binding wrapper. All
24! adaptation logic -- unit handling, index-base translation, packing
25! REMORA's split old/new state into the ROMS three-slot tracer array,
26! and runtime validation of the compiled option set -- lives here, so
27! the ROMS sources stay unmodified apart from the tag emitters.
28!
29! Everything is kept in one file because AMReX's GNUmake dependency scanner
30! resolves module dependencies for .f90/.F90 sources only; .F (fixed-form)
31! sources are compiled without inter-file module ordering. A single
32! translation unit removes the ordering problem entirely.
33!
34! Build: USE_FENNEL_FORT=TRUE (GNUmake). See Exec/Make.REMORA.
35!=======================================================================
36!
37 MODULE mod_kinds
38!
39! ROMS numeric kinds. Values match ROMS mod_kinds.F.
40!
41 implicit none
42 PUBLIC
43!
44 integer, parameter :: i8 = selected_int_kind(8)
45 integer, parameter :: r4 = selected_real_kind(6,30)
46 integer, parameter :: r8 = selected_real_kind(12,300)
47 integer, parameter :: dp = selected_real_kind(12,300)
48!
49 END MODULE mod_kinds
50!
51!=======================================================================
52!
53 MODULE mod_param
54!
55! Grid, tile and tracer dimensioning. Ngrids is fixed at 1: the bridge
56! validates one REMORA level per call and has no ROMS nesting.
57!
58 USE mod_kinds
59!
60 implicit none
61 PUBLIC
62!
63 integer, parameter :: ngrids = 1
64 integer, parameter :: inlm = 1
65!
66! Tracer counts. NAT is temperature and salinity; REMORA has no passive,
67! cohesive or non-cohesive sediment tracers, so NPT/NCS/NNS are zero.
68! NBT is assigned by initialize_biology from the compiled option set.
69!
70 integer, parameter :: nat = 2
71 integer, parameter :: npt = 0
72 integer, parameter :: ncs = 0
73 integer, parameter :: nns = 0
74!
75 integer :: nbt = 0
76 integer :: ndbio2d = 0
77 integer :: ndbio3d = 0
78!
79 integer :: n(ngrids)
80 integer :: nt(ngrids)
81!
82! Dynamic-memory accounting. fennel_mod.h increments Dmem(1) on every
83! allocation; the bridge never reports it.
84!
85 real(r8) :: dmem(ngrids) = 0.0_r8
86!
87! Header-name bookkeeping referenced by SUBROUTINE biology. In ROMS these
88! live in mod_scalars and mod_iounits; they are hosted here because
89! SUBROUTINE biology does not USE those modules.
90!
91 logical :: lbiofile(4) = .true.
92 character (len=256) :: bioname(4) = ' '
93!
94! Tile bounds. The bridge always presents a single tile, index 0, whose
95! extents are the REMORA tilebox. Indices are REMORA's own 0-based i and j,
96! so no horizontal index translation happens anywhere in the bridge.
97!
99 integer :: istr(0:0)
100 integer :: iend(0:0)
101 integer :: jstr(0:0)
102 integer :: jend(0:0)
103 integer :: lbi(0:0)
104 integer :: ubi(0:0)
105 integer :: lbj(0:0)
106 integer :: ubj(0:0)
107 END TYPE t_bounds
108!
110!
111 END MODULE mod_param
112!
113!=======================================================================
114!
116!
117! Time-stepping and physical scalars used by biology_tile, plus the
118! bridge's runtime diagnostic state.
119!
120 USE mod_kinds
121 USE mod_param, ONLY : ngrids
122!
123 implicit none
124 PUBLIC
125!
126 real(r8), parameter :: sec2day = 1.0_r8/86400.0_r8
127!
128 real(r8) :: dt(ngrids) = 0.0_r8
129 real(dp) :: tdays(ngrids) = 0.0_dp
130 integer :: iic(ngrids) = 0
131!
132! Reference density and specific heat. fennel.h uses these to convert
133! ROMS kinematic srflx (degC m/s) into W/m2 before applying PARfrac.
134!
135 real(r8) :: rho0 = 1025.0_r8
136 real(r8) :: cp = 3985.0_r8
137!
138!-----------------------------------------------------------------------
139! Bridge diagnostic state (not part of ROMS).
140!
141! Two-level gating. REMORA_USE_BIOLOGY_DIAG decides at compile time whether
142! the emitters exist at all (off by default, because the C++ half of the
143! contract sits inside a GPU device lambda); these variables then select
144! verbosity at run time, so a validation campaign never rebuilds to change
145! paths or levels. Set by the isohelper on every call from the values the
146! C++ side parsed out of remora.biology_debug*. They are declared
147! unconditionally so the bind(C) signature stays fixed regardless of the
148! flag.
149!
150! bio_dbg_level 0 off, 1 target column only, 2 every column
151! bio_dbg_i/j target column, REMORA 0-based indices
152!-----------------------------------------------------------------------
153!
154 integer :: bio_dbg_level = 0
155 integer :: bio_dbg_i = 0
156 integer :: bio_dbg_j = 0
157!
158 END MODULE mod_scalars
159!
160!=======================================================================
161!
163!
164 implicit none
165 PUBLIC
166!
167 integer :: stdout = 6
168!
169 END MODULE mod_iounits
170!
171!=======================================================================
172!
174!
175! Tracer metadata indices. Only the two active-tracer indices are needed;
176! biology_tile uses them to read temperature and salinity out of t.
177!
178 implicit none
179 PUBLIC
180!
181 integer, parameter :: itemp = 1
182 integer, parameter :: isalt = 2
183!
184 END MODULE mod_ncparam
185!
186!=======================================================================
187!
189!
190! ROMS mod_biology. Body is the tracked copy of fennel_mod.h, included
191! verbatim; the bridge diagnostic emitters are appended to its CONTAINS
192! section below the include.
193!
194#include "REMORA_fennel_mod.h"
195!
196!-----------------------------------------------------------------------
197! Bridge diagnostic emitters (not part of ROMS).
198!
199! Format contract is frozen in Source/Biology/Fortran/tag_map.md and must
200! stay byte-identical to remora_fennel_tag() on the C++ side:
201!
202! FENNEL-FORT <TAG> iter=%3d i=%5d j=%5d k=%4d <VAR> <ES26.17E2>
203!
204! k is printed in REMORA's 0-based convention (Fortran k minus one) so the
205! two logs diff without an index map. Values are printed unmodified.
206!-----------------------------------------------------------------------
207!
208 SUBROUTINE fennel_tag (tag, iter, Istr, Iend, j,
209 & IminS, ImaxS, UBk, UBt, Bio)
210!
211! Body is gated, not the twelve call sites in REMORA_fennel.h. That file is
212! the tracked copy of ROMS fennel.h and its diff against the pristine
213! ../fennel.h is exactly 36 added lines with zero deletions; wrapping each
214! call site would double that and make the copy harder to audit. The
215! residual cost of a no-op call per block seam per j is negligible, and the
216! Fortran path only exists in bridge builds anyway.
217!
218#ifdef REMORA_USE_BIOLOGY_DIAG
220#endif
221!
222 character (len=*), intent(in) :: tag
223 integer, intent(in) :: iter, Istr, Iend, j
224 integer, intent(in) :: IminS, ImaxS, UBk, UBt
225 real(r8), intent(in) :: Bio(IminS:ImaxS,UBk,UBt)
226!
227#ifdef REMORA_USE_BIOLOGY_DIAG
228 integer :: i, k
229!
230 IF (bio_dbg_level.le.0) RETURN
231 IF ((bio_dbg_level.eq.1).and.(j.ne.bio_dbg_j)) RETURN
232!
233 DO i=istr,iend
234 IF ((bio_dbg_level.eq.1).and.(i.ne.bio_dbg_i)) cycle
235 DO k=1,ubk
236 CALL fennel_tag_line (tag, iter, i, j, k, 'NO3 ',
237 & bio(i,k,ino3_))
238 CALL fennel_tag_line (tag, iter, i, j, k, 'NH4 ',
239 & bio(i,k,inh4_))
240 CALL fennel_tag_line (tag, iter, i, j, k, 'CHLO',
241 & bio(i,k,ichlo))
242 CALL fennel_tag_line (tag, iter, i, j, k, 'PHYT',
243 & bio(i,k,iphyt))
244 CALL fennel_tag_line (tag, iter, i, j, k, 'ZOOP',
245 & bio(i,k,izoop))
246 CALL fennel_tag_line (tag, iter, i, j, k, 'LDEN',
247 & bio(i,k,ilden))
248 CALL fennel_tag_line (tag, iter, i, j, k, 'SDEN',
249 & bio(i,k,isden))
250#ifdef RIVER_DON
251 CALL fennel_tag_line (tag, iter, i, j, k, 'RDEN',
252 & bio(i,k,irden))
253#endif
254#ifdef PO4
255 CALL fennel_tag_line (tag, iter, i, j, k, 'PO4 ',
256 & bio(i,k,ipo4_))
257#endif
258#ifdef CARBON
259 CALL fennel_tag_line (tag, iter, i, j, k, 'LDEC',
260 & bio(i,k,ildec))
261 CALL fennel_tag_line (tag, iter, i, j, k, 'SDEC',
262 & bio(i,k,isdec))
263 CALL fennel_tag_line (tag, iter, i, j, k, 'TIC ',
264 & bio(i,k,itic_))
265 CALL fennel_tag_line (tag, iter, i, j, k, 'TALK',
266 & bio(i,k,italk))
267# ifdef RIVER_DON
268 CALL fennel_tag_line (tag, iter, i, j, k, 'RDEC',
269 & bio(i,k,irdec))
270# endif
271#endif
272#ifdef OXYGEN
273 CALL fennel_tag_line (tag, iter, i, j, k, 'OXYG',
274 & bio(i,k,ioxyg))
275#endif
276#ifdef ODU
277 CALL fennel_tag_line (tag, iter, i, j, k, 'ODU ',
278 & bio(i,k,iodu_))
279#endif
280 END DO
281 END DO
282#endif
283!
284 RETURN
285 END SUBROUTINE fennel_tag
286!
287 SUBROUTINE fennel_tag_t (tag, iter, Istr, Iend, j,
288 & LBi, UBi, LBj, UBj, UBk, UBt, nnew, t)
289!
290! As fennel_tag, but reads the updated global tracer array rather than the
291! Bio scratch array. Used for G11_UPDATE, the only tag emitted after the
292! Hz-weighted increment has been applied to t(nnew).
293!
294#ifdef REMORA_USE_BIOLOGY_DIAG
296#endif
297!
298 character (len=*), intent(in) :: tag
299 integer, intent(in) :: iter, Istr, Iend, j
300 integer, intent(in) :: LBi, UBi, LBj, UBj, UBk, UBt, nnew
301 real(r8), intent(in) :: t(LBi:UBi,LBj:UBj,UBk,3,UBt)
302!
303#ifdef REMORA_USE_BIOLOGY_DIAG
304 integer :: i, k
305!
306 IF (bio_dbg_level.le.0) RETURN
307 IF ((bio_dbg_level.eq.1).and.(j.ne.bio_dbg_j)) RETURN
308!
309 DO i=istr,iend
310 IF ((bio_dbg_level.eq.1).and.(i.ne.bio_dbg_i)) cycle
311 DO k=1,ubk
312 CALL fennel_tag_line (tag, iter, i, j, k, 'NO3 ',
313 & t(i,j,k,nnew,ino3_))
314 CALL fennel_tag_line (tag, iter, i, j, k, 'NH4 ',
315 & t(i,j,k,nnew,inh4_))
316 CALL fennel_tag_line (tag, iter, i, j, k, 'CHLO',
317 & t(i,j,k,nnew,ichlo))
318 CALL fennel_tag_line (tag, iter, i, j, k, 'PHYT',
319 & t(i,j,k,nnew,iphyt))
320 CALL fennel_tag_line (tag, iter, i, j, k, 'ZOOP',
321 & t(i,j,k,nnew,izoop))
322 CALL fennel_tag_line (tag, iter, i, j, k, 'LDEN',
323 & t(i,j,k,nnew,ilden))
324 CALL fennel_tag_line (tag, iter, i, j, k, 'SDEN',
325 & t(i,j,k,nnew,isden))
326#ifdef RIVER_DON
327 CALL fennel_tag_line (tag, iter, i, j, k, 'RDEN',
328 & t(i,j,k,nnew,irden))
329#endif
330#ifdef PO4
331 CALL fennel_tag_line (tag, iter, i, j, k, 'PO4 ',
332 & t(i,j,k,nnew,ipo4_))
333#endif
334#ifdef CARBON
335 CALL fennel_tag_line (tag, iter, i, j, k, 'LDEC',
336 & t(i,j,k,nnew,ildec))
337 CALL fennel_tag_line (tag, iter, i, j, k, 'SDEC',
338 & t(i,j,k,nnew,isdec))
339 CALL fennel_tag_line (tag, iter, i, j, k, 'TIC ',
340 & t(i,j,k,nnew,itic_))
341 CALL fennel_tag_line (tag, iter, i, j, k, 'TALK',
342 & t(i,j,k,nnew,italk))
343# ifdef RIVER_DON
344 CALL fennel_tag_line (tag, iter, i, j, k, 'RDEC',
345 & t(i,j,k,nnew,irdec))
346# endif
347#endif
348#ifdef OXYGEN
349 CALL fennel_tag_line (tag, iter, i, j, k, 'OXYG',
350 & t(i,j,k,nnew,ioxyg))
351#endif
352#ifdef ODU
353 CALL fennel_tag_line (tag, iter, i, j, k, 'ODU ',
354 & t(i,j,k,nnew,iodu_))
355#endif
356 END DO
357 END DO
358#endif
359!
360 RETURN
361 END SUBROUTINE fennel_tag_t
362!
363 SUBROUTINE fennel_tag_line (tag, iter, i, j, k, var, val)
364!
365! Single record. k is converted from the ROMS 1-based bottom-up index to
366! REMORA's 0-based bottom-up index here, and nowhere else.
367!
368 character (len=*), intent(in) :: tag, var
369 integer, intent(in) :: iter, i, j, k
370 real(r8), intent(in) :: val
371!
372#ifdef REMORA_USE_BIOLOGY_DIAG
373! Re-pad the tag and variable names into fixed-width locals before writing.
374!
375! This is load-bearing, not cosmetic. AMReX compiles .F sources through
376! "cpp -E -traditional-cpp -P", which collapses runs of two or more spaces
377! even inside Fortran character literals. The 10-character tag literals in
378! REMORA_fennel.h therefore reach the compiler at whatever length survives
379! that collapse ('G00_PRE ' arrives as 'G00_PRE ', while 'G03_ZMETAB'
380! arrives intact). Edit descriptor Aw right-justifies a string shorter than
381! w, so writing the dummy directly would place the tag at a column that
382! varies from tag to tag and never matches the C++ "%-10s".
383!
384! Character assignment blank-pads on the right, so assigning into a
385! fixed-length local restores left justification without depending on any
386! literal surviving preprocessing.
387!
388 character (len=10) :: tagf
389 character (len=4) :: varf
390!
391 tagf = tag
392 varf = var
393!
394 write (6,10) tagf, iter, i, j, k-1, varf, val
395 10 format ('FENNEL-FORT ',a10,' iter=',i3,' i=',i5,' j=',i5,
396 & ' k=',i4,' ',a4,' ',es26.17e2)
397#endif
398!
399 RETURN
400 END SUBROUTINE fennel_tag_line
401!
402 END MODULE mod_biology
403!
404!=======================================================================
405!
406 MODULE mod_grid
407!
408! Referenced only by SUBROUTINE biology, which the bridge never calls.
409!
410 USE mod_kinds
411 USE mod_param, ONLY : ngrids
412!
413 implicit none
414 PUBLIC
415!
417 real(r8), pointer :: rmask(:,:)
418 real(r8), pointer :: hz(:,:,:)
419 real(r8), pointer :: z_r(:,:,:)
420 real(r8), pointer :: z_w(:,:,:)
421 END TYPE t_grid
422!
423 TYPE (t_grid) :: grid(ngrids)
424!
425 END MODULE mod_grid
426!
427!=======================================================================
428!
430!
431 USE mod_kinds
432 USE mod_param, ONLY : ngrids
433!
434 implicit none
435 PUBLIC
436!
438 real(r8), pointer :: srflx(:,:)
439 real(r8), pointer :: sustr(:,:)
440 real(r8), pointer :: svstr(:,:)
441 real(r8), pointer :: uwind(:,:)
442 real(r8), pointer :: vwind(:,:)
443 END TYPE t_forces
444!
446!
447 END MODULE mod_forces
448!
449!=======================================================================
450!
452!
453 USE mod_kinds
454 USE mod_param, ONLY : ngrids
455!
456 implicit none
457 PUBLIC
458!
460 real(r8), pointer :: t(:,:,:,:,:)
461 real(r8), pointer :: ph(:,:)
462 END TYPE t_ocean
463!
465!
466 END MODULE mod_ocean
467!
468!=======================================================================
469!
471!
472 USE mod_param, ONLY : ngrids
473!
474 implicit none
475 PUBLIC
476!
477 integer :: nstp(ngrids) = 1
478 integer :: nnew(ngrids) = 2
479!
480 END MODULE mod_stepping
481!
482!=======================================================================
483!
485!
486! Stub. caldate is referenced by fennel.h only under PCO2AIR_SECULAR,
487! which the bridge does not enable. Present so the tracked copy compiles
488! unmodified if that option is ever turned on for a separate lane.
489!
490 USE mod_kinds
491!
492 implicit none
493 PUBLIC :: caldate
494!
495 CONTAINS
496!
497 SUBROUTINE caldate (CurrentTime, yy_i, yd_i, mm_i, dd_i,
498 & h_i, m_i, s_r, yd_dp)
499!
500! Stub. fennel.h calls caldate only to date the atmospheric pCO2, and the
501! bridge is built with neither PCO2AIR_DATA nor PCO2AIR_SECULAR, so the
502! return value is unused and a fixed date is honest. fennel_check_config
503! refuses a run whose pco2air_type is not constant, which keeps it that way.
504! A lane that does define one of those options must implement this properly.
505! Source/Utils/dateclock.F is the ROMS original and Source/Utils/
506! REMORA_DateClock.H is the C++ port of its time_ref = 0 branch, which is what
507! the native path uses; either could back a real implementation here.
508!
509 real(dp), intent(in) :: currenttime
510 integer, intent(out), optional :: yy_i, yd_i, mm_i, dd_i
511 integer, intent(out), optional :: h_i, m_i
512 real(r8), intent(out), optional :: s_r
513 real(dp), intent(out), optional :: yd_dp
514!
515 IF (PRESENT(yy_i)) yy_i=1951
516 IF (PRESENT(yd_i)) yd_i=1
517 IF (PRESENT(mm_i)) mm_i=1
518 IF (PRESENT(dd_i)) dd_i=1
519 IF (PRESENT(h_i)) h_i=0
520 IF (PRESENT(m_i)) m_i=0
521 IF (PRESENT(s_r)) s_r=0.0_r8
522 IF (PRESENT(yd_dp)) yd_dp=1.0_dp
523!
524 RETURN
525 END SUBROUTINE caldate
526!
527 END MODULE dateclock_mod
528!
529!=======================================================================
530!
532!
533! ROMS biology.F. Body is the tracked copy of fennel.h, included verbatim
534! apart from the fennel_tag calls at the frozen block seams (tag_map.md).
535!
536 implicit none
537!
538 CONTAINS
539!
540#include "REMORA_fennel.h"
541!
542 END MODULE biology_mod
543!
544!=======================================================================
545!
547!
548! iso_c_binding wrapper. Everything that adapts REMORA's data model to the
549! ROMS calling convention lives here:
550!
551! * REMORA splits the tracer state across two MultiFabs (cons_old at nstp,
552! cons_new at nnew); ROMS carries one array with a three-slot time index.
553! The wrapper packs slot 1 from t_nstp and slot 2 from t_nnew, calls with
554! nstp=1/nnew=2, and copies slot 2 back out.
555! * REMORA k runs 0..N bottom-up; ROMS k runs 1..N(ng) bottom-up. The C++
556! packer applies the +1 shift when filling the buffers, so inside this
557! module every k is already ROMS-based.
558! * i and j are passed through as REMORA's own 0-based indices. Fortran
559! lower bounds are set to match, so no horizontal translation occurs.
560! * The compiled ROMS option set is validated against the C++ runtime
561! toggles on every call.
562!
563 USE iso_c_binding
564 USE mod_kinds
565 USE mod_param
566 USE mod_scalars
567 USE mod_biology
568 USE mod_ncparam, ONLY : itemp, isalt
569 USE biology_mod, ONLY : biology_tile
570!
571 implicit none
572!
573 CONTAINS
574!
576 & ilo, ihi, jlo, jhi, nz, nbt_in,
577 & istr_in, iend_in, jstr_in, jend_in,
578 & use_po4, use_carbon, use_oxygen, use_odu,
579 & use_denit, use_biosed, use_bulk,
580 & use_rivdon, use_talknc,
581 & pco2air_kind, co2_sc_kind, o2_sc_kind,
582 & bioiter_in, dt_sec, rho0_in, cp_in,
583 & p_AttSW, p_AttChl, p_PARfrac, p_Vp0, p_I_thNH4, p_D_p5NH4,
584 & p_NitriR, p_K_NO3, p_K_NH4, p_K_PO4, p_K_Phy, p_Chl2C_m,
585 & p_ChlMin, p_PhyCN, p_R_P2N, p_PhyIP, p_PhyIS, p_PhyMin,
586 & p_PhyMR, p_ZooAE_N, p_ZooCN, p_ZooBM, p_ZooER, p_ZooGR,
587 & p_ZooMin, p_ZooMR, p_LDeRRN, p_LDeRRC, p_CoagR, p_SDeRRN,
588 & p_SDeRRC, p_RDeRRN, p_RDeRRC,
589 & p_wPhy, p_wLDet, p_wSDet, p_pCO2air,
590 & rmask, Hz, z_r, z_w, srflx, sustr, svstr, uwind, vwind, pH,
591 & t_nstp, t_nnew,
592 & dbg_level, dbg_i, dbg_j)
593 & bind(C, name="fennel_bridge_advance_c")
594!
595! Storage bounds (ilo..ihi, jlo..jhi, nz) and active bounds
596! (istr_in..iend_in, jstr_in..jend_in) are passed separately. The bridge
597! currently packs exactly the tilebox, so they coincide, but keeping them
598! distinct preserves the ROMS memory-vs-compute distinction and leaves room
599! for a haloed pack later without an interface change.
600!
601 integer(C_INT), VALUE, intent(in) :: ilo, ihi, jlo, jhi, nz
602 integer(C_INT), VALUE, intent(in) :: nbt_in
603 integer(C_INT), VALUE, intent(in) :: istr_in, iend_in
604 integer(C_INT), VALUE, intent(in) :: jstr_in, jend_in
605 integer(C_INT), VALUE, intent(in) :: use_po4, use_carbon
606 integer(C_INT), VALUE, intent(in) :: use_oxygen, use_odu
607 integer(C_INT), VALUE, intent(in) :: use_denit, use_biosed
608 integer(C_INT), VALUE, intent(in) :: use_bulk
609 integer(C_INT), VALUE, intent(in) :: use_rivdon, use_talknc
610 integer(C_INT), VALUE, intent(in) :: pco2air_kind
611 integer(C_INT), VALUE, intent(in) :: co2_sc_kind, o2_sc_kind
612 integer(C_INT), VALUE, intent(in) :: bioiter_in
613 real(C_DOUBLE), VALUE, intent(in) :: dt_sec, rho0_in, cp_in
614 real(C_DOUBLE), VALUE, intent(in) :: p_AttSW, p_AttChl, p_PARfrac
615 real(C_DOUBLE), VALUE, intent(in) :: p_Vp0, p_I_thNH4, p_D_p5NH4
616 real(C_DOUBLE), VALUE, intent(in) :: p_NitriR, p_K_NO3, p_K_NH4
617 real(C_DOUBLE), VALUE, intent(in) :: p_K_PO4, p_K_Phy, p_Chl2C_m
618 real(C_DOUBLE), VALUE, intent(in) :: p_ChlMin, p_PhyCN, p_R_P2N
619 real(C_DOUBLE), VALUE, intent(in) :: p_PhyIP, p_PhyIS, p_PhyMin
620 real(C_DOUBLE), VALUE, intent(in) :: p_PhyMR, p_ZooAE_N, p_ZooCN
621 real(C_DOUBLE), VALUE, intent(in) :: p_ZooBM, p_ZooER, p_ZooGR
622 real(C_DOUBLE), VALUE, intent(in) :: p_ZooMin, p_ZooMR, p_LDeRRN
623 real(C_DOUBLE), VALUE, intent(in) :: p_LDeRRC, p_CoagR, p_SDeRRN
624 real(C_DOUBLE), VALUE, intent(in) :: p_SDeRRC
625 real(C_DOUBLE), VALUE, intent(in) :: p_RDeRRN, p_RDeRRC
626 real(C_DOUBLE), VALUE, intent(in) :: p_wPhy, p_wLDet
627 real(C_DOUBLE), VALUE, intent(in) :: p_wSDet, p_pCO2air
628 integer(C_INT), VALUE, intent(in) :: dbg_level, dbg_i, dbg_j
629!
630 real(C_DOUBLE), intent(in) :: rmask(ilo:ihi,jlo:jhi)
631 real(C_DOUBLE), intent(in) :: Hz(ilo:ihi,jlo:jhi,nz)
632 real(C_DOUBLE), intent(in) :: z_r(ilo:ihi,jlo:jhi,nz)
633 real(C_DOUBLE), intent(in) :: z_w(ilo:ihi,jlo:jhi,0:nz)
634 real(C_DOUBLE), intent(in) :: srflx(ilo:ihi,jlo:jhi)
635!
636! fennel.h reads sustr(i+1,j) and svstr(i,j+1) when averaging the surface
637! stress to cell centres for the gas-exchange transfer velocity, so both
638! stress buffers are packed one cell wider in each direction.
639!
640 real(C_DOUBLE), intent(in) :: sustr(ilo:ihi+1,jlo:jhi+1)
641 real(C_DOUBLE), intent(in) :: svstr(ilo:ihi+1,jlo:jhi+1)
642 real(C_DOUBLE), intent(in) :: uwind(ilo:ihi,jlo:jhi)
643 real(C_DOUBLE), intent(in) :: vwind(ilo:ihi,jlo:jhi)
644 real(C_DOUBLE), intent(inout) :: pH(ilo:ihi,jlo:jhi)
645 real(C_DOUBLE), intent(in) ::
646 & t_nstp(ilo:ihi,jlo:jhi,nz,nbt_in+2)
647 real(C_DOUBLE), intent(inout) ::
648 & t_nnew(ilo:ihi,jlo:jhi,nz,nbt_in+2)
649!
650 integer, parameter :: ng = 1
651 integer, parameter :: tile = 0
652 integer, parameter :: kstp = 1
653 integer, parameter :: knew = 2
654!
655 integer :: i, j, k, itrc, ntrc
656!
657 real(r8), allocatable :: t(:,:,:,:,:)
658!
659 ntrc=nbt_in+nat
660!
661!-----------------------------------------------------------------------
662! Publish grid, time-step and tile geometry into the stub modules.
663!-----------------------------------------------------------------------
664!
665 n(ng)=nz
666 nt(ng)=ntrc
667 dt(ng)=real(dt_sec,r8)
668 rho0=real(rho0_in,r8)
669 cp=real(cp_in,r8)
670!
671 bounds(ng)%Istr(tile)=istr_in
672 bounds(ng)%Iend(tile)=iend_in
673 bounds(ng)%Jstr(tile)=jstr_in
674 bounds(ng)%Jend(tile)=jend_in
675 bounds(ng)%LBi(tile)=ilo
676 bounds(ng)%UBi(tile)=ihi
677 bounds(ng)%LBj(tile)=jlo
678 bounds(ng)%UBj(tile)=jhi
679!
680 bio_dbg_level=dbg_level
681 bio_dbg_i=dbg_i
682 bio_dbg_j=dbg_j
683!
684!-----------------------------------------------------------------------
685! Allocate and index the biology parameter arrays, then validate the
686! compiled option set against the caller's runtime toggles.
687!-----------------------------------------------------------------------
688!
689 CALL initialize_biology
690!
691 CALL fennel_check_config (nbt_in, use_po4, use_carbon,
692 & use_oxygen, use_odu, use_denit,
693 & use_biosed, use_bulk, use_rivdon,
694 & use_talknc, pco2air_kind,
695 & co2_sc_kind, o2_sc_kind)
696!
697!-----------------------------------------------------------------------
698! Copy the parameter set. Order here mirrors the FennelParameters struct
699! declaration order in REMORA_Biology.H so the two lists review side by
700! side.
701!-----------------------------------------------------------------------
702!
703 bioiter(ng)=bioiter_in
704 attsw(ng)=real(p_attsw,r8)
705 attchl(ng)=real(p_attchl,r8)
706 parfrac(ng)=real(p_parfrac,r8)
707 vp0(ng)=real(p_vp0,r8)
708 i_thnh4(ng)=real(p_i_thnh4,r8)
709 d_p5nh4(ng)=real(p_d_p5nh4,r8)
710 nitrir(ng)=real(p_nitrir,r8)
711 k_no3(ng)=real(p_k_no3,r8)
712 k_nh4(ng)=real(p_k_nh4,r8)
713 k_po4(ng)=real(p_k_po4,r8)
714 k_phy(ng)=real(p_k_phy,r8)
715 chl2c_m(ng)=real(p_chl2c_m,r8)
716 chlmin(ng)=real(p_chlmin,r8)
717 phycn(ng)=real(p_phycn,r8)
718 r_p2n(ng)=real(p_r_p2n,r8)
719 phyip(ng)=real(p_phyip,r8)
720 phyis(ng)=real(p_phyis,r8)
721 phymin(ng)=real(p_phymin,r8)
722 phymr(ng)=real(p_phymr,r8)
723 zooae_n(ng)=real(p_zooae_n,r8)
724 zoocn(ng)=real(p_zoocn,r8)
725 zoobm(ng)=real(p_zoobm,r8)
726 zooer(ng)=real(p_zooer,r8)
727 zoogr(ng)=real(p_zoogr,r8)
728 zoomin(ng)=real(p_zoomin,r8)
729 zoomr(ng)=real(p_zoomr,r8)
730 lderrn(ng)=real(p_lderrn,r8)
731 lderrc(ng)=real(p_lderrc,r8)
732 coagr(ng)=real(p_coagr,r8)
733 sderrn(ng)=real(p_sderrn,r8)
734 sderrc(ng)=real(p_sderrc,r8)
735 rderrn(ng)=real(p_rderrn,r8)
736 rderrc(ng)=real(p_rderrc,r8)
737 wphy(ng)=real(p_wphy,r8)
738 wldet(ng)=real(p_wldet,r8)
739 wsdet(ng)=real(p_wsdet,r8)
740 pco2air(ng)=real(p_pco2air,r8)
741!
742!-----------------------------------------------------------------------
743! Pack REMORA's split old/new state into the ROMS three-slot tracer array.
744! Slot 3 is unused by biology_tile but is allocated for shape fidelity.
745!-----------------------------------------------------------------------
746!
747 allocate ( t(ilo:ihi,jlo:jhi,nz,3,ntrc) )
748!
749 DO itrc=1,ntrc
750 DO k=1,nz
751 DO j=jlo,jhi
752 DO i=ilo,ihi
753 t(i,j,k,kstp,itrc)=real(t_nstp(i,j,k,itrc),r8)
754 t(i,j,k,knew,itrc)=real(t_nnew(i,j,k,itrc),r8)
755 t(i,j,k,3,itrc)=0.0_r8
756 END DO
757 END DO
758 END DO
759 END DO
760!
761!-----------------------------------------------------------------------
762! Call the unmodified ROMS kernel.
763!-----------------------------------------------------------------------
764!
765 CALL biology_tile (ng, tile,
766 & ilo, ihi, jlo, jhi, nz, ntrc,
767 & ilo, ihi, jlo, jhi,
768 & kstp, knew,
769#ifdef MASKING
770 & rmask,
771#endif
772 & hz, z_r, z_w, srflx,
773#if defined CARBON || defined OXYGEN
774# ifdef BULK_FLUXES
775 & uwind, vwind,
776# else
777 & sustr, svstr,
778# endif
779#endif
780#ifdef CARBON
781 & ph,
782#endif
783 & t)
784!
785!-----------------------------------------------------------------------
786! Return the updated nnew slot. Temperature and salinity are untouched by
787! biology_tile, so copying the whole array back is safe and keeps the
788! unpack symmetric with the pack.
789!-----------------------------------------------------------------------
790!
791 DO itrc=1,ntrc
792 DO k=1,nz
793 DO j=jlo,jhi
794 DO i=ilo,ihi
795 t_nnew(i,j,k,itrc)=real(t(i,j,k,knew,itrc),c_double)
796 END DO
797 END DO
798 END DO
799 END DO
800!
801 deallocate ( t )
802!
803 RETURN
804 end subroutine fennel_bridge_advance_c
805!
806!=======================================================================
807!
808 SUBROUTINE fennel_check_config (nbt_in, use_po4, use_carbon,
809 & use_oxygen, use_odu, use_denit,
810 & use_biosed, use_bulk, use_rivdon,
811 & use_talknc, pco2air_kind,
812 & co2_sc_kind, o2_sc_kind)
813!
814! ROMS resolves biology options at compile time; REMORA resolves them at
815! run time. A bridge built for one option set and run against a case
816! configured for another would produce a plausible-looking but meaningless
817! comparison, so every call checks the two agree and stops if they do not.
818!
819 integer, intent(in) :: nbt_in
820 integer, intent(in) :: use_po4, use_carbon, use_oxygen, use_odu
821 integer, intent(in) :: use_denit, use_biosed, use_bulk
822 integer, intent(in) :: use_rivdon, use_talknc
823 integer, intent(in) :: pco2air_kind, co2_sc_kind, o2_sc_kind
824!
825 logical :: bad
826!
827 integer :: want_po4, want_carbon, want_oxygen, want_odu
828 integer :: want_denit, want_biosed, want_bulk
829 integer :: want_rivdon, want_talknc
830 integer :: want_pco2air, want_co2_sc, want_o2_sc
831!
832 want_po4=0
833 want_carbon=0
834 want_oxygen=0
835 want_odu=0
836 want_denit=0
837 want_biosed=0
838 want_bulk=0
839 want_rivdon=0
840 want_talknc=0
841!
842! Ordinals, matching REMORABiology::PCO2AirType, CO2SchmidtType and
843! O2SchmidtType in Source/Biology/REMORA_Biology.H. ROMS spells the same
844! choices as mutually exclusive CPP defines, and the default in each family
845! is the one with no define, hence zero.
846!
847 want_pco2air=0
848 want_co2_sc=0
849 want_o2_sc=0
850#ifdef RIVER_DON
851 want_rivdon=1
852#endif
853#ifdef TALK_NONCONSERV
854 want_talknc=1
855#endif
856#if defined PCO2AIR_DATA
857 want_pco2air=1
858#elif defined PCO2AIR_SECULAR
859 want_pco2air=2
860#endif
861#ifdef RW14_CO2_SC
862 want_co2_sc=1
863#endif
864#if defined RW14_OXYGEN_SC
865 want_o2_sc=1
866#elif defined OCMIP_OXYGEN_SC
867 want_o2_sc=2
868#endif
869#ifdef PO4
870 want_po4=1
871#endif
872#ifdef CARBON
873 want_carbon=1
874#endif
875#ifdef OXYGEN
876 want_oxygen=1
877#endif
878#ifdef ODU
879 want_odu=1
880#endif
881#ifdef DENITRIFICATION
882 want_denit=1
883#endif
884#ifdef BIO_SEDIMENT
885 want_biosed=1
886#endif
887#ifdef BULK_FLUXES
888 want_bulk=1
889#endif
890!
891 bad=.false.
892 IF (use_po4.ne.want_po4) bad=.true.
893 IF (use_carbon.ne.want_carbon) bad=.true.
894 IF (use_oxygen.ne.want_oxygen) bad=.true.
895 IF (use_odu.ne.want_odu) bad=.true.
896 IF (use_denit.ne.want_denit) bad=.true.
897 IF (use_biosed.ne.want_biosed) bad=.true.
898 IF (use_bulk.ne.want_bulk) bad=.true.
899 IF (use_rivdon.ne.want_rivdon) bad=.true.
900 IF (use_talknc.ne.want_talknc) bad=.true.
901 IF (pco2air_kind.ne.want_pco2air) bad=.true.
902 IF (co2_sc_kind.ne.want_co2_sc) bad=.true.
903 IF (o2_sc_kind.ne.want_o2_sc) bad=.true.
904 IF (nbt_in.ne.nbt) bad=.true.
905!
906 IF (bad) THEN
907 write (6,10)
908 CALL fennel_config_row ('po4', use_po4, want_po4)
909 CALL fennel_config_row ('carbon', use_carbon, want_carbon)
910 CALL fennel_config_row ('oxygen', use_oxygen, want_oxygen)
911 CALL fennel_config_row ('odu', use_odu, want_odu)
912 CALL fennel_config_row ('denitrification', use_denit,
913 & want_denit)
914 CALL fennel_config_row ('bio_sediment', use_biosed, want_biosed)
915 CALL fennel_config_row ('bulk_fluxes', use_bulk, want_bulk)
916 CALL fennel_config_row ('river_don', use_rivdon, want_rivdon)
917 CALL fennel_config_row ('talk_nonconserv', use_talknc,
918 & want_talknc)
919 CALL fennel_config_row ('pco2air_type', pco2air_kind,
920 & want_pco2air)
921 CALL fennel_config_row ('co2_schmidt', co2_sc_kind, want_co2_sc)
922 CALL fennel_config_row ('oxygen_schmidt', o2_sc_kind,
923 & want_o2_sc)
924 CALL fennel_config_row ('tracer count', nbt_in, nbt)
925 write (6,30)
926!
927! ERROR STOP, not STOP. gfortran gives a character STOP code an exit status
928! of zero, so a plain STOP here would abort the run while still reporting
929! success to the shell, and any exit-status gate (CTest, a campaign runner)
930! would score the mismatch as a pass.
931!
932 error stop 1
933 END IF
934!
935! Format literals use nX edit descriptors rather than runs of spaces: the
936! .F preprocessing pass collapses two or more consecutive spaces inside
937! character literals, which silently destroys column alignment.
938!
939 10 format (/,' FENNEL BRIDGE: runtime option set does not match the',
940 & ' compiled ROMS CPP set.',/,
941 & ' Comparing these two paths would be invalid.',//,
942 & 3x,'option',11x,'runtime',3x,'compiled')
943 30 format (/,' Edit Source/Biology/Fortran/remora_bio_cppdefs.h to',
944 & ' match the case, then rebuild.',/)
945!
946 RETURN
947 END SUBROUTINE fennel_check_config
948!
949 SUBROUTINE fennel_config_row (name, runtime_val, compiled_val)
950!
951! One row of the option-mismatch table. The name is re-padded into a
952! fixed-length local for the same reason the tag emitter does it: trailing
953! blanks in the caller's literals do not survive preprocessing, and Aw
954! right-justifies anything shorter than w.
955!
956 character (len=*), intent(in) :: name
957 integer, intent(in) :: runtime_val, compiled_val
958!
959 character (len=15) :: nm
960!
961 nm = name
962 write (6,10) nm, runtime_val, compiled_val
963 10 format (3x,a15,2x,i7,3x,i8)
964!
965 RETURN
966 END SUBROUTINE fennel_config_row
967!
968 END MODULE remora_fennel_isohelper
void fennel_bridge_advance_c(int ilo, int ihi, int jlo, int jhi, int nz, int nbt, int istr, int iend, int jstr, int jend, int use_po4, int use_carbon, int use_oxygen, int use_odu, int use_denit, int use_biosed, int use_bulk, int use_rivdon, int use_talknc, int pco2air_kind, int co2_sc_kind, int o2_sc_kind, int bioiter, double dt_sec, double rho0, double cp_heat, double p_AttSW, double p_AttChl, double p_PARfrac, double p_Vp0, double p_I_thNH4, double p_D_p5NH4, double p_NitriR, double p_K_NO3, double p_K_NH4, double p_K_PO4, double p_K_Phy, double p_Chl2C_m, double p_ChlMin, double p_PhyCN, double p_R_P2N, double p_PhyIP, double p_PhyIS, double p_PhyMin, double p_PhyMR, double p_ZooAE_N, double p_ZooCN, double p_ZooBM, double p_ZooER, double p_ZooGR, double p_ZooMin, double p_ZooMR, double p_LDeRRN, double p_LDeRRC, double p_CoagR, double p_SDeRRN, double p_SDeRRC, double p_RDeRRN, double p_RDeRRC, double p_wPhy, double p_wLDet, double p_wSDet, double p_pCO2air, const double *rmask, const double *Hz, const double *z_r, const double *z_w, const double *srflx, const double *sustr, const double *svstr, const double *uwind, const double *vwind, double *pH, const double *t_nstp, double *t_nnew, int dbg_level, int dbg_i, int dbg_j)
subroutine, public caldate(currenttime, yy_i, yd_i, mm_i, dd_i, h_i, m_i, s_r, yd_dp)
subroutine fennel_tag_t(tag, iter, istr, iend, j, lbi, ubi, lbj, ubj, ubk, ubt, nnew, t)
subroutine fennel_tag(tag, iter, istr, iend, j, imins, imaxs, ubk, ubt, bio)
subroutine fennel_tag_line(tag, iter, i, j, k, var, val)
type(t_forces), dimension(ngrids) forces
type(t_grid), dimension(ngrids) grid
integer, parameter r4
integer, parameter r8
integer, parameter i8
integer, parameter dp
integer, parameter isalt
integer, parameter itemp
type(t_ocean), dimension(ngrids) ocean
integer, parameter inlm
logical, dimension(4) lbiofile
integer, parameter ngrids
integer, parameter nns
integer, parameter ncs
integer, parameter nat
integer, parameter npt
character(len=256), dimension(4) bioname
integer, dimension(ngrids) nt
type(t_bounds), dimension(ngrids) bounds
real(r8), dimension(ngrids) dmem
integer, dimension(ngrids) n
real(r8), parameter sec2day
real(r8), dimension(ngrids) dt
integer, dimension(ngrids) iic
real(dp), dimension(ngrids) tdays
integer, dimension(ngrids) nnew
integer, dimension(ngrids) nstp
subroutine fennel_config_row(name, runtime_val, compiled_val)
subroutine fennel_check_config(nbt_in, use_po4, use_carbon, use_oxygen, use_odu, use_denit, use_biosed, use_bulk, use_rivdon, use_talknc, pco2air_kind, co2_sc_kind, o2_sc_kind)