class_docstr: Responsible for a rectangular partitioning of a global domain,
  which is expressed as the numbers of cells in the different
  spatial directions. The partitioning info is expressed as an
  array of integers, each indicating the number of subdomains in
  one spatial direction.
docstr: Find the subdomain rank (tuple) for each processor and
  determine the neighbor info.
docstr_tok: ["Find","the","subdomain","rank","(","tuple",")","for","each","processor","and","determine","the","neighbor","info","."]
code: def prepare_communication(self):
      """
      Find the subdomain rank (tuple) for each processor and
      determine the neighbor info.
      """
  
      nsd_ = self.nsd
      if nsd_ < 1:
          print(
              "Number of space dimensions is %d, nothing to do" % nsd_
          )
          return
  
      self.subd_rank = [-1, -1, -1]
      self.subd_lo_ix = [-1, -1, -1]
      self.subd_hi_ix = [-1, -1, -1]
      self.lower_neighbors = [-1, -1, -1]
      self.upper_neighbors = [-1, -1, -1]
  
      num_procs = self.num_procs
      my_id = self.my_id
  
      num_subds = 1
      for i in range(nsd_):
          num_subds = num_subds * self.num_parts[i]
      if my_id == 0:
          print("# subds=", num_subds)
          # should check num_subds againt num_procs
  
      offsets = [1, 0, 0]
  
      # find the subdomain rank
      self.subd_rank[0] = my_id % self.num_parts[0]
      if nsd_ >= 2:
          offsets[1] = self.num_parts[0]
          self.subd_rank[1] = my_id / offsets[1]
      if nsd_ == 3:
          offsets[1] = self.num_parts[0]
          offsets[2] = self.num_parts[0] * self.num_parts[1]
          self.subd_rank[1] = (my_id % offsets[2]) / self.num_parts[0]
          self.subd_rank[2] = my_id / offsets[2]
  
      print("my_id=%d, subd_rank: " % my_id, self.subd_rank)
      if my_id == 0:
          print("offsets=", offsets)
  
      # find the neighbor ids
      for i in range(nsd_):
          rank = self.subd_rank[i]
          if rank > 0:
              self.lower_neighbors[i] = my_id - offsets[i]
          if rank < self.num_parts[i] - 1:
              self.upper_neighbors[i] = my_id + offsets[i]
  
          k = self.global_num_cells[i] / self.num_parts[i]
          m = self.global_num_cells[i] % self.num_parts[i]
  
          ix = rank * k + max(0, rank + m - self.num_parts[i])
          self.subd_lo_ix[i] = ix
  
          ix = ix + k
          if rank >= (self.num_parts[i] - m):
              ix = ix + 1  # load balancing
          if rank < self.num_parts[i] - 1:
              ix = ix + 1  # one cell of overlap
          self.subd_hi_ix[i] = ix
  
      print(
          "subd_rank:",
          self.subd_rank,
          "lower_neig:",
          self.lower_neighbors,
          "upper_neig:",
          self.upper_neighbors,
      )
      print(
          "subd_rank:",
          self.subd_rank,
          "subd_lo_ix:",
          self.subd_lo_ix,
          "subd_hi_ix:",
          self.subd_hi_ix,
      )
  
imports: from numpy import ascontiguousarray, frombuffer, zeros
label_desc: Use image node_id 3 for calling the RectPartitioner obj's underlying member method code with example usage: obj.prepare_communication() without return types