Skip to content

Integer divide-by-zero crash when setting distribution type to anything other than cartesian/rake #114

Description

@manodeep

Setting the distribution type to sectrobin crashes with an integer-divide-by-zero error for a ESM 1.6 PiControl (i.e., access driver)

  CICE (cice_init) 1    jobnum =            3
  CICE (cice_init) 1 init_date =        10101
  CICE (cice_init) 1   runtime =     31622400
  CICE (cice_init) 1     idate =            0           0
forrtl: severe (71): integer divide by zero
Image              PC                Routine            Line        Source             
libpthread-2.28.s  000014F6AFB65990  Unknown               Unknown  Unknown
cice_access-esm1.  000000000048C337  init_cpl                  318  cpl_interface.F90
cice_access-esm1.  0000000000413531  cice_init                 131  CICE_InitMod.F90
cice_access-esm1.  0000000000412D40  icemodel                   51  CICE.F90

The relevant code is here

  l_ilo=mod(my_task,nprocsX)*nx_global/nprocsX+1
  l_ihi=l_ilo + nx_global/nprocsX -1
  l_jlo=int(my_task/nprocsX) * ny_global/nprocsY+1
  l_jhi=l_jlo+ny_global/nprocsY - 1

@anton-seaice showed that nprocsX, nprocsY are set to public but only ever set in the Cartesian distribution types. Hence, those public vars retain the default zero and get this divide-by-zero at runtime for all non-Cartesian distribution types. I have also confirmed that the same crash exists with the released config, by running an experiment with only the distribution type changed sectrobin and getting an identical crash.

Claude suggested the following patch for the access driver:

From 99db6f0502394461f9599ea3236c3fc639d01a44 Mon Sep 17 00:00:00 2001
From: Claude <noreply@anthropic.com>
Date: Wed, 19 Aug 2026 03:39:37 +0000
Subject: [PATCH] Fix divide-by-zero in the ESM coupler for non-cartesian
 distributions

init_cpl derived its OASIS BOX partition from ice_distribution's public
nprocsX/nprocsY.  Those are only ever assigned by create_distrb_cart, so
with distribution_type = 'sectrobin', 'roundrobin', 'sectcart' or
'spacecurve' they were still zero and the model died with an integer
divide by zero at startup.  Only 'cartesian' (and 'rake', which builds on
it) happened to work.

The loop immediately above already computes the task's global index range
from the block distribution, and init_cpl requires max_blocks == 1, so
that range is exactly the rectangle this task owns for every distribution
type.  Use it, and drop the overwrite.  It is also more correct where the
two disagree: the old formula assumed the domain divided evenly by the
processor grid.

nprocsX/nprocsY were made public solely for this call site, so they go
back to being local to create_distrb_cart.

Pre-existing bug; not introduced by the run-time domain sizing series.
---
 drivers/access/cpl_interface.F90 | 21 ++++++++++++++-------
 source/ice_distribution.F90      | 16 ++++++++--------
 2 files changed, 22 insertions(+), 15 deletions(-)

diff --git a/drivers/access/cpl_interface.F90 b/drivers/access/cpl_interface.F90
index 0308db7..a978a42 100644
--- a/drivers/access/cpl_interface.F90
+++ b/drivers/access/cpl_interface.F90
@@ -14,7 +14,7 @@
   use ice_broadcast
   use ice_blocks       !, only : nx_block, ny_block, nghost
   use ice_domain_size  !, only : max_blocks, nx_global, ny_global, ncat
-  use ice_distribution, only : distrb, nprocsX, nprocsY
+  use ice_distribution, only : distrb
   use ice_gather_scatter
   use ice_constants
   use ice_boundary, only : ice_HaloUpdate
@@ -312,12 +312,19 @@
 !print ghost info
   ! write(il_out,*) '  ghost global:',gh_ilo, gh_ihi, gh_jlo, gh_jhi 
 
-!calculate partition using nprocsX and nprocsX
-  l_ilo=mod(my_task,nprocsX)*nx_global/nprocsX+1
-  l_ihi=l_ilo + nx_global/nprocsX -1
-  l_jlo=int(my_task/nprocsX) * ny_global/nprocsY+1
-  l_jhi=l_jlo+ny_global/nprocsY - 1
- 
+! The OASIS partition is the rectangle this task actually owns, as derived
+! from the block distribution in the loop above.  It used to be recomputed
+! here from ice_distribution's nprocsX/nprocsY, but those are only ever set
+! by create_distrb_cart, so with distribution_type = 'sectrobin', 'roundrobin',
+! 'sectcart' or 'spacecurve' they were still zero and this divided by zero.
+! The distribution-derived bounds are correct for every distribution type, and
+! this driver already requires max_blocks == 1, so the region is a rectangle.
+  if (l_ihi < l_ilo .or. l_jhi < l_jlo) then
+    call abort_ice('(init_cpl): this processor owns no ice blocks; the '// &
+                   'ACCESS/ESM coupling requires exactly one block per '// &
+                   'processor, so reduce nprocs or the block size')
+  endif
+
   ! write(il_out,*) '  2local partion, ilo, ihi, jlo, jhi=', l_ilo, l_ihi, l_jlo, l_jhi
   ! write(il_out,*) '  2partition x,y sizes:', l_ihi-l_ilo+1, l_jhi-l_jlo+1
  
diff --git a/source/ice_distribution.F90 b/source/ice_distribution.F90
index 0a3a31e..770ee14 100644
--- a/source/ice_distribution.F90
+++ b/source/ice_distribution.F90
@@ -50,12 +50,12 @@
                              ! 'slenderX1' (NPX x 1)
                              ! 'slenderX2' (NPX x 2)
 
-!ars599: 26032014: will call from cpl_interface
-!	from function create_distrb_cart
-!	so change to public
-   integer (int_kind), public ::    &
-       nprocsX,             &! num of procs in x for global domain
-       nprocsY               ! num of procs in y for global domain
+! nprocsX/nprocsY used to be module variables, made public (ars599, 26032014)
+! so that drivers/access/cpl_interface.F90 could reuse them.  They are only
+! ever assigned by create_distrb_cart, so with any other distribution_type
+! they stayed zero and the driver divided by zero.  The driver now derives its
+! OASIS partition from the block distribution itself, so these are local to
+! create_distrb_cart again.
 
 
 !***********************************************************************
@@ -561,8 +561,8 @@
       processor,             &! processor position in cartesian decomp
       globalID,              &! global block ID
       localID,               &! block location on this processor
-!      nprocsX,             &! num of procs in x for global domain
-!      nprocsY,             &! num of procs in y for global domain
+      nprocsX,               &! num of procs in x for global domain
+      nprocsY,               &! num of procs in y for global domain
       numBlocksXPerProc,     &! num of blocks per processor in x
       numBlocksYPerProc       ! num of blocks per processor in y
 
-- 
2.43.0

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions