Skip to content
Snippets Groups Projects
icedyn_rhg_vp.F90 87.8 KiB
Newer Older
Guillaume Samson's avatar
Guillaume Samson committed
                  ! - Stop if error is too large ("safeguard against bad forcing" of original Zhang routine)
                  IF ( i_inn > 1 .AND. zverr_max > zuerr_max_vp ) THEN
                      IF ( lwp ) WRITE(numout,*) " VP rheology error was too large : ", zverr_max, " in outer V-iteration ", i_out, " after ", i_inn, " iterations, we stopped "
                      ll_v_iterate = .FALSE.
                  ENDIF
                  
                  ! - Stop if error small enough
                  IF ( zverr_max < zuerr_min_vp ) THEN                                        
                      IF ( lwp ) WRITE(numout,*) " VP rheology nicely done in outer V-iteration ", i_out, " after ", i_inn, " iterations, finished! "
                      ll_v_iterate = .FALSE.
                  ENDIF
                  
               ENDIF ! ll_v_iterate

            ENDIF ! ---    end ll_u_iterate or ll_v_iterate
               
            !---------------------------------------------------------------------------------------
            !
            ! --- Calculate extra convergence diagnostics and save them
            !
            !---------------------------------------------------------------------------------------
            IF( nn_rhg_chkcvg/=0 .AND. MOD ( i_inn - 1, nn_vp_chkcvg ) == 0 ) THEN

               CALL rhg_cvg_vp( kt, i_out, i_inn, i_inn_tot, nn_vp_nout, nn_vp_ninn, nn_nvp,        &
                      &         u_ice, v_ice, zu_b, zv_b, zu_c, zv_c,                               &
                      &         zmt, za_iU, za_iV, zuerr_max, zverr_max, zglob_area,                &
                      &         zrhsu, zAU, zBU, zCU, zDU, zEU, zFU,                                &
                      &         zrhsv, zAV, zBV, zCV, zDV, zEV, zFV,                                &
                                zvel_res, zvel_diff )

            ENDIF

         END DO ! i_inn, end of inner loop

      END DO ! End of outer loop (i_out) =============================================================================================

      IF( nn_rhg_chkcvg/=0  ) THEN
          
         IF( iom_use('velo_res') )   CALL iom_put( 'velo_res', zvel_res  )   ! linear system residual  @last inner&outer iteration
         IF( iom_use('velo_ero') )   CALL iom_put( 'velo_ero', zvel_diff )   ! abs velocity difference @last outer iteration
         IF( iom_use('uice_eri') )   CALL iom_put( 'uice_eri', zuerr     )   ! abs velocity difference @last inner iteration
         IF( iom_use('vice_eri') )   CALL iom_put( 'vice_eri', zverr     )   ! abs velocity difference @last inner iteration
        
      ENDIF ! nn_rhg_chkcvg

      !------------------------------------------------------------------------------!
      !
      ! --- Recompute delta, shear and div (inputs for mechanical redistribution) 
      !
      !------------------------------------------------------------------------------!
      !
      DO_2D( nn_hls, nn_hls-1, nn_hls, nn_hls-1 ) ! 1->jpj-1; 1->jpi-1
Guillaume Samson's avatar
Guillaume Samson committed

            ! shear at F points
            zds(ji,jj) = ( ( u_ice(ji,jj+1) * r1_e1u(ji,jj+1) - u_ice(ji,jj) * r1_e1u(ji,jj) ) * e1f(ji,jj) * e1f(ji,jj)   &
               &         + ( v_ice(ji+1,jj) * r1_e2v(ji+1,jj) - v_ice(ji,jj) * r1_e2v(ji,jj) ) * e2f(ji,jj) * e2f(ji,jj)   &
               &         ) * r1_e1e2f(ji,jj) * fimask(ji,jj)

      END_2D      
      
      DO_2D( 0, 0, 0, 0 ) ! 2->jpj-1; 2->jpi-1
            
            ! shear**2 at T points (doc eq. A16)
            zds2 = ( zds(ji,jj  ) * zds(ji,jj  ) * e1e2f(ji,jj  ) + zds(ji-1,jj  ) * zds(ji-1,jj  ) * e1e2f(ji-1,jj  )  &
               &   + zds(ji,jj-1) * zds(ji,jj-1) * e1e2f(ji,jj-1) + zds(ji-1,jj-1) * zds(ji-1,jj-1) * e1e2f(ji-1,jj-1)  &
               &   ) * 0.25_wp * r1_e1e2t(ji,jj)
Guillaume Samson's avatar
Guillaume Samson committed
            
            ! tension**2 at T points
            zdt  = ( ( u_ice(ji,jj) * r1_e2u(ji,jj) - u_ice(ji-1,jj) * r1_e2u(ji-1,jj) ) * e2t(ji,jj) * e2t(ji,jj)   &
               &   - ( v_ice(ji,jj) * r1_e1v(ji,jj) - v_ice(ji,jj-1) * r1_e1v(ji,jj-1) ) * e1t(ji,jj) * e1t(ji,jj)   &
               &   ) * r1_e1e2t(ji,jj)
            zdt2 = zdt * zdt
            
            zten_i(ji,jj) = zdt * zmsk(ji,jj)
Guillaume Samson's avatar
Guillaume Samson committed
            
            ! maximum shear rate at T points (includes tension, output only)
            pshear_i(ji,jj) = SQRT( zdt2 + zds2 ) * zmsk(ji,jj)
Guillaume Samson's avatar
Guillaume Samson committed
            
            ! shear at T-points
            zshear(ji,jj)   = SQRT( zds2 ) * zmsk(ji,jj)
Guillaume Samson's avatar
Guillaume Samson committed

            ! divergence at T points
            pdivu_i(ji,jj) = ( e2u(ji,jj) * u_ice(ji,jj) - e2u(ji-1,jj) * u_ice(ji-1,jj)   &
               &             + e1v(ji,jj) * v_ice(ji,jj) - e1v(ji,jj-1) * v_ice(ji,jj-1)   &
               &             ) * r1_e1e2t(ji,jj) * zmsk(ji,jj)
