New URL for NEMO forge!   http://forge.nemo-ocean.eu

Since March 2022 along with NEMO 4.2 release, the code development moved to a self-hosted GitLab.
This present forge is now archived and remained online for history.
icbutl.F90 in NEMO/branches/2020/tickets_2494_2375/src/OCE/ICB – NEMO

source: NEMO/branches/2020/tickets_2494_2375/src/OCE/ICB/icbutl.F90 @ 13265

Last change on this file since 13265 was 13265, checked in by mathiot, 4 years ago

ticket #2494 and #2375: ticket #2494 changes and part of ticket #2375 (lbc_icb_lnk on extended variable at T point removed), ff_e initialisation and lbc_lnk move in the icbini.F90; tests on possible useless lbc_lnk in icbclv not yet done

  • Property svn:keywords set to Id
File size: 33.9 KB
Line 
1MODULE icbutl
2   !!======================================================================
3   !!                       ***  MODULE  icbutl  ***
4   !! Icebergs:  various iceberg utility routines
5   !!======================================================================
6   !! History : 3.3.1 !  2010-01  (Martin&Adcroft) Original code
7   !!            -    !  2011-03  (Madec)          Part conversion to NEMO form
8   !!            -    !                            Removal of mapping from another grid
9   !!            -    !  2011-04  (Alderson)       Split into separate modules
10   !!           4.2   !  2020-07  (P. Mathiot)     simplification of interpolation routine
11   !!                 !                            and add Nacho Merino work
12   !!----------------------------------------------------------------------
13
14   !!----------------------------------------------------------------------
15   !!   icb_utl_interp   :
16   !!   icb_utl_pos      : compute bottom left corner indice, weight and mask
17   !!   icb_utl_bilin_h  : interpolation field to icb position
18   !!   icb_utl_bilin_e  : interpolation of scale factor to icb position
19   !!----------------------------------------------------------------------
20   USE par_oce                             ! ocean parameters
21   USE dom_oce                             ! ocean domain
22   USE in_out_manager                      ! IO parameters
23   USE lbclnk                              ! lateral boundary condition
24   USE lib_mpp                             ! MPI code and lk_mpp in particular
25   USE icb_oce                             ! define iceberg arrays
26   USE sbc_oce                             ! ocean surface boundary conditions
27#if defined key_si3
28   USE ice,    ONLY: u_ice, v_ice, hm_i    ! SI3 variables
29   USE icevar                              ! ice_var_sshdyn
30   USE sbc_ice, ONLY: snwice_mass, snwice_mass_b
31#endif
32
33   IMPLICIT NONE
34   PRIVATE
35
36   PUBLIC   icb_utl_copy          ! routine called in icbstp module
37   PUBLIC   icb_utl_interp        ! routine called in icbdyn, icbthm modules
38   PUBLIC   icb_utl_bilin_h       ! routine called in icbdyn module
39   PUBLIC   icb_utl_add           ! routine called in icbini.F90, icbclv, icblbc and icbrst modules
40   PUBLIC   icb_utl_delete        ! routine called in icblbc, icbthm modules
41   PUBLIC   icb_utl_destroy       ! routine called in icbstp module
42   PUBLIC   icb_utl_track         ! routine not currently used, retain just in case
43   PUBLIC   icb_utl_print_berg    ! routine called in icbthm module
44   PUBLIC   icb_utl_print         ! routine called in icbini, icbstp module
45   PUBLIC   icb_utl_count         ! routine called in icbdia, icbini, icblbc, icbrst modules
46   PUBLIC   icb_utl_incr          ! routine called in icbini, icbclv modules
47   PUBLIC   icb_utl_yearday       ! routine called in icbclv, icbstp module
48   PUBLIC   icb_utl_mass          ! routine called in icbdia module
49   PUBLIC   icb_utl_heat          ! routine called in icbdia module
50
51   !!----------------------------------------------------------------------
52   !! NEMO/OCE 4.0 , NEMO Consortium (2018)
53   !! $Id$
54   !! Software governed by the CeCILL license (see ./LICENSE)
55   !!----------------------------------------------------------------------
56CONTAINS
57
58   SUBROUTINE icb_utl_copy()
59      !!----------------------------------------------------------------------
60      !!                  ***  ROUTINE icb_utl_copy  ***
61      !!
62      !! ** Purpose :   iceberg initialization.
63      !!
64      !! ** Method  : - blah blah
65      !!----------------------------------------------------------------------
66#if defined key_si3
67      REAL(wp), DIMENSION(jpi,jpj) :: zssh_lead_m    !    ocean surface (ssh_m) if ice is not embedded
68      !                                              !    ocean surface in leads if ice is embedded   
69#endif
70      ! copy nemo forcing arrays into iceberg versions with extra halo
71      ! only necessary for variables not on T points
72      ! and ssh which is used to calculate gradients
73
74      uo_e(1:jpi,1:jpj) = ssu_m(:,:) * umask(:,:,1)
75      vo_e(1:jpi,1:jpj) = ssv_m(:,:) * vmask(:,:,1)
76      tt_e(1:jpi,1:jpj) = sst_m(:,:)
77      fr_e(1:jpi,1:jpj) = fr_i (:,:)
78      ua_e(1:jpi,1:jpj) = utau (:,:) * umask(:,:,1) ! maybe mask useless because mask applied in sbcblk
79      va_e(1:jpi,1:jpj) = vtau (:,:) * vmask(:,:,1) ! maybe mask useless because mask applied in sbcblk
80      !
81      CALL lbc_lnk_icb( 'icbutl', uo_e, 'U', -1._wp, 1, 1 )
82      CALL lbc_lnk_icb( 'icbutl', vo_e, 'V', -1._wp, 1, 1 )
83      CALL lbc_lnk_icb( 'icbutl', ua_e, 'U', -1._wp, 1, 1 )
84      CALL lbc_lnk_icb( 'icbutl', va_e, 'V', -1._wp, 1, 1 )
85#if defined key_si3
86      hi_e(1:jpi, 1:jpj) = hm_i (:,:) 
87      ui_e(1:jpi, 1:jpj) = u_ice(:,:)
88      vi_e(1:jpi, 1:jpj) = v_ice(:,:)
89      !     
90      ! compute ssh slope using ssh_lead if embedded
91      zssh_lead_m(:,:) = ice_var_sshdyn(ssh_m, snwice_mass, snwice_mass_b)
92      ssh_e(1:jpi, 1:jpj) = zssh_lead_m(:,:) * tmask(:,:,1)
93      !
94      CALL lbc_lnk_icb( 'icbutl', ui_e , 'U', -1._wp, 1, 1 )
95      CALL lbc_lnk_icb( 'icbutl', vi_e , 'V', -1._wp, 1, 1 )
96#else
97      ssh_e(1:jpi, 1:jpj) = ssh_m(:,:) * tmask(:,:,1)         
98#endif
99      !
100   END SUBROUTINE icb_utl_copy
101
102
103   SUBROUTINE icb_utl_interp( pi, pj, pe1, puo, pui, pua, pssh_i,   &
104      &                               pe2, pvo, pvi, pva, pssh_j,   &
105      &                               psst, pcn, phi, pff, plon, plat)
106      !!----------------------------------------------------------------------
107      !!                  ***  ROUTINE icb_utl_interp  ***
108      !!
109      !! ** Purpose :   interpolation
110      !!
111      !! ** Method  : - interpolate from various ocean arrays onto iceberg position
112      !!
113      !!       !!gm  CAUTION here I do not care of the slip/no-slip conditions
114      !!             this can be done later (not that easy to do...)
115      !!             right now, U is 0 in land so that the coastal value of velocity parallel to the coast
116      !!             is half the off shore value, wile the normal-to-the-coast value is zero.
117      !!             This is OK as a starting point.
118      !!       !!pm  HARD CODED: - rho_air now computed in sbcblk (what are the effect ?)
119      !!                         - drag coefficient (should it be namelist parameter ?)
120      !!
121      !!----------------------------------------------------------------------
122      REAL(wp), INTENT(in   ) ::   pi , pj                        ! position in (i,j) referential
123      REAL(wp), INTENT(  out), OPTIONAL ::   pe1, pe2                       ! i- and j scale factors
124      REAL(wp), INTENT(  out), OPTIONAL ::   puo, pvo, pui, pvi, pua, pva   ! ocean, ice and wind speeds
125      REAL(wp), INTENT(  out), OPTIONAL ::   pssh_i, pssh_j                 ! ssh i- & j-gradients
126      REAL(wp), INTENT(  out), OPTIONAL ::   psst, pcn, phi, pff            ! SST, ice concentration, ice thickness, Coriolis
127      REAL(wp), INTENT(  out), OPTIONAL ::   plat, plon                     ! position
128      !
129      REAL(wp), DIMENSION(4) :: zwT  , zwU  , zwV  , zwF   ! interpolation weight
130      REAL(wp), DIMENSION(4) :: zmskF, zmskU, zmskV, zmskT ! mask
131      REAL(wp), DIMENSION(4) :: zwTp, zmskTp, zwTm, zmskTm
132      INTEGER                :: iiT, iiU, iiV, iiF, ijT, ijU, ijV, ijF ! bottom left corner
133      INTEGER                :: iiTp, iiTm, ijTp, ijTm
134      REAL(wp) ::   zcd, zmod       ! local scalars
135      !!----------------------------------------------------------------------
136      !
137      ! get position, weight and mask
138      CALL icb_utl_pos( pi, pj, 'T', iiT, ijT, zwT, zmskT )
139      CALL icb_utl_pos( pi, pj, 'U', iiU, ijU, zwU, zmskU )
140      CALL icb_utl_pos( pi, pj, 'V', iiV, ijV, zwV, zmskV )
141      CALL icb_utl_pos( pi, pj, 'F', iiF, ijF, zwF, zmskF )
142      !
143      ! metrics and coordinates
144      IF ( PRESENT(pe1 ) ) pe1 = icb_utl_bilin_e( e1t, e1u, e1v, e1f, pi, pj )      ! scale factors
145      IF ( PRESENT(pe2 ) ) pe2 = icb_utl_bilin_e( e2t, e2u, e2v, e2f, pi, pj )
146      IF ( PRESENT(plon) ) plon= icb_utl_bilin_h( rlon_e, iiT, ijT, zwT, .true.  )
147      IF ( PRESENT(plat) ) plat= icb_utl_bilin_h( rlat_e, iiT, ijT, zwT, .false. )
148      !
149      IF ( PRESENT(puo ) ) puo  = icb_utl_bilin_h( uo_e, iiU, ijU, zwU        , .false. )         ! ocean velocities
150      IF ( PRESENT(pvo ) ) pvo  = icb_utl_bilin_h( vo_e, iiV, ijV, zwV        , .false. )         !
151      IF ( PRESENT(psst) ) psst = icb_utl_bilin_h( tt_e, iiT, ijT, zwT * zmskT, .false. ) ! sst
152      IF ( PRESENT(pcn ) ) pcn  = icb_utl_bilin_h( fr_e, iiT, ijT, zwT * zmskT, .false. ) ! ice concentration
153      IF ( PRESENT(pff ) ) pff  = icb_utl_bilin_h( ff_e, iiF, ijF, zwF        , .false. )         ! Coriolis parameter
154      !
155      IF ( PRESENT(pua) .AND. PRESENT(pva) ) THEN
156         pua  = icb_utl_bilin_h( ua_e, iiU, ijU, zwU * zmskU, .false. ) ! 10m wind
157         pva  = icb_utl_bilin_h( va_e, iiV, ijV, zwV * zmskV, .false. ) ! here (ua,va) are stress => rough conversion from stress to speed
158         zcd  = 1.22_wp * 1.5e-3_wp                               ! air density * drag coefficient
159         zmod = 1._wp / MAX(  1.e-20, SQRT(  zcd * SQRT( pua*pua + pva*pva)  )  )
160         pua  = pua * zmod                                       ! note: stress module=0 necessarly implies ua=va=0
161         pva  = pva * zmod
162      END IF
163      !
164#if defined key_si3
165      IF ( PRESENT(pui) ) pui = icb_utl_bilin_h( ui_e , iiU, ijU, zwU        , .false. ) ! sea-ice velocities
166      IF ( PRESENT(pvi) ) pvi = icb_utl_bilin_h( vi_e , iiV, ijV, zwV        , .false. )
167      IF ( PRESENT(phi) ) phi = icb_utl_bilin_h( hi_e , iiT, ijT, zwT * zmskT, .false. ) ! ice thickness
168#else
169      IF ( PRESENT(pui) ) pui = 0._wp
170      IF ( PRESENT(pvi) ) pvi = 0._wp
171      IF ( PRESENT(phi) ) phi = 0._wp
172#endif
173      !
174      ! Estimate SSH gradient in i- and j-direction (centred evaluation)
175      IF ( PRESENT(pssh_i) .AND. PRESENT(pssh_j) ) THEN
176         CALL icb_utl_pos( pi+0.1, pj    , 'T', iiTp, ijTp, zwTp, zmskTp )
177         CALL icb_utl_pos( pi-0.1, pj    , 'T', iiTm, ijTm, zwTm, zmskTm )
178         !
179         IF ( .NOT. PRESENT(pe1) ) pe1 = icb_utl_bilin_e( e1t, e1u, e1v, e1f, pi, pj )
180         pssh_i = ( icb_utl_bilin_h( ssh_e, iiTp, ijTp, zwTp*zmskTp, .false. ) -   &
181            &       icb_utl_bilin_h( ssh_e, iiTm, ijTm, zwTm*zmskTm, .false. )  ) / ( 0.2_wp * pe1 )
182         !
183         CALL icb_utl_pos( pi    , pj+0.1, 'T', iiTp, ijTp, zwTp, zmskTp )
184         CALL icb_utl_pos( pi    , pj-0.1, 'T', iiTm, ijTm, zwTm, zmskTm )
185         !
186         IF ( .NOT. PRESENT(pe2) ) pe2 = icb_utl_bilin_e( e2t, e2u, e2v, e2f, pi, pj )
187         pssh_j = ( icb_utl_bilin_h( ssh_e, iiTp, ijTp, zwTp*zmskTp, .false. ) -   &
188            &       icb_utl_bilin_h( ssh_e, iiTm, ijTm, zwTm*zmskTm, .false. )  ) / ( 0.2_wp * pe2 )
189      END IF
190      !
191   END SUBROUTINE icb_utl_interp
192
193   SUBROUTINE icb_utl_pos( pi, pj, cd_type, kii, kij, pw, pmsk )
194      !!----------------------------------------------------------------------
195      !!                  ***  FUNCTION icb_utl_bilin  ***
196      !!
197      !! ** Purpose :   bilinear interpolation at berg location depending on the grid-point type
198      !!                this version deals with extra halo points
199      !!
200      !!       !!gm  CAUTION an optional argument should be added to handle
201      !!             the slip/no-slip conditions  ==>>> to be done later
202      !!
203      !!----------------------------------------------------------------------
204      REAL(wp)              , INTENT(IN)  ::   pi, pj    ! targeted coordinates in (i,j) referential
205      CHARACTER(len=1)      , INTENT(IN)  ::   cd_type   ! point type
206      REAL(wp), DIMENSION(4), INTENT(OUT) ::   pw, pmsk  ! weight and mask
207      INTEGER ,               INTENT(OUT) ::   kii, kij  ! bottom left corner position in local domain
208      !
209      REAL(wp) :: zwi, zwj ! distance to bottom left corner
210      INTEGER  :: ierr 
211      !
212      !!----------------------------------------------------------------------
213      !
214      SELECT CASE ( cd_type )
215      CASE ( 'T' )
216         ! note that here there is no +0.5 added
217         ! since we're looking for four T points containing quadrant we're in of
218         ! current T cell
219         kii = MAX(0, INT( pi     ))
220         kij = MAX(0, INT( pj     ))    ! T-point
221         zwi = pi - REAL(kii,wp)
222         zwj = pj - REAL(kij,wp)
223      CASE ( 'U' )
224         kii = MAX(0, INT( pi-0.5_wp ))
225         kij = MAX(0, INT( pj     ))    ! U-point
226         zwi = pi - 0.5_wp - REAL(kii,wp)
227         zwj = pj - REAL(kij,wp)
228      CASE ( 'V' )
229         kii = MAX(0, INT( pi     ))
230         kij = MAX(0, INT( pj-0.5_wp ))    ! V-point
231         zwi = pi - REAL(kii,wp)
232         zwj = pj - 0.5_wp - REAL(kij,wp)
233      CASE ( 'F' )
234         kii = MAX(0, INT( pi-0.5_wp ))
235         kij = MAX(0, INT( pj-0.5_wp ))    ! F-point
236         zwi = pi - 0.5_wp - REAL(kii,wp)
237         zwj = pj - 0.5_wp - REAL(kij,wp)
238      END SELECT
239      !
240      ! compute weight
241      pw(1) = (1._wp-zwi) * (1._wp-zwj)
242      pw(2) =        zwi  * (1._wp-zwj)
243      pw(3) = (1._wp-zwi) *        zwj
244      pw(4) =        zwi  *        zwj
245      !
246      ! find position in this processor. Prevent near edge problems (see #1389)
247      !
248      IF (TRIM(cd_type) == 'T' ) THEN
249         ierr = 0
250         IF    ( kii <  mig( 1 ) ) THEN   ;  ierr = ierr + 1
251         ELSEIF( kii >= mig(jpi) ) THEN   ;  ierr = ierr + 1
252         ENDIF
253         !
254         IF    ( kij <  mjg( 1 ) ) THEN   ;   ierr = ierr + 1
255         ELSEIF( kij >= mjg(jpj) ) THEN   ;   ierr = ierr + 1
256         ENDIF
257         !
258         IF ( ierr > 0 ) THEN
259            WRITE(numout,*) 'bottom left corner T point out of bound'
260            WRITE(numout,*) kii, mig( 1 ), mig(jpi)
261            WRITE(numout,*) kij, mjg( 1 ), mjg(jpj)
262            CALL ctl_stop('STOP','icb_utl_bilin_h: an icebergs coordinates is out of valid range (out of bound error)')
263         END IF
264      END IF
265      !
266      ! find position in this processor. Prevent near edge problems (see #1389)
267      ! (PM) will be useless if extra halo is used in NEMO
268      !
269      IF    ( kii <= mig(1)-1 ) THEN   ;   kii = 0
270      ELSEIF( kii  > mig(jpi) ) THEN   ;   kii = jpi
271      ELSE                             ;   kii = mi1(kii)
272      ENDIF
273      IF    ( kij <= mjg(1)-1 ) THEN   ;   kij = 0
274      ELSEIF( kij  > mjg(jpj) ) THEN   ;   kij = jpj
275      ELSE                             ;   kij = mj1(kij)
276      ENDIF
277      !
278      ! define mask array
279      ! land value is not used in the interpolation
280      SELECT CASE ( cd_type )
281      CASE ( 'T' )
282         pmsk = (/tmask_e(kii,kij), tmask_e(kii+1,kij), tmask_e(kii,kij+1), tmask_e(kii+1,kij+1)/)
283      CASE ( 'U' )
284         pmsk = (/umask_e(kii,kij), umask_e(kii+1,kij), umask_e(kii,kij+1), umask_e(kii+1,kij+1)/)
285      CASE ( 'V' )
286         pmsk = (/vmask_e(kii,kij), vmask_e(kii+1,kij), vmask_e(kii,kij+1), vmask_e(kii+1,kij+1)/)
287      CASE ( 'F' )
288         ! F case only used for coriolis, ff_f is not mask so zmask = 1
289         pmsk = 1.
290      END SELECT
291   END SUBROUTINE icb_utl_pos
292
293   REAL(wp) FUNCTION icb_utl_bilin_h( pfld, pii, pij, pw, pllon )
294      !!----------------------------------------------------------------------
295      !!                  ***  FUNCTION icb_utl_bilin  ***
296      !!
297      !! ** Purpose :   bilinear interpolation at berg location depending on the grid-point type
298      !!                this version deals with extra halo points
299      !!
300      !!       !!gm  CAUTION an optional argument should be added to handle
301      !!             the slip/no-slip conditions  ==>>> to be done later
302      !!
303      !!----------------------------------------------------------------------
304      REAL(wp), DIMENSION(0:jpi+1,0:jpj+1), INTENT(in) ::   pfld      ! field to be interpolated
305      REAL(wp), DIMENSION(4)              , INTENT(in) ::   pw        ! weight
306      LOGICAL                             , INTENT(in) ::   pllon     ! input data is a longitude
307      INTEGER ,                             INTENT(in) ::   pii, pij  ! bottom left corner
308      !
309      REAL(wp), DIMENSION(4) :: zdat ! input data
310      !!----------------------------------------------------------------------
311      !
312      ! data
313      zdat(1) = pfld(pii  ,pij  )
314      zdat(2) = pfld(pii+1,pij  )
315      zdat(3) = pfld(pii  ,pij+1)
316      zdat(4) = pfld(pii+1,pij+1)
317      !
318      IF( pllon .AND. MAXVAL(zdat) - MINVAL(zdat) > 90._wp ) THEN
319         WHERE( zdat < 0._wp ) zdat = zdat + 360._wp
320      ENDIF
321      !
322      ! compute interpolated value
323      icb_utl_bilin_h = ( zdat(1)*pw(1) + zdat(2)*pw(2) + zdat(3)*pw(3) + zdat(4)*pw(4) ) / MAX(1.e-20, pw(1)+pw(2)+pw(3)+pw(4)) 
324      !
325      IF( pllon .AND. icb_utl_bilin_h > 180._wp ) icb_utl_bilin_h = icb_utl_bilin_h - 360._wp
326      !
327   END FUNCTION icb_utl_bilin_h
328
329   REAL(wp) FUNCTION icb_utl_bilin_e( pet, peu, pev, pef, pi, pj )
330      !!----------------------------------------------------------------------
331      !!                  ***  FUNCTION dom_init  ***
332      !!
333      !! ** Purpose :   bilinear interpolation at berg location of horizontal scale factor
334      !! ** Method  :   interpolation done using the 4 nearest grid points among
335      !!                t-, u-, v-, and f-points.
336      !!----------------------------------------------------------------------
337      REAL(wp), DIMENSION(:,:), INTENT(in) ::   pet, peu, pev, pef   ! horizontal scale factor to be interpolated at t-,u-,v- & f-pts
338      REAL(wp)                , INTENT(IN) ::   pi , pj              ! iceberg position
339      !
340      ! weights corresponding to corner points of a T cell quadrant
341      REAL(wp) ::   zi, zj          ! local real
342      INTEGER  ::   ii, ij          ! bottom left corner coordinate in local domain
343      !
344      ! values at corner points of a T cell quadrant
345      ! 00 = bottom left, 10 = bottom right, 01 = top left, 11 = top right
346      REAL(wp) ::   ze00, ze10, ze01, ze11
347      !!----------------------------------------------------------------------
348      !
349      ! cannot used iiT because need ii/ij reltaive to global indices not local one
350      ii = MAX(1, INT( pi ))   ;   ij = MAX(1, INT( pj ))            ! left bottom T-point (i,j) indices
351      !
352      ! fractional box spacing
353      ! 0   <= zi < 0.5, 0   <= zj < 0.5   -->  NW quadrant of current T cell
354      ! 0.5 <= zi < 1  , 0   <= zj < 0.5   -->  NE quadrant
355      ! 0   <= zi < 0.5, 0.5 <= zj < 1     -->  SE quadrant
356      ! 0.5 <= zi < 1  , 0.5 <= zj < 1     -->  SW quadrant
357
358      zi = pi - REAL(ii,wp)          !!gm use here mig, mjg arrays
359      zj = pj - REAL(ij,wp)
360
361      ! conversion to local domain (no need to do a sanity check already done in icbpos)
362      ii = mi1(ii)
363      ij = mj1(ij)
364
365      IF (lwp) THEN
366         WRITE(numout,*) 'bilin_e ', pi, pj
367         WRITE(numout,*) ii, ij, zi, zj
368         WRITE(numout,*) ''
369      END IF
370
371      !
372      IF(    0.0_wp <= zi .AND. zi < 0.5_wp   ) THEN
373         IF( 0.0_wp <= zj .AND. zj < 0.5_wp        )   THEN        !  NE quadrant
374            !                                                      !             i=I       i=I+1/2
375            ze01 = pev(ii  ,ij  )   ;   ze11 = pef(ii  ,ij  )      !   j=J+1/2    V ------- F
376            ze00 = pet(ii  ,ij  )   ;   ze10 = peu(ii  ,ij  )      !   j=J        T ------- U
377            zi = 2._wp * zi
378            zj = 2._wp * zj
379         ELSE                                                      !  SE quadrant
380            !                                                                    !             i=I       i=I+1/2
381            ze01 = pet(ii  ,ij+1)   ;   ze11 = peu(ii  ,ij+1)      !   j=J+1      T ------- U
382            ze00 = pev(ii  ,ij  )   ;   ze10 = pef(ii  ,ij  )      !   j=J+1/2    V ------- F
383            zi = 2._wp *  zi
384            zj = 2._wp * (zj-0.5_wp)
385         ENDIF
386      ELSE
387         IF( 0.0_wp <= zj .AND. zj < 0.5_wp        )   THEN        !  NW quadrant
388            !                                                                    !             i=I       i=I+1/2
389            ze01 = pef(ii  ,ij  )   ;   ze11 = pev(ii+1,ij)        !   j=J+1/2    F ------- V
390            ze00 = peu(ii  ,ij  )   ;   ze10 = pet(ii+1,ij)        !   j=J        U ------- T
391            zi = 2._wp * (zi-0.5_wp)
392            zj = 2._wp *  zj
393         ELSE                                                      !  SW quadrant
394            !                                                                    !             i=I+1/2   i=I+1
395            ze01 = peu(ii  ,ij+1)   ;   ze11 = pet(ii+1,ij+1)      !   j=J+1      U ------- T
396            ze00 = pef(ii  ,ij  )   ;   ze10 = pev(ii+1,ij  )      !   j=J+1/2    F ------- V
397            zi = 2._wp * (zi-0.5_wp)
398            zj = 2._wp * (zj-0.5_wp)
399         ENDIF
400      ENDIF
401      !
402      icb_utl_bilin_e = ( ze01 * (1._wp-zi) + ze11 * zi ) *        zj    &
403         &            + ( ze00 * (1._wp-zi) + ze10 * zi ) * (1._wp-zj)
404      !
405   END FUNCTION icb_utl_bilin_e
406
407
408   SUBROUTINE icb_utl_add( bergvals, ptvals )
409      !!----------------------------------------------------------------------
410      !!                ***  ROUTINE icb_utl_add           ***
411      !!
412      !! ** Purpose :   add a new berg to the iceberg list
413      !!
414      !!----------------------------------------------------------------------
415      TYPE(iceberg), INTENT(in)           ::   bergvals
416      TYPE(point)  , INTENT(in)           ::   ptvals
417      !
418      TYPE(iceberg), POINTER ::   new => NULL()
419      !!----------------------------------------------------------------------
420      !
421      new => NULL()
422      CALL icb_utl_create( new, bergvals, ptvals )
423      CALL icb_utl_insert( new )
424      new => NULL()     ! Clear new
425      !
426   END SUBROUTINE icb_utl_add         
427
428
429   SUBROUTINE icb_utl_create( berg, bergvals, ptvals )
430      !!----------------------------------------------------------------------
431      !!                ***  ROUTINE icb_utl_create  ***
432      !!
433      !! ** Purpose :   add a new berg to the iceberg list
434      !!
435      !!----------------------------------------------------------------------
436      TYPE(iceberg), INTENT(in) ::   bergvals
437      TYPE(point)  , INTENT(in) ::   ptvals
438      TYPE(iceberg), POINTER    ::   berg
439      !
440      TYPE(point)  , POINTER    ::   pt
441      INTEGER                   ::   istat
442      !!----------------------------------------------------------------------
443      !
444      IF( ASSOCIATED(berg) )   CALL ctl_stop( 'icebergs, icb_utl_create: berg already associated' )
445      ALLOCATE(berg, STAT=istat)
446      IF( istat /= 0 ) CALL ctl_stop( 'failed to allocate iceberg' )
447      berg%number(:) = bergvals%number(:)
448      berg%mass_scaling = bergvals%mass_scaling
449      berg%prev => NULL()
450      berg%next => NULL()
451      !
452      ALLOCATE(pt, STAT=istat)
453      IF( istat /= 0 ) CALL ctl_stop( 'failed to allocate first iceberg point' )
454      pt = ptvals
455      berg%current_point => pt
456      !
457   END SUBROUTINE icb_utl_create
458
459
460   SUBROUTINE icb_utl_insert( newberg )
461      !!----------------------------------------------------------------------
462      !!                 ***  ROUTINE icb_utl_insert  ***
463      !!
464      !! ** Purpose :   add a new berg to the iceberg list
465      !!
466      !!----------------------------------------------------------------------
467      TYPE(iceberg), POINTER  ::   newberg
468      !
469      TYPE(iceberg), POINTER  ::   this, prev, last
470      !!----------------------------------------------------------------------
471      !
472      IF( ASSOCIATED( first_berg ) ) THEN
473         last => first_berg
474         DO WHILE (ASSOCIATED(last%next))
475            last => last%next
476         ENDDO
477         newberg%prev => last
478         last%next    => newberg
479         last         => newberg
480      ELSE                       ! list is empty so create it
481         first_berg => newberg
482      ENDIF
483      !
484   END SUBROUTINE icb_utl_insert
485
486
487   REAL(wp) FUNCTION icb_utl_yearday(kmon, kday, khr, kmin, ksec)
488      !!----------------------------------------------------------------------
489      !!                 ***  FUNCTION icb_utl_yearday  ***
490      !!
491      !! ** Purpose :   
492      !!
493      ! sga - improved but still only applies to 365 day year, need to do this properly
494      !
495      !!gm  all these info are already known in daymod, no???
496      !!
497      !!----------------------------------------------------------------------
498      INTEGER, INTENT(in)     :: kmon, kday, khr, kmin, ksec
499      !
500      INTEGER, DIMENSION(12)  :: imonths = (/ 0,31,28,31,30,31,30,31,31,30,31,30 /)
501      !!----------------------------------------------------------------------
502      !
503      icb_utl_yearday = REAL( SUM( imonths(1:kmon) ), wp )
504      icb_utl_yearday = icb_utl_yearday + REAL(kday-1,wp) + (REAL(khr,wp) + (REAL(kmin,wp) + REAL(ksec,wp)/60.)/60.)/24.
505      !
506   END FUNCTION icb_utl_yearday
507
508   !!-------------------------------------------------------------------------
509
510   SUBROUTINE icb_utl_delete( first, berg )
511      !!----------------------------------------------------------------------
512      !!                 ***  ROUTINE icb_utl_delete  ***
513      !!
514      !! ** Purpose :   
515      !!
516      !!----------------------------------------------------------------------
517      TYPE(iceberg), POINTER :: first, berg
518      !!----------------------------------------------------------------------
519      ! Connect neighbors to each other
520      IF ( ASSOCIATED(berg%prev) ) THEN
521        berg%prev%next => berg%next
522      ELSE
523        first => berg%next
524      ENDIF
525      IF (ASSOCIATED(berg%next)) berg%next%prev => berg%prev
526      !
527      CALL icb_utl_destroy(berg)
528      !
529   END SUBROUTINE icb_utl_delete
530
531
532   SUBROUTINE icb_utl_destroy( berg )
533      !!----------------------------------------------------------------------
534      !!                 ***  ROUTINE icb_utl_destroy  ***
535      !!
536      !! ** Purpose :   remove a single iceberg instance
537      !!
538      !!----------------------------------------------------------------------
539      TYPE(iceberg), POINTER :: berg
540      !!----------------------------------------------------------------------
541      !
542      ! Remove any points
543      IF( ASSOCIATED( berg%current_point ) )   DEALLOCATE( berg%current_point )
544      !
545      DEALLOCATE(berg)
546      !
547   END SUBROUTINE icb_utl_destroy
548
549
550   SUBROUTINE icb_utl_track( knum, cd_label, kt )
551      !!----------------------------------------------------------------------
552      !!                 ***  ROUTINE icb_utl_track  ***
553      !!
554      !! ** Purpose :   
555      !!
556      !!----------------------------------------------------------------------
557      INTEGER, DIMENSION(nkounts)    :: knum       ! iceberg number
558      CHARACTER(len=*)               :: cd_label   !
559      INTEGER                        :: kt         ! timestep number
560      !
561      TYPE(iceberg), POINTER         :: this
562      LOGICAL                        :: match
563      INTEGER                        :: k
564      !!----------------------------------------------------------------------
565      !
566      this => first_berg
567      DO WHILE( ASSOCIATED(this) )
568         match = .TRUE.
569         DO k = 1, nkounts
570            IF( this%number(k) /= knum(k) ) match = .FALSE.
571         END DO
572         IF( match )   CALL icb_utl_print_berg(this, kt)
573         this => this%next
574      END DO
575      !
576   END SUBROUTINE icb_utl_track
577
578
579   SUBROUTINE icb_utl_print_berg( berg, kt )
580      !!----------------------------------------------------------------------
581      !!                 ***  ROUTINE icb_utl_print_berg  ***
582      !!
583      !! ** Purpose :   print one
584      !!
585      !!----------------------------------------------------------------------
586      TYPE(iceberg), POINTER :: berg
587      TYPE(point)  , POINTER :: pt
588      INTEGER                :: kt      ! timestep number
589      !!----------------------------------------------------------------------
590      !
591      IF (nn_verbose_level == 0) RETURN
592      pt => berg%current_point
593      WRITE(numicb, 9200) kt, berg%number(1), &
594                   pt%xi, pt%yj, pt%lon, pt%lat, pt%uvel, pt%vvel,  &
595                   pt%uo, pt%vo, pt%ua, pt%va, pt%ui, pt%vi
596      CALL flush( numicb )
597 9200 FORMAT(5x,i5,2x,i10,6(2x,2f10.4))
598      !
599   END SUBROUTINE icb_utl_print_berg
600
601
602   SUBROUTINE icb_utl_print( cd_label, kt )
603      !!----------------------------------------------------------------------
604      !!                 ***  ROUTINE icb_utl_print  ***
605      !!
606      !! ** Purpose :   print many
607      !!
608      !!----------------------------------------------------------------------
609      CHARACTER(len=*)       :: cd_label
610      INTEGER                :: kt             ! timestep number
611      !
612      INTEGER                :: ibergs, inbergs
613      TYPE(iceberg), POINTER :: this
614      !!----------------------------------------------------------------------
615      !
616      IF (nn_verbose_level == 0) RETURN
617      this => first_berg
618      IF( ASSOCIATED(this) ) THEN
619         WRITE(numicb,'(a," pe=(",i3,")")' ) cd_label, narea
620         WRITE(numicb,'(a8,4x,a6,12x,a5,15x,a7,19x,a3,17x,a5,17x,a5,17x,a5)' )   &
621            &         'timestep', 'number', 'xi,yj','lon,lat','u,v','uo,vo','ua,va','ui,vi'
622      ENDIF
623      DO WHILE( ASSOCIATED(this) )
624        CALL icb_utl_print_berg(this, kt)
625        this => this%next
626      END DO
627      ibergs = icb_utl_count()
628      inbergs = ibergs
629      CALL mpp_sum('icbutl', inbergs)
630      IF( ibergs > 0 )   WRITE(numicb,'(a," there are",i5," bergs out of",i6," on PE ",i4)')   &
631         &                                  cd_label, ibergs, inbergs, narea
632      !
633   END SUBROUTINE icb_utl_print
634
635
636   SUBROUTINE icb_utl_incr()
637      !!----------------------------------------------------------------------
638      !!                 ***  ROUTINE icb_utl_incr  ***
639      !!
640      !! ** Purpose :   
641      !!
642      ! Small routine for coping with very large integer values labelling icebergs
643      ! num_bergs is a array of integers
644      ! the first member is incremented in steps of jpnij starting from narea
645      ! this means each iceberg is labelled with a unique number
646      ! when this gets to the maximum allowed integer the second and subsequent members are
647      ! used to count how many times the member before cycles
648      !!----------------------------------------------------------------------
649      INTEGER ::   ii, ibig
650      !!----------------------------------------------------------------------
651
652      ibig = HUGE(num_bergs(1))
653      IF( ibig-jpnij < num_bergs(1) ) THEN
654         num_bergs(1) = narea
655         DO ii = 2,nkounts
656            IF( num_bergs(ii) == ibig ) THEN
657               num_bergs(ii) = 0
658               IF( ii == nkounts ) CALL ctl_stop('Sorry, run out of iceberg number space')
659            ELSE
660               num_bergs(ii) = num_bergs(ii) + 1
661               EXIT
662            ENDIF
663         END DO
664      ELSE
665         num_bergs(1) = num_bergs(1) + jpnij
666      ENDIF
667      !
668   END SUBROUTINE icb_utl_incr
669
670
671   INTEGER FUNCTION icb_utl_count()
672      !!----------------------------------------------------------------------
673      !!                 ***  FUNCTION icb_utl_count  ***
674      !!
675      !! ** Purpose :   
676      !!----------------------------------------------------------------------
677      TYPE(iceberg), POINTER :: this
678      !!----------------------------------------------------------------------
679      !
680      icb_utl_count = 0
681      this => first_berg
682      DO WHILE( ASSOCIATED(this) )
683         icb_utl_count = icb_utl_count+1
684         this => this%next
685      END DO
686      !
687   END FUNCTION icb_utl_count
688
689
690   REAL(wp) FUNCTION icb_utl_mass( first, justbits, justbergs )
691      !!----------------------------------------------------------------------
692      !!                 ***  FUNCTION icb_utl_mass  ***
693      !!
694      !! ** Purpose :   compute the mass all iceberg, all berg bits or all bergs.
695      !!----------------------------------------------------------------------
696      TYPE(iceberg)      , POINTER  ::   first
697      TYPE(point)        , POINTER  ::   pt
698      LOGICAL, INTENT(in), OPTIONAL ::   justbits, justbergs
699      !
700      TYPE(iceberg), POINTER ::   this
701      !!----------------------------------------------------------------------
702      icb_utl_mass = 0._wp
703      this => first
704      !
705      IF( PRESENT( justbergs  ) ) THEN
706         DO WHILE( ASSOCIATED( this ) )
707            pt => this%current_point
708            icb_utl_mass = icb_utl_mass + pt%mass         * this%mass_scaling
709            this => this%next
710         END DO
711      ELSEIF( PRESENT(justbits) ) THEN
712         DO WHILE( ASSOCIATED( this ) )
713            pt => this%current_point
714            icb_utl_mass = icb_utl_mass + pt%mass_of_bits * this%mass_scaling
715            this => this%next
716         END DO
717      ELSE
718         DO WHILE( ASSOCIATED( this ) )
719            pt => this%current_point
720            icb_utl_mass = icb_utl_mass + ( pt%mass + pt%mass_of_bits ) * this%mass_scaling
721            this => this%next
722         END DO
723      ENDIF
724      !
725   END FUNCTION icb_utl_mass
726
727
728   REAL(wp) FUNCTION icb_utl_heat( first, justbits, justbergs )
729      !!----------------------------------------------------------------------
730      !!                 ***  FUNCTION icb_utl_heat  ***
731      !!
732      !! ** Purpose :   compute the heat in all iceberg, all bergies or all bergs.
733      !!----------------------------------------------------------------------
734      TYPE(iceberg)      , POINTER  ::   first
735      LOGICAL, INTENT(in), OPTIONAL ::   justbits, justbergs
736      !
737      TYPE(iceberg)      , POINTER  ::   this
738      TYPE(point)        , POINTER  ::   pt
739      !!----------------------------------------------------------------------
740      icb_utl_heat = 0._wp
741      this => first
742      !
743      IF( PRESENT( justbergs  ) ) THEN
744         DO WHILE( ASSOCIATED( this ) )
745            pt => this%current_point
746            icb_utl_heat = icb_utl_heat + pt%mass         * this%mass_scaling * pt%heat_density
747            this => this%next
748         END DO
749      ELSEIF( PRESENT(justbits) ) THEN
750         DO WHILE( ASSOCIATED( this ) )
751            pt => this%current_point
752            icb_utl_heat = icb_utl_heat + pt%mass_of_bits * this%mass_scaling * pt%heat_density
753            this => this%next
754         END DO
755      ELSE
756         DO WHILE( ASSOCIATED( this ) )
757            pt => this%current_point
758            icb_utl_heat = icb_utl_heat + ( pt%mass + pt%mass_of_bits ) * this%mass_scaling * pt%heat_density
759            this => this%next
760         END DO
761      ENDIF
762      !
763   END FUNCTION icb_utl_heat
764
765   !!======================================================================
766END MODULE icbutl
Note: See TracBrowser for help on using the repository browser.