Skip to content
Snippets Groups Projects
icblbc.F90 36 KiB
Newer Older
Guillaume Samson's avatar
Guillaume Samson committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828
MODULE icblbc
   !!======================================================================
   !!                       ***  MODULE  icblbc  ***
   !! Ocean physics:  routines to handle boundary exchanges for icebergs
   !!======================================================================
   !! History :  3.3  !  2010-01  (Martin&Adcroft) Original code
   !!             -   !  2011-03  (Madec)          Part conversion to NEMO form
   !!             -   !                            Removal of mapping from another grid
   !!             -   !  2011-04  (Alderson)       Split into separate modules
   !!             -   !  2011-05  (Alderson)       MPP exchanges written based on lib_mpp
   !!             -   !  2011-05  (Alderson)       MPP and single processor boundary conditions added
   !!----------------------------------------------------------------------

   !!----------------------------------------------------------------------
   !!   icb_lbc       : -  Pass icebergs across cyclic boundaries
   !!   icb_lbc_mpp   : -  In MPP pass icebergs from linked list between processors
   !!                      as they advect around
   !!                   -  Lagrangian processes cannot be handled by existing NEMO MPP
   !!                      routines because they do not lie on regular jpi,jpj grids
   !!                   -  Processor exchanges are handled as in lib_mpp whenever icebergs step 
   !!                      across boundary of interior domain (nicbdi-nicbei, nicbdj-nicbej)
   !!                      so that iceberg does not exist in more than one processor
   !!                   -  North fold exchanges controlled by three arrays:
   !!                         nicbflddest - unique processor numbers that current one exchanges with
   !!                         nicbfldproc - processor number that current grid point exchanges with
   !!                         nicbfldpts  - packed i,j point in exchanging processor
   !!----------------------------------------------------------------------
   USE par_oce                             ! ocean parameters
   USE dom_oce                             ! ocean domain
   USE in_out_manager                      ! IO parameters
   USE lib_mpp                             ! MPI code and lk_mpp in particular
   USE icb_oce                             ! define iceberg arrays
   USE icbutl                              ! iceberg utility routines

   IMPLICIT NONE
   PRIVATE

#if ! defined key_mpi_off

!$AGRIF_DO_NOT_TREAT
   INCLUDE 'mpif.h'
!$AGRIF_END_DO_NOT_TREAT

   TYPE, PUBLIC :: buffer
      INTEGER :: size = 0
      REAL(wp), DIMENSION(:,:), POINTER ::   data
   END TYPE buffer

   TYPE(buffer), POINTER       ::   obuffer_n=>NULL() , ibuffer_n=>NULL()
   TYPE(buffer), POINTER       ::   obuffer_s=>NULL() , ibuffer_s=>NULL()
   TYPE(buffer), POINTER       ::   obuffer_e=>NULL() , ibuffer_e=>NULL()
   TYPE(buffer), POINTER       ::   obuffer_w=>NULL() , ibuffer_w=>NULL()

   ! north fold exchange buffers
   TYPE(buffer), POINTER       ::   obuffer_f=>NULL() , ibuffer_f=>NULL()

   INTEGER, PARAMETER, PRIVATE ::   jp_delta_buf = 25             ! Size by which to increment buffers
   INTEGER, PARAMETER, PRIVATE ::   jp_buffer_width = 15+nkounts  ! items to store for each berg

#endif

   PUBLIC   icb_lbc
   PUBLIC   icb_lbc_mpp

   !! * Substitutions
#  include "do_loop_substitute.h90"
   !!----------------------------------------------------------------------
   !! NEMO/OCE 4.0 , NEMO Consortium (2018)
   !! $Id: icblbc.F90 15088 2021-07-06 13:03:34Z acc $
   !! Software governed by the CeCILL license (see ./LICENSE)
   !!----------------------------------------------------------------------