Guillaume Samson's avatar
Guillaume Samson committed
            
            ! delta at T points
            zfac               = SQRT( pdivu_i(ji,jj) * pdivu_i(ji,jj) + ( zdt2 + zds2 ) * z1_ecc2 ) * zmsk(ji,jj) ! delta
            zdelta(ji,jj)      = zfac
            
            ! delta* at T points
            rswitch            =   1._wp - MAX( 0._wp, SIGN( 1._wp, -zfac ) ) ! 0 if delta=0
            pdelta_i(ji,jj)    = zfac + rn_creepl ! * rswitch
           
      END_2D
Guillaume Samson's avatar
Guillaume Samson committed

      CALL lbc_lnk( 'icedyn_rhg_vp', pshear_i, 'T', 1._wp, pdivu_i, 'T', 1._wp, pdelta_i, 'T', 1._wp, &
              &                      zdelta  , 'T', 1._wp, zten_i , 'T', 1._wp, zshear  , 'T', 1._wp )
Guillaume Samson's avatar
Guillaume Samson committed

      ! --- Sea ice stresses at T-points --- !
Guillaume Samson's avatar
Guillaume Samson committed
      IF ( iom_use('normstr')    .OR. iom_use('sheastr')    .OR. &
     &     iom_use('intstrx')    .OR. iom_use('intstry')    .OR. &
     &     iom_use('sig1_pnorm') .OR. iom_use('sig2_pnorm') ) THEN
      
         ! sigma1, sigma2, sigma12 are some recombination of the stresses (HD MWR002, Bouillon et al., OM2013)
         ! not to be confused with stress tensor components, stress invariants, or stress principal components

         DO_2D( nn_hls, nn_hls, nn_hls, nn_hls ) ! 2->jpj-1; 2->jpi-1

            zvisc_t(ji,jj)     =   strength(ji,jj) / pdelta_i(ji,jj) ! update viscosity
            zfac               =   zvisc_t(ji,jj)
            zs1(ji,jj)         =   zfac * ( pdivu_i(ji,jj) - zdelta(ji,jj) )
            zs2(ji,jj)         =   zfac * z1_ecc2 * zten_i(ji,jj)
            zs12(ji,jj)        =   zfac * z1_ecc2 * zshear(ji,jj) * 0.5_wp 
!!$         CALL lbc_lnk( 'icedyn_rhg_vp', zs1, 'T', 1., zs2, 'T', 1., zs12, 'T', 1. )
      ! --- Shear (s12) at F-points --- !
Guillaume Samson's avatar
Guillaume Samson committed
      IF ( iom_use('intstrx') .OR. iom_use('intstry') ) THEN

         DO_2D( nn_hls, nn_hls-1, nn_hls, nn_hls-1 ) ! 1->jpj-1; 1->jpi-1
Guillaume Samson's avatar
Guillaume Samson committed
            
               ! P/delta* at F points
               zvisc_f = 0.25_wp * ( zvisc_t(ji,jj) + zvisc_t(ji+1,jj) + zvisc_t(ji,jj+1) + zvisc_t(ji+1,jj+1) )
Guillaume Samson's avatar
Guillaume Samson committed
               
               ! s12 at F-points 
               zs12f(ji,jj) = zvisc_f * z1_ecc2 * zds(ji,jj)
Guillaume Samson's avatar
Guillaume Samson committed
               
         END_2D

         CALL lbc_lnk( 'icedyn_rhg_vp', zs12f, 'F', 1. )
         
      ENDIF
      
      !------------------------------------------------------------------------------!
      !
      ! --- Diagnostics
Guillaume Samson's avatar
Guillaume Samson committed
      !
      !------------------------------------------------------------------------------!
Guillaume Samson's avatar
Guillaume Samson committed
      !
Guillaume Samson's avatar
Guillaume Samson committed
      ! --- Ice-ocean, ice-atm. & ice-ocean bottom (landfast) stresses --- !
      IF(  iom_use('utau_oi') .OR. iom_use('vtau_oi') .OR. iom_use('utau_ai') .OR. iom_use('vtau_ai') .OR. &
         & iom_use('utau_bi') .OR. iom_use('vtau_bi') ) THEN

         ALLOCATE( ztaux_oi(jpi,jpj) , ztauy_oi(jpi,jpj) )

         !--- Recalculate oceanic stress at last inner iteration
         DO_2D( nn_hls-1, nn_hls-1, nn_hls-1, nn_hls-1 ) ! 2->jpj-1; 2->jpi-1

                !--- ice u-velocity @V points, v-velocity @U points (for non-linear drag computation)
                zu_cV            = 0.25_wp * ( u_ice(ji,jj) + u_ice(ji-1,jj) + u_ice(ji,jj+1) + u_ice(ji-1,jj+1) ) * vmask(ji,jj,1)
                zv_cU            = 0.25_wp * ( v_ice(ji,jj) + v_ice(ji,jj-1) + v_ice(ji+1,jj) + v_ice(ji+1,jj-1) ) * umask(ji,jj,1)
                
                !--- non-linear drag coefficients (need to be updated at each outer loop, see Lemieux and Tremblay JGR09, p.3, beginning of Section 3)
                zCwU(ji,jj)          = za_iU(ji,jj) * zrhoco * SQRT( ( u_ice(ji,jj) - u_oce (ji,jj) ) * ( u_ice(ji,jj) - u_oce (ji,jj) )  &
                  &                                                + ( zv_cU - v_oceU(ji,jj) ) * ( zv_cU - v_oceU(ji,jj) ) )
                zCwV(ji,jj)          = za_iV(ji,jj) * zrhoco * SQRT( ( v_ice(ji,jj) - v_oce (ji,jj) ) * ( v_ice(ji,jj) - v_oce (ji,jj) )  &
                  &                                                + ( zu_cV - u_oceV(ji,jj) ) * ( zu_cV - u_oceV(ji,jj) ) )
                 
                !--- Ocean-ice stress
                ztaux_oi(ji,jj) = zCwU(ji,jj) * ( u_oce(ji,jj) - u_ice(ji,jj) )
                ztauy_oi(ji,jj) = zCwV(ji,jj) * ( v_oce(ji,jj) - v_ice(ji,jj) )
                
         END_2D
         
         !
         CALL lbc_lnk( 'icedyn_rhg_vp', ztaux_oi, 'U', -1., ztauy_oi, 'V', -1., ztaux_ai, 'U', -1., ztauy_ai, 'V', -1. ) !, &
!            &                          ztaux_bi, 'U', -1., ztauy_bi, 'V', -1. )
         !
         CALL iom_put( 'utau_oi' , ztaux_oi * zmsk00 )
         CALL iom_put( 'vtau_oi' , ztauy_oi * zmsk00 )
         CALL iom_put( 'utau_ai' , ztaux_ai * zmsk00 )
         CALL iom_put( 'vtau_ai' , ztauy_ai * zmsk00 )
!        CALL iom_put( 'utau_bi' , ztaux_bi * zmsk00 )
!        CALL iom_put( 'vtau_bi' , ztauy_bi * zmsk00 )

         DEALLOCATE( ztaux_oi , ztauy_oi )

      ENDIF
       
      ! --- Divergence, shear and strength --- !
      IF( iom_use('icediv') )   CALL iom_put( 'icediv' , pdivu_i  * zmsk00 )   ! divergence
      IF( iom_use('iceshe') )   CALL iom_put( 'iceshe' , pshear_i * zmsk00 )   ! maximum shear rate
      IF( iom_use('icedlt') )   CALL iom_put( 'icedlt' , zdelta   * zmsk00 )   ! delta
Guillaume Samson's avatar
Guillaume Samson committed
      IF( iom_use('icestr') )   CALL iom_put( 'icestr' , strength * zmsk00 )   ! strength

      ! --- Stress tensor invariants (SIMIP diags) --- !
      IF( iom_use('normstr') .OR. iom_use('sheastr') ) THEN
         !
         ALLOCATE( zsig_I(jpi,jpj) , zsig_II(jpi,jpj) )
         !         
         ! Stress invariants (sigma_I, sigma_II, Coon 1974, Feltham 2008)
         DO_2D( nn_hls, nn_hls, nn_hls, nn_hls ) ! 2->jpj-1; 2->jpi-1
            zsig_I(ji,jj)    =   0.5_wp * zs1(ji,jj)
            zsig_II(ji,jj)   =   0.5_wp * SQRT ( zs2(ji,jj) * zs2(ji,jj) + 4. * zs12(ji,jj) * zs12(ji,jj) )
Guillaume Samson's avatar
Guillaume Samson committed
         END_2D

!!$         CALL lbc_lnk( 'icedyn_rhg_vp', zsig_I, 'T', 1., zsig_II, 'T', 1.)
Guillaume Samson's avatar
Guillaume Samson committed
         
         IF( iom_use('normstr') )   CALL iom_put( 'normstr' ,   zsig_I(:,:)  * zmsk00(:,:) ) ! Normal stress
         IF( iom_use('sheastr') )   CALL iom_put( 'sheastr' ,   zsig_II(:,:) * zmsk00(:,:) ) ! Maximum shear stress
         
         DEALLOCATE ( zsig_I, zsig_II )
         
      ENDIF

      ! --- Normalized stress tensor principal components --- !
      ! These are used to plot the normalized yield curve (Lemieux & Dupont, GMD 2020)
      ! To plot the yield curve and evaluate physical convergence, they have two recommendations
      ! Recommendation 1 : Use ice strength, not replacement pressure
      ! Recommendation 2 : Need to use deformations at PREVIOUS iterate for viscosities (see p. 1765)
      ! R2 means we need to recompute stresses

      IF( iom_use('sig1_pnorm') .OR. iom_use('sig2_pnorm') ) THEN
         !
         ALLOCATE( zsig1_p(jpi,jpj) , zsig2_p(jpi,jpj) , zsig_I(jpi,jpj) , zsig_II(jpi,jpj) )
         !         
         DO_2D( 0, 0, 0, 0 ) ! clem: check bounds
Guillaume Samson's avatar
Guillaume Samson committed
         
               ! Ice stresses computed with **viscosities** (P/delta) at **previous** iterates 
Guillaume Samson's avatar
Guillaume Samson committed
               !                        and **deformations** at current iterates
               !                        following Lemieux & Dupont (2020)
               zfac             =   zvisc_t_prev(ji,jj)
               zsig1            =   zfac * ( pdivu_i(ji,jj) - zdelta(ji,jj) )  
               zsig2            =   zfac * z1_ecc2 * zten_i(ji,jj)
               zsig12           =   zfac * z1_ecc2 * zshear(ji,jj) * 0.5_wp
Guillaume Samson's avatar
Guillaume Samson committed
               
               ! Stress invariants (sigma_I, sigma_II, Coon 1974, Feltham 2008), T-point
               zsig_I(ji,jj)    =   0.5_wp * zsig1                                          ! normal stress
               zsig_II(ji,jj)   =   0.5_wp * SQRT ( zsig2 * zsig2 + 4. *zsig12 * zsig12 )   ! max shear stress
Guillaume Samson's avatar
Guillaume Samson committed

               ! Normalized  principal stresses (used to display the ellipse)
               z1_strength      =   1._wp / MAX ( 1._wp , strength(ji,jj) )
               zsig1_p(ji,jj)   =   ( zsig_I(ji,jj) + zsig_II(ji,jj) ) * z1_strength
               zsig2_p(ji,jj)   =   ( zsig_I(ji,jj) - zsig_II(ji,jj) ) * z1_strength
               
         END_2D
         !
         ! CALL lbc_lnk( 'icedyn_rhg_vp', zsig1_p, 'T', 1., zsig2_p, 'T', 1.)