CONTAINS

   SUBROUTINE icb_lbc()
      !!----------------------------------------------------------------------
      !!                 ***  SUBROUTINE icb_lbc  ***
      !!
      !! ** Purpose :   in non-mpp case need to deal with cyclic conditions
      !!                including north-fold
      !!----------------------------------------------------------------------
      TYPE(iceberg), POINTER ::   this
      TYPE(point)  , POINTER ::   pt
      !!----------------------------------------------------------------------

      !! periodic east/west boundaries
      !! =============================

      IF( l_Iperio ) THEN

         this => first_berg
         DO WHILE( ASSOCIATED(this) )
            pt => this%current_point
            IF( pt%xi > REAL(mig(nicbei),wp) + 0.5_wp ) THEN
               pt%xi = ricb_right + MOD(pt%xi, 1._wp ) - 1._wp
            ELSE IF( pt%xi < REAL(mig(nicbdi),wp) - 0.5_wp ) THEN
               pt%xi = ricb_left + MOD(pt%xi, 1._wp )
            ENDIF
            this => this%next
         END DO
         !
      ENDIF

      !! north/south boundaries
      !! ======================
      IF( l_Jperio)      CALL ctl_stop(' north-south periodicity not implemented for icebergs')
      ! north fold
      IF( l_IdoNFold )   CALL icb_lbc_nfld()
      !
   END SUBROUTINE icb_lbc


   SUBROUTINE icb_lbc_nfld()
      !!----------------------------------------------------------------------
      !!                 ***  SUBROUTINE icb_lbc_nfld  ***
      !!
      !! ** Purpose :   single processor north fold exchange
      !!----------------------------------------------------------------------
      TYPE(iceberg), POINTER ::   this
      TYPE(point)  , POINTER ::   pt
      INTEGER                ::   iine, ijne, ipts
      INTEGER                ::   iiglo, ijglo
      !!----------------------------------------------------------------------
      !
      this => first_berg
      DO WHILE( ASSOCIATED(this) )
         pt => this%current_point
         ijne = INT( pt%yj + 0.5 )
         IF( pt%yj > REAL(mjg(nicbej),wp) + 0.5_wp ) THEN
            !
            iine = INT( pt%xi + 0.5 )
            ipts  = nicbfldpts (mi1(iine))
            !
            ! moving across the cut line means both position and
            ! velocity must change
            ijglo = INT( ipts/nicbpack )
            iiglo = ipts - nicbpack*ijglo
            pt%xi = iiglo - ( pt%xi - REAL(iine,wp) )
            pt%yj = ijglo - ( pt%yj - REAL(ijne,wp) )
            pt%uvel = -1._wp * pt%uvel
            pt%vvel = -1._wp * pt%vvel
         ENDIF
         this => this%next
      END DO
      !
   END SUBROUTINE icb_lbc_nfld