Guillaume Samson's avatar
Guillaume Samson committed
         !
         CALL iom_put( 'sig1_pnorm' , zsig1_p ) 
         CALL iom_put( 'sig2_pnorm' , zsig2_p ) 

         DEALLOCATE( zsig1_p , zsig2_p , zsig_I , zsig_II )
         
      ENDIF

      ! --- SIMIP, terms of tendency for momentum equation  --- !
      IF(  iom_use('dssh_dx') .OR. iom_use('dssh_dy') .OR. &
         & iom_use('corstrx') .OR. iom_use('corstry') ) THEN

         ! --- Recalculate Coriolis stress at last inner iteration
         DO_2D( 0, 0, 0, 0 ) ! clem: check bounds
Guillaume Samson's avatar
Guillaume Samson committed
                ! --- U-component 
                zCorU(ji,jj)         =   0.25_wp * r1_e1u(ji,jj) *  &
                           &             ( zmf(ji  ,jj) * ( e1v(ji  ,jj) * v_ice(ji  ,jj) + e1v(ji  ,jj-1) * v_ice(ji  ,jj-1) )  &
                           &             + zmf(ji+1,jj) * ( e1v(ji+1,jj) * v_ice(ji+1,jj) + e1v(ji+1,jj-1) * v_ice(ji+1,jj-1) ) )
                zCorV(ji,jj)         = - 0.25_wp * r1_e2v(ji,jj) *  &
                           &             ( zmf(ji,jj  ) * ( e2u(ji,jj  ) * u_ice(ji,jj  ) + e2u(ji-1,jj  ) * u_ice(ji-1,jj  ) )  &
                           &             + zmf(ji,jj+1) * ( e2u(ji,jj+1) * u_ice(ji,jj+1) + e2u(ji-1,jj+1) * u_ice(ji-1,jj+1) ) )
         END_2D
         !
         CALL lbc_lnk( 'icedyn_rhg_vp', zspgU, 'U', -1., zspgV, 'V', -1., &
            &                           zCorU, 'U', -1., zCorV, 'V', -1. )
         !
         CALL iom_put( 'dssh_dx' , zspgU * zmsk00 )   ! Sea-surface tilt term in force balance (x)
         CALL iom_put( 'dssh_dy' , zspgV * zmsk00 )   ! Sea-surface tilt term in force balance (y)
         CALL iom_put( 'corstrx' , zCorU * zmsk00 )   ! Coriolis force term in force balance (x)
         CALL iom_put( 'corstry' , zCorV * zmsk00 )   ! Coriolis force term in force balance (y)

      ENDIF
      
      IF ( iom_use('intstrx') .OR. iom_use('intstry') ) THEN

         ! Recalculate internal forces (divergence of stress tensor) at last inner iteration
         DO_2D( 0, 0, 0, 0 ) ! clem: check bounds
Guillaume Samson's avatar
Guillaume Samson committed

               zfU(ji,jj) = 0.5_wp * ( ( zs1(ji+1,jj) - zs1(ji,jj) ) * e2u(ji,jj)                                             &
                  &                  + ( zs2(ji+1,jj) * e2t(ji+1,jj) * e2t(ji+1,jj) - zs2(ji,jj) * e2t(ji,jj) * e2t(ji,jj)    &
                  &                    ) * r1_e2u(ji,jj)                                                                      &
                  &                  + ( zs12f(ji,jj) * e1f(ji,jj) * e1f(ji,jj) - zs12f(ji,jj-1) * e1f(ji,jj-1) * e1f(ji,jj-1)  &
                  &                    ) * 2._wp * r1_e1u(ji,jj)                                                              &
                  &                  ) * r1_e1e2u(ji,jj)

               zfV(ji,jj) = 0.5_wp * ( ( zs1(ji,jj+1) - zs1(ji,jj) ) * e1v(ji,jj)                                             &
                  &                  - ( zs2(ji,jj+1) * e1t(ji,jj+1) * e1t(ji,jj+1) - zs2(ji,jj) * e1t(ji,jj) * e1t(ji,jj)    &
                  &                    ) * r1_e1v(ji,jj)                                                                      &
                  &                  + ( zs12f(ji,jj) * e2f(ji,jj) * e2f(ji,jj) - zs12f(ji-1,jj) * e2f(ji-1,jj) * e2f(ji-1,jj)  &
                  &                    ) * 2._wp * r1_e2v(ji,jj)                                                              &
                  &                  ) * r1_e1e2v(ji,jj)

         END_2D
            
         CALL lbc_lnk( 'icedyn_rhg_vp', zfU, 'U', -1., zfV, 'V', -1. )
         
         CALL iom_put( 'intstrx' , zfU   * zmsk00 )   ! Internal force term in force balance (x)
         CALL iom_put( 'intstry' , zfV   * zmsk00 )   ! Internal force term in force balance (y)
         
      ENDIF

      ! --- Ice & snow mass and ice area transports
      IF(  iom_use('xmtrpice') .OR. iom_use('ymtrpice') .OR. &
         & iom_use('xmtrpsnw') .OR. iom_use('ymtrpsnw') .OR. iom_use('xatrp') .OR. iom_use('yatrp') ) THEN
         !
         ALLOCATE( zdiag_xmtrp_ice(jpi,jpj) , zdiag_ymtrp_ice(jpi,jpj) , &
            &      zdiag_xmtrp_snw(jpi,jpj) , zdiag_ymtrp_snw(jpi,jpj) , zdiag_xatrp(jpi,jpj) , zdiag_yatrp(jpi,jpj) )
         !
         DO_2D( 0, 0, 0, 0 ) ! clem: check bounds
Guillaume Samson's avatar
Guillaume Samson committed

               zfac_x = 0.5 * u_ice(ji,jj) * e2u(ji,jj) * zmsk00(ji,jj)
               zfac_y = 0.5 * v_ice(ji,jj) * e1v(ji,jj) * zmsk00(ji,jj)

               zdiag_xmtrp_ice(ji,jj) = rhoi * zfac_x * ( vt_i(ji+1,jj) + vt_i(ji,jj) ) ! ice mass transport, X-component
               zdiag_ymtrp_ice(ji,jj) = rhoi * zfac_y * ( vt_i(ji,jj+1) + vt_i(ji,jj) ) !        ''           Y-   ''

               zdiag_xmtrp_snw(ji,jj) = rhos * zfac_x * ( vt_s(ji+1,jj) + vt_s(ji,jj) ) ! snow mass transport, X-component
               zdiag_ymtrp_snw(ji,jj) = rhos * zfac_y * ( vt_s(ji,jj+1) + vt_s(ji,jj) ) !          ''          Y-   ''

               zdiag_xatrp(ji,jj)     = zfac_x * ( at_i(ji+1,jj) + at_i(ji,jj) )        ! area transport,      X-component
               zdiag_yatrp(ji,jj)     = zfac_y * ( at_i(ji,jj+1) + at_i(ji,jj) )        !        ''            Y-   ''

         END_2D
         
         CALL lbc_lnk( 'icedyn_rhg_vp', zdiag_xmtrp_ice, 'U', -1., zdiag_ymtrp_ice, 'V', -1., &
            &                           zdiag_xmtrp_snw, 'U', -1., zdiag_ymtrp_snw, 'V', -1., &
            &                           zdiag_xatrp    , 'U', -1., zdiag_yatrp    , 'V', -1. )

         CALL iom_put( 'xmtrpice' , zdiag_xmtrp_ice )   ! X-component of sea-ice mass transport (kg/s)
         CALL iom_put( 'ymtrpice' , zdiag_ymtrp_ice )   ! Y-component of sea-ice mass transport 
         CALL iom_put( 'xmtrpsnw' , zdiag_xmtrp_snw )   ! X-component of snow mass transport (kg/s)
         CALL iom_put( 'ymtrpsnw' , zdiag_ymtrp_snw )   ! Y-component of snow mass transport
         CALL iom_put( 'xatrp'    , zdiag_xatrp     )   ! X-component of ice area transport
         CALL iom_put( 'yatrp'    , zdiag_yatrp     )   ! Y-component of ice area transport

         DEALLOCATE( zdiag_xmtrp_ice , zdiag_ymtrp_ice , &
            &        zdiag_xmtrp_snw , zdiag_ymtrp_snw , zdiag_xatrp , zdiag_yatrp )

      ENDIF

   END SUBROUTINE ice_dyn_rhg_vp
   
   
   SUBROUTINE rhg_cvg_vp( kt, kitout, kitinn, kitinntot, kitoutmax, kitinnmax, kitinntotmax , &
                  &       pu, pv, pub, pvb, pub_outer, pvb_outer                     , &
                  &       pmt, pat_iu, pat_iv, puerr_max, pverr_max, pglob_area      , &
                  &       prhsu, pAU, pBU, pCU, pDU, pEU, pFU                        , &
                  &       prhsv, pAV, pBV, pCV, pDV, pEV, pFV                        , &   
                  &       pvel_res, pvel_diff                                            )
      !!
      !!----------------------------------------------------------------------
      !!                    ***  ROUTINE rhg_cvg_vp  ***
      !!                     
      !! ** Purpose :   check convergence of VP ice rheology
      !!
      !! ** Method  :   create a file ice_cvg.nc containing a few convergence diagnostics
      !!                This routine is called every sub-iteration, so it is cpu expensive
      !!
      !!                Calculates / stores
      !!                   - maximum absolute U-V difference (uice_cvg, u_dif, v_dif, m/s)
      !!                   - residuals in U, V and UV-mean taken as square-root of area-weighted mean square residual (u_res, v_res, vel_res, N/m2)
      !!                   - mean kinetic energy (mke_ice, J/m2)
      !!
      !! ** Note    :   for the first sub-iteration, uice_cvg is set to 0 (too large otherwise)   
      !!
      !!----------------------------------------------------------------------
      !!
      INTEGER ,                 INTENT(in) ::   kt, kitout, kitinn, kitinntot    ! ocean model iterate, outer, inner and total n-iterations
      INTEGER ,                 INTENT(in) ::   kitoutmax, kitinnmax             ! max number of outer & inner iterations
      INTEGER ,                 INTENT(in) ::   kitinntotmax                     ! max number of total sub-iterations
      REAL(wp), DIMENSION(:,:), INTENT(in) ::   pu, pv, pub, pvb                 ! now & sub-iter-before velocities
      REAL(wp), DIMENSION(:,:), INTENT(in) ::   pub_outer, pvb_outer             ! velocities @before outer iterations
      REAL(wp), DIMENSION(:,:), INTENT(in) ::   pmt, pat_iu, pat_iv              ! mass at T-point, ice concentration at U&V
      REAL(wp),                 INTENT(in) ::   puerr_max, pverr_max             ! absolute mean velocity difference
      REAL(wp),                 INTENT(in) ::   pglob_area                       ! global ice area
      REAL(wp), DIMENSION(:,:), INTENT(in) ::   prhsu, pAU, pBU, pCU, pDU, pEU, pFU ! linear system coefficients 
      REAL(wp), DIMENSION(:,:), INTENT(in) ::   prhsv, pAV, pBV, pCV, pDV, pEV, pFV
      REAL(wp), DIMENSION(:,:), INTENT(inout) ::  pvel_res                       ! velocity residual @last inner iteration
      REAL(wp), DIMENSION(:,:), INTENT(inout) ::  pvel_diff                      ! velocity difference @last outer iteration
      !!

      INTEGER           ::   idtime, istatus, ix_dim, iy_dim
      INTEGER           ::   ji, jj          ! dummy loop indices
      INTEGER           ::   it_inn_file, it_out_file
      REAL(wp)          ::   zu_res_mean, zv_res_mean, zvel_res_mean                  ! mean residuals of the linear system
      REAL(wp)          ::   zu_mad, zv_mad, zvel_mad                                 ! mean absolute deviation, sub-iterates
      REAL(wp)          ::   zu_mad_outer, zv_mad_outer, zvel_mad_outer               ! mean absolute deviation, outer-iterates
      REAL(wp)          ::   zvel_err_max, zmke, zu, zv                               ! local scalars
      REAL(wp)          ::   z1_pglob_area                                            ! inverse global ice area

      REAL(wp), DIMENSION(jpi,jpj) ::   zu_res, zv_res, zvel2                         ! local arrays
      REAL(wp), DIMENSION(jpi,jpj) ::   zu_diff, zv_diff                              ! local arrays
                                                                             
      CHARACTER(len=20) ::   clname
      !!----------------------------------------------------------------------


      IF( lwp ) THEN

         WRITE(numout,*)
         WRITE(numout,*) 'rhg_cvg_vp : ice rheology convergence control'
         WRITE(numout,*) '~~~~~~~~~~~'
         WRITE(numout,*) ' kt          =  : ', kt
         WRITE(numout,*) ' kitout      =  : ', kitout
         WRITE(numout,*) ' kitinn      =  : ', kitinn
         WRITE(numout,*) ' kitinntot   =  : ', kitinntot
         WRITE(numout,*) ' kitoutmax (nn_vp_nout) =  ', kitoutmax
         WRITE(numout,*) ' kitinnmax (nn_vp_ninn) =  ', kitinnmax
         WRITE(numout,*) ' kitinntotmax (nn_nvp)  =  ', kitinntotmax
         WRITE(numout,*)

      ENDIF

      z1_pglob_area = 1._wp / pglob_area      ! inverse global ice area

      ! create file
      IF( kt == nit000 .AND. kitinntot == 1 ) THEN
         !
         IF( lwm ) THEN

            clname = 'ice_cvg.nc'
            IF( .NOT. Agrif_Root() )   clname = TRIM(Agrif_CFixed())//"_"//TRIM(clname)
            istatus = NF90_CREATE( TRIM(clname), NF90_CLOBBER, ncvgid )

            istatus = NF90_DEF_DIM( ncvgid, 'time'  , NF90_UNLIMITED, idtime )
            istatus = NF90_DEF_DIM( ncvgid, 'x'     , jpi, ix_dim )
            istatus = NF90_DEF_DIM( ncvgid, 'y'     , jpj, iy_dim )

            istatus = NF90_DEF_VAR( ncvgid, 'u_res'         , NF90_DOUBLE  , (/ idtime /), nvarid_ures )
            istatus = NF90_DEF_VAR( ncvgid, 'v_res'         , NF90_DOUBLE  , (/ idtime /), nvarid_vres )
            istatus = NF90_DEF_VAR( ncvgid, 'vel_res'       , NF90_DOUBLE  , (/ idtime /), nvarid_velres )

            istatus = NF90_DEF_VAR( ncvgid, 'uerr_max_sub'  , NF90_DOUBLE  , (/ idtime /), nvarid_uerr_max )
            istatus = NF90_DEF_VAR( ncvgid, 'verr_max_sub'  , NF90_DOUBLE  , (/ idtime /), nvarid_verr_max )
            istatus = NF90_DEF_VAR( ncvgid, 'velerr_max_sub', NF90_DOUBLE  , (/ idtime /), nvarid_velerr_max )

            istatus = NF90_DEF_VAR( ncvgid, 'umad_sub'      , NF90_DOUBLE  , (/ idtime /), nvarid_umad )
            istatus = NF90_DEF_VAR( ncvgid, 'vmad_sub'      , NF90_DOUBLE  , (/ idtime /), nvarid_vmad )
            istatus = NF90_DEF_VAR( ncvgid, 'velmad_sub'    , NF90_DOUBLE  , (/ idtime /), nvarid_velmad )
            
            istatus = NF90_DEF_VAR( ncvgid, 'umad_outer'    , NF90_DOUBLE  , (/ idtime /), nvarid_umad_outer   )
            istatus = NF90_DEF_VAR( ncvgid, 'vmad_outer'    , NF90_DOUBLE  , (/ idtime /), nvarid_vmad_outer   )
            istatus = NF90_DEF_VAR( ncvgid, 'velmad_outer'  , NF90_DOUBLE  , (/ idtime /), nvarid_velmad_outer )

            istatus = NF90_DEF_VAR( ncvgid, 'mke_ice', NF90_DOUBLE  , (/ idtime /), nvarid_mke )

            istatus = NF90_ENDDEF(ncvgid)

         ENDIF
         !
      ENDIF

      !------------------------------------------------------------
      !
      ! Max absolute velocity difference with previous sub-iterate
      ! ( zvel_err_max )
      !
      !------------------------------------------------------------
      !
      ! This comes from the criterion used to stop the iterative procedure
      zvel_err_max   = 0.5_wp * ( puerr_max + pverr_max ) ! average of U- and V- maximum error over the whole domain

      !----------------------------------------------
      !
      ! Mean-absolute-deviation (sub-iterates)
      ! ( zu_mad, zv_mad, zvel_mad)
      !
      !----------------------------------------------
      !
      ! U
      
      DO_2D( 0, 0, 0, 0 ) !clem check bounds