#if ! defined key_mpi_off
   !!----------------------------------------------------------------------
   !!            MPI massively parallel processing library
   !!----------------------------------------------------------------------

   SUBROUTINE icb_lbc_mpp()
      !!----------------------------------------------------------------------
      !!                 ***  SUBROUTINE icb_lbc_mpp  ***
      !!
      !! ** Purpose :   multi processor exchange
      !!
      !! ** Method  :   identify direction for exchange, pack into a buffer
      !!                which is basically a real array and delete from linked list
      !!                length of buffer is exchanged first with receiving processor
      !!                then buffer is sent if necessary
      !!----------------------------------------------------------------------
      TYPE(iceberg)         , POINTER     ::   tmpberg, this
      TYPE(point)           , POINTER     ::   pt
      INTEGER                             ::   ibergs_to_send_e, ibergs_to_send_w
      INTEGER                             ::   ibergs_to_send_n, ibergs_to_send_s
      INTEGER                             ::   ibergs_rcvd_from_e, ibergs_rcvd_from_w
      INTEGER                             ::   ibergs_rcvd_from_n, ibergs_rcvd_from_s
      INTEGER                             ::   i, ibergs_start, ibergs_end
      INTEGER                             ::   ipe_N, ipe_S, ipe_W, ipe_E
      REAL(wp), DIMENSION(2)              ::   zewbergs, zwebergs, znsbergs, zsnbergs
      INTEGER                             ::   iml_req1, iml_req2, iml_req3, iml_req4
      INTEGER                             ::   iml_req5, iml_req6, iml_req7, iml_req8, iml_err
      INTEGER, DIMENSION(MPI_STATUS_SIZE) ::   iml_stat

      ! set up indices of neighbouring processors
      ipe_N = -1
      ipe_S = -1
      ipe_W = -1
      ipe_E = -1
      IF( mpinei(jpwe) >= 0 ) ipe_W = mpinei(jpwe)
      IF( mpinei(jpea) >= 0 ) ipe_E = mpinei(jpea)
      IF( mpinei(jpso) >= 0 ) ipe_S = mpinei(jpso)
      IF( mpinei(jpno) >= 0 ) ipe_N = mpinei(jpno)
      !
      ! at northern line of processors with north fold handle bergs differently
      IF( l_IdoNFold )   ipe_N = -1

      ! if there's only one processor in x direction then don't let mpp try to handle periodicity
      IF( jpni == 1 ) THEN
         ipe_E = -1
         ipe_W = -1
      ENDIF

      IF( nn_verbose_level >= 2 ) THEN
         WRITE(numicb,*) 'processor west  : ', ipe_W
         WRITE(numicb,*) 'processor east  : ', ipe_E
         WRITE(numicb,*) 'processor north : ', ipe_N
         WRITE(numicb,*) 'processor south : ', ipe_S
         WRITE(numicb,*) 'processor nimpp : ', nimpp
         WRITE(numicb,*) 'processor njmpp : ', njmpp
         CALL flush( numicb )
      ENDIF

      ! periodicity is handled here when using mpp when there is more than one processor in
      ! the i direction, but it also has to happen when jpni=1 case so this is dealt with
      ! in icb_lbc and called here

      IF( jpni == 1 ) CALL icb_lbc()

      ! Note that xi is adjusted when swapping because of periodic condition

      IF( nn_verbose_level > 0 ) THEN
         ! store the number of icebergs on this processor at start
         ibergs_start = icb_utl_count()
      ENDIF

      ibergs_to_send_e   = 0
      ibergs_to_send_w   = 0
      ibergs_to_send_n   = 0
      ibergs_to_send_s   = 0
      ibergs_rcvd_from_e = 0
      ibergs_rcvd_from_w = 0
      ibergs_rcvd_from_n = 0
      ibergs_rcvd_from_s = 0

      IF( ASSOCIATED(first_berg) ) THEN      ! Find number of bergs that headed east/west
         this => first_berg
         DO WHILE (ASSOCIATED(this))
            pt => this%current_point
            IF( ipe_E >= 0 .AND. pt%xi > REAL(mig(nicbei),wp) + 0.5_wp - (nn_hls-1) ) THEN
               tmpberg => this
               this => this%next
               ibergs_to_send_e = ibergs_to_send_e + 1
               IF( nn_verbose_level >= 4 ) THEN
                  WRITE(numicb,*) 'bergstep ',nktberg,' packing berg ',tmpberg%number(:),' for transfer to east'
                  CALL flush( numicb )
               ENDIF
               ! deal with periodic case
               tmpberg%current_point%xi = ricb_right + MOD(tmpberg%current_point%xi, 1._wp ) - 1._wp
               ! now pack it into buffer and delete from list
               CALL icb_pack_into_buffer( tmpberg, obuffer_e, ibergs_to_send_e)
               CALL icb_utl_delete(first_berg, tmpberg)
            ELSE IF( ipe_W >= 0 .AND. pt%xi < REAL(mig(nicbdi),wp) - 0.5_wp - (nn_hls-1) ) THEN
               tmpberg => this
               this => this%next
               ibergs_to_send_w = ibergs_to_send_w + 1
               IF( nn_verbose_level >= 4 ) THEN
                  WRITE(numicb,*) 'bergstep ',nktberg,' packing berg ',tmpberg%number(:),' for transfer to west'
                  CALL flush( numicb )
               ENDIF
               ! deal with periodic case
               tmpberg%current_point%xi = ricb_left + MOD(tmpberg%current_point%xi, 1._wp )
               ! now pack it into buffer and delete from list
               CALL icb_pack_into_buffer( tmpberg, obuffer_w, ibergs_to_send_w)
               CALL icb_utl_delete(first_berg, tmpberg)
            ELSE
               this => this%next
            ENDIF
         END DO
      ENDIF
      IF( nn_verbose_level >= 3) THEN
         WRITE(numicb,*) 'bergstep ',nktberg,' send ew: ', ibergs_to_send_e, ibergs_to_send_w
         CALL flush(numicb)
      ENDIF

      ! send bergs east and receive bergs from west (ie ones that were sent east) and vice versa

      ! pattern here is copied from lib_mpp code

      IF( mpinei(jpwe) >= 0  )   zewbergs(1) = ibergs_to_send_w
      IF( mpinei(jpea) >= 0  )   zwebergs(1) = ibergs_to_send_e
      IF( mpinei(jpwe) >= 0  )   CALL mppsend( 11, zewbergs(1), 1, ipe_W, iml_req2)
      IF( mpinei(jpea) >= 0  )   CALL mppsend( 12, zwebergs(1), 1, ipe_E, iml_req3)
      IF( mpinei(jpea) >= 0  )   CALL mpprecv( 11, zewbergs(2), 1, ipe_E )
      IF( mpinei(jpwe) >= 0  )   CALL mpprecv( 12, zwebergs(2), 1, ipe_W )
      IF( mpinei(jpwe) >= 0  )   CALL mpi_wait( iml_req2, iml_stat, iml_err )
      IF( mpinei(jpea) >= 0  )   CALL mpi_wait( iml_req3, iml_stat, iml_err )
      IF( mpinei(jpea) >= 0  )   ibergs_rcvd_from_e = INT( zewbergs(2) )
      IF( mpinei(jpwe) >= 0  )   ibergs_rcvd_from_w = INT( zwebergs(2) )
      
      IF( nn_verbose_level >= 3) THEN
         WRITE(numicb,*) 'bergstep ',nktberg,' recv ew: ', ibergs_rcvd_from_w, ibergs_rcvd_from_e
         CALL flush(numicb)
      ENDIF
      
      IF( ibergs_to_send_w > 0 ) CALL mppsend( 13, obuffer_w%data, ibergs_to_send_w*jp_buffer_width, ipe_W, iml_req2 )
      IF( ibergs_to_send_e > 0 ) CALL mppsend( 14, obuffer_e%data, ibergs_to_send_e*jp_buffer_width, ipe_E, iml_req3 )
      IF( ibergs_rcvd_from_e > 0 ) THEN
         CALL icb_increase_ibuffer(ibuffer_e, ibergs_rcvd_from_e)
         CALL mpprecv( 13, ibuffer_e%data, ibergs_rcvd_from_e*jp_buffer_width )
      ENDIF
      IF( ibergs_rcvd_from_w > 0 ) THEN
         CALL icb_increase_ibuffer(ibuffer_w, ibergs_rcvd_from_w)
         CALL mpprecv( 14, ibuffer_w%data, ibergs_rcvd_from_w*jp_buffer_width )
      ENDIF
      IF( ibergs_to_send_w > 0 ) CALL mpi_wait( iml_req2, iml_stat, iml_err )
      IF( ibergs_to_send_e > 0 ) CALL mpi_wait( iml_req3, iml_stat, iml_err )
      DO i = 1, ibergs_rcvd_from_e
         IF( nn_verbose_level >= 4 ) THEN
            WRITE(numicb,*) 'bergstep ',nktberg,' unpacking berg ',INT(ibuffer_e%data(16,i)),' from east'
            CALL FLUSH( numicb )
         ENDIF
         CALL icb_unpack_from_buffer(first_berg, ibuffer_e, i)
      END DO
      DO i = 1, ibergs_rcvd_from_w
         IF( nn_verbose_level >= 4 ) THEN
            WRITE(numicb,*) 'bergstep ',nktberg,' unpacking berg ',INT(ibuffer_w%data(16,i)),' from west'
            CALL FLUSH( numicb )
         ENDIF
         CALL icb_unpack_from_buffer(first_berg, ibuffer_w, i)
      END DO

      ! Find number of bergs that headed north/south
      ! (note: this block should technically go ahead of the E/W recv block above
      !  to handle arbitrary orientation of PEs. But for simplicity, it is
      !  here to accomodate diagonal transfer of bergs between PEs -AJA)

      IF( ASSOCIATED(first_berg) ) THEN
         this => first_berg
         DO WHILE (ASSOCIATED(this))
            pt => this%current_point
            IF( ipe_N >= 0 .AND. pt%yj > REAL(mjg(nicbej),wp) + 0.5_wp - (nn_hls-1) ) THEN
               tmpberg => this
               this => this%next
               ibergs_to_send_n = ibergs_to_send_n + 1
               IF( nn_verbose_level >= 4 ) THEN
                  WRITE(numicb,*) 'bergstep ',nktberg,' packing berg ',tmpberg%number(:),' for transfer to north'
                  CALL flush( numicb )
               ENDIF
               CALL icb_pack_into_buffer( tmpberg, obuffer_n, ibergs_to_send_n)
               CALL icb_utl_delete(first_berg, tmpberg)
            ELSE IF( ipe_S >= 0 .AND. pt%yj < REAL(mjg(nicbdj),wp) - 0.5_wp - (nn_hls-1) ) THEN
               tmpberg => this
               this => this%next
               ibergs_to_send_s = ibergs_to_send_s + 1
               IF( nn_verbose_level >= 4 ) THEN
                  WRITE(numicb,*) 'bergstep ',nktberg,' packing berg ',tmpberg%number(:),' for transfer to south'
                  CALL flush( numicb )
               ENDIF
               CALL icb_pack_into_buffer( tmpberg, obuffer_s, ibergs_to_send_s)
               CALL icb_utl_delete(first_berg, tmpberg)
            ELSE
               this => this%next
            ENDIF
         END DO
      ENDIF
      if( nn_verbose_level >= 3) then
         write(numicb,*) 'bergstep ',nktberg,' send ns: ', ibergs_to_send_n, ibergs_to_send_s
         call flush(numicb)
      endif

      ! send bergs north
      ! and receive bergs from south (ie ones sent north)
      
      IF( mpinei(jpso) >= 0  )   znsbergs(1) = ibergs_to_send_s
      IF( mpinei(jpno) >= 0  )   zsnbergs(1) = ibergs_to_send_n
      IF( mpinei(jpso) >= 0  )   CALL mppsend( 15, znsbergs(1), 1, ipe_S, iml_req2)
      IF( mpinei(jpno) >= 0  )   CALL mppsend( 16, zsnbergs(1), 1, ipe_N, iml_req3)
      IF( mpinei(jpno) >= 0  )   CALL mpprecv( 15, znsbergs(2), 1, ipe_N )
      IF( mpinei(jpso) >= 0  )   CALL mpprecv( 16, zsnbergs(2), 1, ipe_S )
      IF( mpinei(jpso) >= 0  )   CALL mpi_wait( iml_req2, iml_stat, iml_err )
      IF( mpinei(jpno) >= 0  )   CALL mpi_wait( iml_req3, iml_stat, iml_err )
      IF( mpinei(jpno) >= 0  )   ibergs_rcvd_from_n = INT( znsbergs(2) )
      IF( mpinei(jpso) >= 0  )   ibergs_rcvd_from_s = INT( zsnbergs(2) )
      
      IF( nn_verbose_level >= 3) THEN
         WRITE(numicb,*) 'bergstep ',nktberg,' recv ns: ', ibergs_rcvd_from_s, ibergs_rcvd_from_n
         CALL FLUSH(numicb)
      ENDIF

      IF( ibergs_to_send_s > 0 ) CALL mppsend( 17, obuffer_s%data, ibergs_to_send_s*jp_buffer_width, ipe_S, iml_req2 )
      IF( ibergs_to_send_n > 0 ) CALL mppsend( 18, obuffer_n%data, ibergs_to_send_n*jp_buffer_width, ipe_N, iml_req3 )
      IF( ibergs_rcvd_from_n > 0 ) THEN
         CALL icb_increase_ibuffer(ibuffer_n, ibergs_rcvd_from_n)
         CALL mpprecv( 17, ibuffer_n%data, ibergs_rcvd_from_n*jp_buffer_width )
      ENDIF
      IF( ibergs_rcvd_from_s > 0 ) THEN
         CALL icb_increase_ibuffer(ibuffer_s, ibergs_rcvd_from_s)
         CALL mpprecv( 18, ibuffer_s%data, ibergs_rcvd_from_s*jp_buffer_width )
      ENDIF
      IF( ibergs_to_send_s > 0 ) CALL mpi_wait( iml_req2, iml_stat, iml_err )
      IF( ibergs_to_send_n > 0 ) CALL mpi_wait( iml_req3, iml_stat, iml_err )
      DO i = 1, ibergs_rcvd_from_n
         IF( nn_verbose_level >= 4 ) THEN
            WRITE(numicb,*) 'bergstep ',nktberg,' unpacking berg ',INT(ibuffer_n%data(16,i)),' from north'
            CALL FLUSH( numicb )
         ENDIF
         CALL icb_unpack_from_buffer(first_berg, ibuffer_n, i)
      END DO
      DO i = 1, ibergs_rcvd_from_s
         IF( nn_verbose_level >= 4 ) THEN
            WRITE(numicb,*) 'bergstep ',nktberg,' unpacking berg ',INT(ibuffer_s%data(16,i)),' from south'
            CALL FLUSH( numicb )
         ENDIF
         CALL icb_unpack_from_buffer(first_berg, ibuffer_s, i)
      END DO
      
      IF( nn_verbose_level > 0 ) THEN
         ! compare the number of icebergs on this processor from the start to the end
         ibergs_end = icb_utl_count()
         i = ( ibergs_rcvd_from_n + ibergs_rcvd_from_s + ibergs_rcvd_from_e + ibergs_rcvd_from_w ) - &
             ( ibergs_to_send_n + ibergs_to_send_s + ibergs_to_send_e + ibergs_to_send_w )
         IF( ibergs_end-(ibergs_start+i) .NE. 0 ) THEN
            WRITE( numicb,*   ) 'send_bergs_to_other_pes: net change in number of icebergs'
            WRITE( numicb,1000) 'send_bergs_to_other_pes: ibergs_end=', &
                                ibergs_end,' on PE',narea
            WRITE( numicb,1000) 'send_bergs_to_other_pes: ibergs_start=', &
                                ibergs_start,' on PE',narea
            WRITE( numicb,1000) 'send_bergs_to_other_pes: delta=', &
                                i,' on PE',narea
            WRITE( numicb,1000) 'send_bergs_to_other_pes: error=', &
                                ibergs_end-(ibergs_start+i),' on PE',narea
            WRITE( numicb,1000) 'send_bergs_to_other_pes: ibergs_to_send_n=', &
                                ibergs_to_send_n,' on PE',narea
            WRITE( numicb,1000) 'send_bergs_to_other_pes: ibergs_to_send_s=', &
                                ibergs_to_send_s,' on PE',narea
            WRITE( numicb,1000) 'send_bergs_to_other_pes: ibergs_to_send_e=', &
                                ibergs_to_send_e,' on PE',narea
            WRITE( numicb,1000) 'send_bergs_to_other_pes: ibergs_to_send_w=', &
                                ibergs_to_send_w,' on PE',narea
            WRITE( numicb,1000) 'send_bergs_to_other_pes: ibergs_rcvd_from_n=', &
                                ibergs_rcvd_from_n,' on PE',narea
            WRITE( numicb,1000) 'send_bergs_to_other_pes: ibergs_rcvd_from_s=', &
                                ibergs_rcvd_from_s,' on PE',narea
            WRITE( numicb,1000) 'send_bergs_to_other_pes: ibergs_rcvd_from_e=', &
                                ibergs_rcvd_from_e,' on PE',narea
            WRITE( numicb,1000) 'send_bergs_to_other_pes: ibergs_rcvd_from_w=', &
                                ibergs_rcvd_from_w,' on PE',narea
  1000      FORMAT(a,i5,a,i4)
            CALL ctl_stop('send_bergs_to_other_pes: lost or gained an iceberg or two')
         ENDIF
      ENDIF

      ! deal with north fold if we necessary when there is more than one top row processor
      ! note that for jpni=1 north fold has been dealt with above in call to icb_lbc
      IF( l_IdoNFold .AND. jpni > 1 ) CALL icb_lbc_mpp_nfld( )

      IF( nn_verbose_level > 0 ) THEN
         i = 0
         this => first_berg
         DO WHILE (ASSOCIATED(this))
            pt => this%current_point
            IF( pt%xi < REAL(mig(nicbdi),wp) - 0.5_wp - (nn_hls-1) .OR. &
                pt%xi > REAL(mig(nicbei),wp) + 0.5_wp - (nn_hls-1) .OR. &
                pt%yj < REAL(mjg(nicbdj),wp) - 0.5_wp - (nn_hls-1) .OR. &
                pt%yj > REAL(mjg(nicbej),wp) + 0.5_wp - (nn_hls-1) ) THEN
               i = i + 1
               WRITE(numicb,*) 'berg lost in halo: ', this%number(:)
               WRITE(numicb,*) '                   ', nimpp, njmpp
               WRITE(numicb,*) '                   ', nicbdi, nicbei, nicbdj, nicbej
               CALL flush( numicb )
            ENDIF
            this => this%next
         ENDDO ! WHILE
         CALL mpp_sum('icblbc', i)
         IF( i .GT. 0 ) THEN
            WRITE( numicb,'(a,i4)') 'send_bergs_to_other_pes: # of bergs outside computational domain = ',i
            CALL ctl_stop('send_bergs_to_other_pes:  there are bergs still in halos!')
         ENDIF ! root_pe
      ENDIF ! debug
      !
      CALL mppsync()
      !
   END SUBROUTINE icb_lbc_mpp


   SUBROUTINE icb_lbc_mpp_nfld()
      !!----------------------------------------------------------------------
      !!                 ***  SUBROUTINE icb_lbc_mpp_nfld  ***
      !!
      !! ** Purpose :   north fold treatment in multi processor exchange
      !!
      !! ** Method  :   
      !!----------------------------------------------------------------------
      TYPE(iceberg)         , POINTER     :: tmpberg, this
      TYPE(point)           , POINTER     :: pt
      INTEGER                             :: ibergs_to_send
      INTEGER                             :: ibergs_to_rcv
      INTEGER                             :: iiglo, ijglo, jk, jn
      INTEGER                             :: ifldproc, iproc, ipts
      INTEGER                             :: iine, ijne
      INTEGER                             :: jjn
      REAL(wp), DIMENSION(0:3)            :: zsbergs, znbergs
      INTEGER                             :: iml_req1, iml_req2, iml_err
      INTEGER, DIMENSION(MPI_STATUS_SIZE) :: iml_stat

      ! set up indices of neighbouring processors

      ! nicbfldproc is a list of unique processor numbers that this processor
      ! exchanges with (including itself), so we loop over this array; since
      ! its of fixed size, the first -1 marks end of list of processors
      !
      nicbfldnsend(:) = 0
      nicbfldexpect(:) = 0
      nicbfldreq(:) = 0
      !
      ! Since each processor may be communicating with more than one northern
      ! neighbour, cycle through the sends so that the receive order can be
      ! controlled.
      !
      ! First compute how many icebergs each active neighbour should expect
      DO jn = 1, jpni
         IF( nicbfldproc(jn) /= -1 ) THEN
            ifldproc = nicbfldproc(jn)
            nicbfldnsend(jn) = 0

            ! Find number of bergs that need to be exchanged
            ! Pick out exchanges with processor ifldproc
            ! if ifldproc is this processor then don't send
            !
            IF( ASSOCIATED(first_berg) ) THEN
               this => first_berg
               DO WHILE (ASSOCIATED(this))
                  pt => this%current_point
                  iine = INT( pt%xi + 0.5 ) + (nn_hls-1)
                  iproc = nicbflddest(mi1(iine))
                  IF( pt%yj > REAL(mjg(nicbej),wp) + 0.5_wp - (nn_hls-1) ) THEN
                     IF( iproc == ifldproc ) THEN
                        !
                        IF( iproc /= narea ) THEN
                           tmpberg => this
                           nicbfldnsend(jn) = nicbfldnsend(jn) + 1
                        ENDIF
                        !
                     ENDIF
                  ENDIF
                  this => this%next
               END DO
            ENDIF
            !
         ENDIF
         !
      END DO
      !
      ! Now tell each active neighbour how many icebergs to expect
      DO jn = 1, jpni
         IF( nicbfldproc(jn) /= -1 ) THEN
            ifldproc = nicbfldproc(jn)
            IF( ifldproc == narea ) CYCLE
   
            zsbergs(0) = narea
            zsbergs(1) = nicbfldnsend(jn)
            !IF ( nicbfldnsend(jn) .GT. 0 .AND. nn_verbose_level > 0 ) write(numicb,*) 'ICB sending ',nicbfldnsend(jn),' to ', ifldproc
            CALL mppsend( 21, zsbergs(0:1), 2, ifldproc-1, nicbfldreq(jn))
         ENDIF
         !
      END DO
      !
      ! and receive the heads-up from active neighbours preparing to send
      DO jn = 1, jpni
         IF( nicbfldproc(jn) /= -1 ) THEN
            ifldproc = nicbfldproc(jn)
            IF( ifldproc == narea ) CYCLE

            CALL mpprecv( 21, znbergs(1:2), 2 )
            DO jjn = 1,jpni
             IF( nicbfldproc(jjn) .eq. INT(znbergs(1)) ) EXIT
            END DO
            IF( jjn .GT. jpni .AND. nn_verbose_level > 0 ) write(numicb,*) 'ICB ERROR'
            nicbfldexpect(jjn) = INT( znbergs(2) )
            !IF ( nicbfldexpect(jjn) .GT. 0 .AND. nn_verbose_level > 0 ) write(numicb,*) 'ICB expecting ',nicbfldexpect(jjn),' from ', nicbfldproc(jjn)
            !IF (nn_verbose_level > 0) CALL FLUSH(numicb)
         ENDIF
         !
      END DO
      !
      ! post the mpi waits if using immediate send protocol
      DO jn = 1, jpni
         IF( nicbfldproc(jn) /= -1 ) THEN
            ifldproc = nicbfldproc(jn)
            IF( ifldproc == narea ) CYCLE
            CALL mpi_wait( nicbfldreq(jn), iml_stat, iml_err )
         ENDIF
         !
      END DO
   
         !
         ! Cycle through the icebergs again, this time packing and sending any
         ! going through the north fold. They will be expected.
      DO jn = 1, jpni
         IF( nicbfldproc(jn) /= -1 ) THEN
            ifldproc = nicbfldproc(jn)
            ibergs_to_send = 0
   
            ! Find number of bergs that need to be exchanged
            ! Pick out exchanges with processor ifldproc
            ! if ifldproc is this processor then don't send
            !
            IF( ASSOCIATED(first_berg) ) THEN
               this => first_berg
               DO WHILE (ASSOCIATED(this))
                  pt => this%current_point
                  iine = INT( pt%xi + 0.5 ) + (nn_hls-1)
                  ijne = INT( pt%yj + 0.5 ) + (nn_hls-1)
                  ipts  = nicbfldpts (mi1(iine))
                  iproc = nicbflddest(mi1(iine))
                  IF( pt%yj > REAL(mjg(nicbej),wp) + 0.5_wp - (nn_hls-1) ) THEN
                     IF( iproc == ifldproc ) THEN
                        !
                        ! moving across the cut line means both position and
                        ! velocity must change
                        ijglo = INT( ipts/nicbpack )
                        iiglo = ipts - nicbpack*ijglo
                        pt%xi = iiglo - ( pt%xi - REAL(iine,wp) )
                        pt%yj = ijglo - ( pt%yj - REAL(ijne,wp) )
                        pt%uvel = -1._wp * pt%uvel
                        pt%vvel = -1._wp * pt%vvel
                        !
                        ! now remove berg from list and pack it into a buffer
                        IF( iproc /= narea ) THEN
                           tmpberg => this
                           ibergs_to_send = ibergs_to_send + 1
                           IF( nn_verbose_level >= 4 ) THEN
                              WRITE(numicb,*) 'bergstep ',nktberg,' packing berg ',tmpberg%number(:),' for north fold'
                              CALL flush( numicb )
                           ENDIF
                           CALL icb_pack_into_buffer( tmpberg, obuffer_f, ibergs_to_send)
                           CALL icb_utl_delete(first_berg, tmpberg)
                        ENDIF
                        !
                     ENDIF
                  ENDIF
                  this => this%next
               END DO
            ENDIF
            if( nn_verbose_level >= 3) then
               write(numicb,*) 'bergstep ',nktberg,' send nfld: ', ibergs_to_send
               call flush(numicb)
            endif
            !
            ! if we're in this processor, then we've done everything we need to
            ! so go on to next element of loop
            IF( ifldproc == narea ) CYCLE
   
            ! send bergs
   
            IF( ibergs_to_send > 0 )  &
                CALL mppsend( 12, obuffer_f%data, ibergs_to_send*jp_buffer_width, ifldproc-1, nicbfldreq(jn) )
            !
         ENDIF
         !
      END DO
      !
      ! Now receive the expected number of bergs from the active neighbours
      DO jn = 1, jpni
         IF( nicbfldproc(jn) /= -1 ) THEN
            ifldproc = nicbfldproc(jn)
            IF( ifldproc == narea ) CYCLE
            ibergs_to_rcv = nicbfldexpect(jn)

            IF( ibergs_to_rcv  > 0 ) THEN
               CALL icb_increase_ibuffer(ibuffer_f, ibergs_to_rcv)
               CALL mpprecv( 12, ibuffer_f%data, ibergs_to_rcv*jp_buffer_width, ifldproc-1 )
            ENDIF
            !
            DO jk = 1, ibergs_to_rcv
               IF( nn_verbose_level >= 4 ) THEN
                  WRITE(numicb,*) 'bergstep ',nktberg,' unpacking berg ',INT(ibuffer_f%data(16,jk)),' from north fold'
                  CALL flush( numicb )
               ENDIF
               CALL icb_unpack_from_buffer(first_berg, ibuffer_f, jk )
            END DO
         ENDIF
         !
      END DO
      !
      ! Finally post the mpi waits if using immediate send protocol
      DO jn = 1, jpni
         IF( nicbfldproc(jn) /= -1 ) THEN
            ifldproc = nicbfldproc(jn)
            IF( ifldproc == narea ) CYCLE
            CALL mpi_wait( nicbfldreq(jn), iml_stat, iml_err )
         ENDIF
         !
      END DO
      !
   END SUBROUTINE icb_lbc_mpp_nfld


   SUBROUTINE icb_pack_into_buffer( berg, pbuff, kb )
      !!----------------------------------------------------------------------
      !!----------------------------------------------------------------------
      TYPE(iceberg), POINTER :: berg
      TYPE(buffer) , POINTER :: pbuff
      INTEGER               , INTENT(in) :: kb
      ! 
      INTEGER ::   k   ! local integer
      !!----------------------------------------------------------------------
      !
      IF( .NOT. ASSOCIATED(pbuff) ) CALL icb_increase_buffer( pbuff, jp_delta_buf )
      IF( kb .GT. pbuff%size ) CALL icb_increase_buffer( pbuff, jp_delta_buf )

      !! pack points into buffer

      pbuff%data( 1,kb) = berg%current_point%lon
      pbuff%data( 2,kb) = berg%current_point%lat
      pbuff%data( 3,kb) = berg%current_point%uvel
      pbuff%data( 4,kb) = berg%current_point%vvel
      pbuff%data( 5,kb) = berg%current_point%xi
      pbuff%data( 6,kb) = berg%current_point%yj
      pbuff%data( 7,kb) = float(berg%current_point%year)
      pbuff%data( 8,kb) = berg%current_point%day
      pbuff%data( 9,kb) = berg%current_point%mass
      pbuff%data(10,kb) = berg%current_point%thickness
      pbuff%data(11,kb) = berg%current_point%width
      pbuff%data(12,kb) = berg%current_point%length
      pbuff%data(13,kb) = berg%current_point%mass_of_bits
      pbuff%data(14,kb) = berg%current_point%heat_density

      pbuff%data(15,kb) = berg%mass_scaling
      DO k=1,nkounts
         pbuff%data(15+k,kb) = REAL( berg%number(k), wp )
      END DO
      !
   END SUBROUTINE icb_pack_into_buffer


   SUBROUTINE icb_unpack_from_buffer(first, pbuff, kb)
      !!----------------------------------------------------------------------
      !!----------------------------------------------------------------------
      TYPE(iceberg),             POINTER :: first
      TYPE(buffer) ,             POINTER :: pbuff
      INTEGER      , INTENT(in)          :: kb
      ! 
      TYPE(iceberg)                      :: currentberg
      TYPE(point)                        :: pt
      INTEGER                            :: ik
      !!----------------------------------------------------------------------
      !
      pt%lon            =      pbuff%data( 1,kb)
      pt%lat            =      pbuff%data( 2,kb)
      pt%uvel           =      pbuff%data( 3,kb)
      pt%vvel           =      pbuff%data( 4,kb)
      pt%xi             =      pbuff%data( 5,kb)
      pt%yj             =      pbuff%data( 6,kb)
      pt%year           = INT( pbuff%data( 7,kb) )
      pt%day            =      pbuff%data( 8,kb)
      pt%mass           =      pbuff%data( 9,kb)
      pt%thickness      =      pbuff%data(10,kb)
      pt%width          =      pbuff%data(11,kb)
      pt%length         =      pbuff%data(12,kb)
      pt%mass_of_bits   =      pbuff%data(13,kb)
      pt%heat_density   =      pbuff%data(14,kb)

      currentberg%mass_scaling =      pbuff%data(15,kb)
      DO ik = 1, nkounts
         currentberg%number(ik) = INT( pbuff%data(15+ik,kb) )
      END DO
      !
      CALL icb_utl_add(currentberg, pt )
      !
   END SUBROUTINE icb_unpack_from_buffer


   SUBROUTINE icb_increase_buffer(old,kdelta)
      !!----------------------------------------------------------------------
      TYPE(buffer), POINTER    :: old
      INTEGER     , INTENT(in) :: kdelta
      ! 
      TYPE(buffer), POINTER ::   new
      INTEGER ::   inew_size
      !!----------------------------------------------------------------------
      !
      IF( .NOT. ASSOCIATED(old) ) THEN   ;   inew_size = kdelta
      ELSE                               ;   inew_size = old%size + kdelta
      ENDIF
      ALLOCATE( new )
      ALLOCATE( new%data( jp_buffer_width, inew_size) )
      new%size = inew_size
      IF( ASSOCIATED(old) ) THEN
         new%data(:,1:old%size) = old%data(:,1:old%size)
         DEALLOCATE(old%data)
         DEALLOCATE(old)
      ENDIF
      old => new
      !
   END SUBROUTINE icb_increase_buffer


   SUBROUTINE icb_increase_ibuffer(old,kdelta)
      !!----------------------------------------------------------------------
      !!----------------------------------------------------------------------
      TYPE(buffer),            POINTER :: old
      INTEGER     , INTENT(in)         :: kdelta
      !
      TYPE(buffer),            POINTER :: new
      INTEGER                          :: inew_size, iold_size
      !!----------------------------------------------------------------------

      IF( .NOT. ASSOCIATED(old) ) THEN
         inew_size = kdelta + jp_delta_buf
         iold_size = 0
      ELSE
         iold_size = old%size
         IF( kdelta .LT. old%size ) THEN
            inew_size = old%size + kdelta
         ELSE
            inew_size = kdelta + jp_delta_buf
         ENDIF
      ENDIF

      IF( iold_size .NE. inew_size ) THEN
         ALLOCATE( new )
         ALLOCATE( new%data( jp_buffer_width, inew_size) )
         new%size = inew_size
         IF( ASSOCIATED(old) ) THEN
            new%data(:,1:old%size) = old%data(:,1:old%size)
            DEALLOCATE(old%data)
            DEALLOCATE(old)
         ENDIF
         old => new
         !IF (nn_verbose_level > 0) WRITE( numicb,*) 'icb_increase_ibuffer',narea,' increased to',inew_size
      ENDIF
      !
   END SUBROUTINE icb_increase_ibuffer

#else
   !!----------------------------------------------------------------------
   !!   Default case:            Dummy module        share memory computing
   !!----------------------------------------------------------------------
   SUBROUTINE icb_lbc_mpp()
      WRITE(numout,*) 'icb_lbc_mpp: You should not have seen this message!!'
   END SUBROUTINE icb_lbc_mpp
#endif

   !!======================================================================
END MODULE icblbc