Guillaume Samson's avatar
Guillaume Samson committed
      
         zu_diff(ji,jj) = ABS ( ( pu(ji,jj) - pub(ji,jj) ) ) * e1e2u(ji,jj) * pat_iu(ji,jj) * umask(ji,jj,1) * z1_pglob_area     
Guillaume Samson's avatar
Guillaume Samson committed
         zv_diff(ji,jj) = ABS ( ( pv(ji,jj) - pvb(ji,jj) ) ) * e1e2v(ji,jj) * pat_iv(ji,jj) * vmask(ji,jj,1) * z1_pglob_area
      
      END_2D

      ! global sum & U-V average
      zu_mad   = glob_sum( 'icedyn_rhg_vp : ', zu_diff )
      zv_mad   = glob_sum( 'icedyn_rhg_vp : ', zv_diff )

      zvel_mad = 0.5_wp * ( zu_mad + zv_mad )

      !-----------------------------------------------
      !
      ! Mean-absolute-deviation (outer-iterates)
      ! ( zu_mad_outer, zv_mad_outer, zvel_mad_outer)
      !
      !-----------------------------------------------
      !
      IF ( kitinn == kitinnmax ) THEN ! only work at the end of outer iterates 
         
         DO_2D( 0, 0, 0, 0 ) !clem check bounds
Guillaume Samson's avatar
Guillaume Samson committed
         
            zu_diff(ji,jj) = ABS ( ( pu(ji,jj) - pub_outer(ji,jj) ) ) * e1e2u(ji,jj) * pat_iu(ji,jj) * umask(ji,jj,1) * &
Guillaume Samson's avatar
Guillaume Samson committed
            zv_diff(ji,jj) = ABS ( ( pv(ji,jj) - pvb_outer(ji,jj) ) ) * e1e2v(ji,jj) * pat_iv(ji,jj) * vmask(ji,jj,1) * &
Guillaume Samson's avatar
Guillaume Samson committed
         END_2D
         
         ! Global ice-concentration, grid-cell-area weighted mean

         zu_mad_outer   = glob_sum( 'icedyn_rhg_vp : ', zu_diff )
         zv_mad_outer   = glob_sum( 'icedyn_rhg_vp : ', zv_diff )
   
         ! Average of both U & V
         zvel_mad_outer = 0.5_wp * ( zu_mad_outer + zv_mad_outer )
                  
      ENDIF

      ! --- Spatially-resolved absolute difference to send back to main routine 
      ! (last iteration only, T-point)

      IF ( kitinntot == kitinntotmax) THEN

         DO_2D( 0, 0, 0, 0 ) !clem check bounds
Guillaume Samson's avatar
Guillaume Samson committed

               zu_diff(ji,jj) = ( ABS ( ( pu(ji-1,jj) - pub_outer(ji-1,jj) ) ) * umask(ji-1,jj,1) &
    &                           + ABS ( ( pu(ji,jj  ) - pub_outer(ji,jj)   ) ) * umask(ji,jj,1) ) &
    &                           / ( umask(ji-1,jj,1) + umask(ji,jj,1) )

               zv_diff(ji,jj) = ( ABS ( ( pv(ji,jj-1)   - pvb_outer(ji,jj-1) ) ) * vmask(ji,jj-1,1) &
    &                           + ABS ( ( pv(ji,jj  ) - pvb_outer(ji,jj)     ) ) * vmask(ji,jj,1)   &
    &                           / ( vmask(ji,jj-1,1) + vmask(ji,jj,1) ) )
     
               pvel_diff(ji,jj) = 0.5_wp * ( zu_diff(ji,jj) + zv_diff(ji,jj) )
  
         END_2D    
         CALL lbc_lnk( 'icedyn_rhg_cvg_vp', pvel_diff,  'T',  1._wp )
Guillaume Samson's avatar
Guillaume Samson committed

      ELSE

         pvel_diff(:,:) = 0._wp

      ENDIF

      !---------------------------------------
      !
      ! ---  Mean residual & kinetic energy
      !
      !---------------------------------------

      IF ( kitinntot == 1 ) THEN

         zu_res_mean   = 0._wp
         zv_res_mean   = 0._wp
         zvel_res_mean = 0._wp
         zmke          = 0._wp

      ELSE

         ! * Mean residual (N/m2)
         ! Here we take the residual of the linear system (N/m2), 
         ! We define it as in mitgcm: global area-weighted mean of square-root residual
         ! Local residual r = Ax - B expresses to which extent the momentum balance is verified 
         ! i.e., how close we are to a solution
         
         DO_2D( 0, 0, 0, 0 ) !clem check bounds
Guillaume Samson's avatar
Guillaume Samson committed

               zu_res(ji,jj)  = ( prhsu(ji,jj) + pDU(ji,jj) * pu(ji,jj-1) + pEU(ji,jj) * pu(ji,jj+1)               &
                  &             - pAU(ji,jj) * pu(ji-1,jj) - pBU(ji,jj) * pu(ji,jj) - pCU(ji,jj) * pu(ji+1,jj) )
               zv_res(ji,jj)  = ( prhsv(ji,jj) + pDV(ji,jj) * pv(ji-1,jj) + pEV(ji,jj) * pv(ji+1,jj)               &
                  &             - pAV(ji,jj) * pv(ji,jj-1) - pBV(ji,jj) * pv(ji,jj) - pCV(ji,jj) * pv(ji,jj+1) )

!              zu_res(ji,jj)  = pFU(ji,jj) - pAU(ji,jj) * pu(ji-1,jj) - pBU(ji,jj) * pu(ji,jj) - pCU(ji,jj) * pu(ji+1,jj)
!              zv_res(ji,jj)  = pFV(ji,jj) - pAV(ji,jj) * pv(ji,jj-1) - pBV(ji,jj) * pv(ji,jj) - pCV(ji,jj) * pv(ji,jj+1)
   
               zu_res(ji,jj)  = SQRT( zu_res(ji,jj) * zu_res(ji,jj) ) * umask(ji,jj,1) * pat_iu(ji,jj) * e1e2u(ji,jj) * z1_pglob_area
               zv_res(ji,jj)  = SQRT( zv_res(ji,jj) * zv_res(ji,jj) ) * vmask(ji,jj,1) * pat_iv(ji,jj) * e1e2v(ji,jj) * z1_pglob_area
   
         END_2D
         
         ! Global ice-concentration, grid-cell-area weighted mean   
Guillaume Samson's avatar
Guillaume Samson committed
         zu_res_mean   = glob_sum( 'ice_rhg_vp', zu_res(:,:) )
         zv_res_mean   = glob_sum( 'ice_rhg_vp', zv_res(:,:) )
         zvel_res_mean = 0.5_wp * ( zu_res_mean + zv_res_mean )
   
         ! --- Global mean kinetic energy per unit area (J/m2)
         zvel2(:,:) = 0._wp

         DO_2D( 0, 0, 0, 0 ) !clem check bounds
                 
Guillaume Samson's avatar
Guillaume Samson committed
               zu     = 0.5_wp * ( pu(ji-1,jj) + pu(ji,jj) ) ! u-vel at T-point
               zv     = 0.5_wp * ( pv(ji,jj-1) + pv(ji,jj) )
               zvel2(ji,jj)  = zu*zu + zv*zv              ! square of ice velocity at T-point  

         END_2D
                   
         zmke = 0.5_wp * glob_sum( 'ice_rhg_vp', pmt(:,:) * e1e2t(:,:) * zvel2(:,:) ) / pglob_area
   
      ENDIF ! kitinntot

      !--- Spatially-resolved residual at last iteration to send back to main routine (last iteration only)
      !--- Calculation @T-point

      IF ( kitinntot == kitinntotmax) THEN

         DO_2D( nn_hls-1, nn_hls-1, nn_hls-1, nn_hls-1 )
Guillaume Samson's avatar
Guillaume Samson committed

               zu_res(ji,jj)  = ( prhsu(ji,jj) + pDU(ji,jj) * pu(ji,jj-1) + pEU(ji,jj) * pu(ji,jj+1)               &
                  &             - pAU(ji,jj) * pu(ji-1,jj) - pBU(ji,jj) * pu(ji,jj) - pCU(ji,jj) * pu(ji+1,jj) )
               zv_res(ji,jj)  = ( prhsv(ji,jj) + pDV(ji,jj) * pv(ji-1,jj) + pEV(ji,jj) * pv(ji+1,jj)               &
                  &             - pAV(ji,jj) * pv(ji,jj-1) - pBV(ji,jj) * pv(ji,jj) - pCV(ji,jj) * pv(ji,jj+1) )

               zu_res(ji,jj)  = SQRT( zu_res(ji,jj) * zu_res(ji,jj) ) * umask(ji,jj,1) 
               zv_res(ji,jj)  = SQRT( zv_res(ji,jj) * zv_res(ji,jj) ) * vmask(ji,jj,1) 

         END_2D
         
         IF( nn_hls == 1 )   CALL lbc_lnk( 'icedyn_rhg_cvg_vp', zu_res,  'U',  1., zv_res , 'V',  1. )
Guillaume Samson's avatar
Guillaume Samson committed

         DO_2D( 0, 0, 0, 0 ) !clem check bounds
        
Guillaume Samson's avatar
Guillaume Samson committed
               pvel_res(ji,jj) = 0.25_wp * ( zu_res(ji-1,jj) + zu_res(ji,jj) + zv_res(ji,jj-1) + zv_res(ji,jj) )
         
         END_2D
         CALL lbc_lnk( 'icedyn_rhg_cvg_vp', pvel_res, 'T', 1. )

      ELSE

         pvel_res(:,:) = 0._wp

      ENDIF
                  
      !                                                ! ==================== !

      it_inn_file =  ( kt - nit000 ) * kitinntotmax + kitinntot ! time step in the file
      it_out_file =  ( kt - nit000 ) * kitoutmax    + kitout

      ! write variables
      IF( lwm ) THEN

         istatus = NF90_PUT_VAR( ncvgid, nvarid_ures  , (/zu_res_mean/), (/it_inn_file/), (/1/) )        ! Residuals of the linear system, area weighted mean
         istatus = NF90_PUT_VAR( ncvgid, nvarid_vres  , (/zv_res_mean/), (/it_inn_file/), (/1/) )        !
         istatus = NF90_PUT_VAR( ncvgid, nvarid_velres, (/zvel_res_mean/), (/it_inn_file/), (/1/) )      !

         istatus = NF90_PUT_VAR( ncvgid, nvarid_uerr_max  , (/puerr_max/), (/it_inn_file/), (/1/) )      ! Max velocit_inn_filey error, sub-it_inn_fileerates
         istatus = NF90_PUT_VAR( ncvgid, nvarid_verr_max  , (/pverr_max/), (/it_inn_file/), (/1/) )      ! 
         istatus = NF90_PUT_VAR( ncvgid, nvarid_velerr_max, (/zvel_err_max/), (/it_inn_file/), (/1/) )   ! 

         istatus = NF90_PUT_VAR( ncvgid, nvarid_umad    , (/zu_mad/)  , (/it_inn_file/), (/1/) )         ! velocit_inn_filey MAD, area/sic-weighted, sub-it_inn_fileerates
         istatus = NF90_PUT_VAR( ncvgid, nvarid_vmad    , (/zv_mad/)  , (/it_inn_file/), (/1/) )         ! 
         istatus = NF90_PUT_VAR( ncvgid, nvarid_velmad  , (/zvel_mad/), (/it_inn_file/), (/1/) )         ! 

         istatus = NF90_PUT_VAR( ncvgid, nvarid_mke, (/zmke/), (/kitinntot/), (/1/) )                    ! mean kinetic energy

         IF ( kitinn == kitinnmax ) THEN ! only print outer mad at the end of inner loop

            istatus = NF90_PUT_VAR( ncvgid, nvarid_umad_outer    , (/zu_mad_outer/)  , (/it_out_file/), (/1/) )   ! velocity MAD, area/sic-weighted, outer-iterates
            istatus = NF90_PUT_VAR( ncvgid, nvarid_vmad_outer    , (/zv_mad_outer/)  , (/it_out_file/), (/1/) )   !
            istatus = NF90_PUT_VAR( ncvgid, nvarid_velmad_outer  , (/zvel_mad_outer/), (/it_out_file/), (/1/) )   !

         ENDIF

         IF( kt == nitend - nn_fsbc + 1 .AND. kitinntot == kitinntotmax )    istatus = NF90_CLOSE( ncvgid )
      ENDIF
      
   END SUBROUTINE rhg_cvg_vp
   

   
#else
   !!----------------------------------------------------------------------
   !!   Default option         Empty module           NO SI3 sea-ice model
   !!----------------------------------------------------------------------
#endif

   !!==============================================================================
END MODULE icedyn_rhg_vp