subroutine cubeio_write_cube(cubset,cubdef,head,cub,error)
  use cubeio_messaging
  use cubetools_dataformat
  use cubetools_header_types
  use cubetools_setup_types
  use cubeio_interfaces, except_this=>cubeio_write_cube
  use cubeio_header
  use cubeio_cube_define
  use cubeio_cube
  !---------------------------------------------------------------------
  ! @ private
  ! Write a cube from memory to disk
  !---------------------------------------------------------------------
  type(cube_setup_t),  intent(in)    :: cubset
  type(cube_define_t), intent(in)    :: cubdef
  type(cube_header_t), intent(in)    :: head
  type(cubeio_cube_t), intent(inout) :: cub
  logical,             intent(inout) :: error
  ! Local
  character(len=*), parameter :: rname='WRITE>CUBE'
  !
  select case (cub%desc%buffered)
  case (code_buffer_none)
    call cubeio_message(seve%e,rname,'No data available')
    error = .true.
    return
    !
  case (code_buffer_memory)
    if (.not.cubset%output%write)  return  ! Writing is disabled
    !
    ! Sanity
    if (.not.cubdef%dofilename) then
      call cubeio_message(seve%e,rname,'Missing file name')
      error = .true.
      return
    endif
    if (.not.cubdef%doaccess) then
      call cubeio_message(seve%e,rname,'Internal error: output access mode must be set')
      error = .true.
      return
    endif
    !
    ! Write everything (header+data) from memory to disk
    select case (cubdef%filekind)
    case (code_dataformat_fits)
      call cubeio_write_cube_fits(cubset,cubdef,head,cub,error)
    case (code_dataformat_gdf)
      call cubeio_write_cube_gdf(cubset,cubdef,head,cub,error)
    case default
      call cubeio_message(seve%e,rname,'No associated file on disk')
      error = .true.
    end select
    if (error)  return
    call cubeio_message(seve%i,rname,'Cube written to file '//cubdef%filename)
    !
  case (code_buffer_disk)
    ! Already on disk... might need flushing the last buffer.
    call cubeio_flush_block(cubset,head,cub,cub%file%block,error)
    if (error)  return
    call cubeio_message(seve%i,rname,'Cube flushed to file '//cub%file%hgdf%file)
    !
  case default
    call cubeio_message(seve%e,rname,'Unexpected buffering kind')
    error = .true.
    return
  end select
  !
end subroutine cubeio_write_cube
!
subroutine cubeio_write_cube_gdf(cubset,cubdef,head,cub,error)
  use cubetools_header_types
  use cubetools_setup_types
  use cubeio_interfaces, except_this=>cubeio_write_cube_gdf
  use cubeio_header
  use cubeio_range
  use cubeio_cube_define
  use cubeio_cube
  !---------------------------------------------------------------------
  ! @ private
  ! Write cube to disk under GDF format
  !---------------------------------------------------------------------
  type(cube_setup_t),  intent(in)    :: cubset
  type(cube_define_t), intent(in)    :: cubdef
  type(cube_header_t), intent(in)    :: head
  type(cubeio_cube_t), intent(inout) :: cub
  logical,             intent(inout) :: error
  ! Local
  character(len=*), parameter :: rname='WRITE>CUBE>GDF'
  type(cubeio_range_t) :: range
  !
  call cubeio_create_hgdf(head,cub%desc,cubdef%order,cub%file%hgdf,error)
  if (error)  return
  cub%file%hgdf%file = cubdef%filename
  call gdf_create_image(cub%file%hgdf,error)
  if (error)  return
  range%blc(:) = 0
  range%trc(:) = 0
  if (cub%desc%iscplx) then
    call cubeio_write_cube_data(rname,cubset,cub,range,cub%memo%c4,error)
  else
    call cubeio_write_cube_data(rname,cubset,cub,range,cub%memo%r4,error)
  endif
  if (error)  return
  call gdf_close_image(cub%file%hgdf,error)
  if (error)  return
  !
end subroutine cubeio_write_cube_gdf
!
subroutine cubeio_write_cube_fits(cubset,cubdef,head,cub,error)
  use cubefitsio_image_write
  use cubefitsio_image_utils
  use cubeio_messaging
  use cubetools_header_types
  use cubetools_setup_types
  use cubeio_interfaces, except_this=>cubeio_write_cube_fits
  use cubeio_header
  use cubeio_range
  use cubeio_cube_define
  use cubeio_cube
  !---------------------------------------------------------------------
  ! @ private
  ! Write cube to disk under FITS format
  !---------------------------------------------------------------------
  type(cube_setup_t),  intent(in)    :: cubset
  type(cube_define_t), intent(in)    :: cubdef
  type(cube_header_t), intent(in)    :: head
  type(cubeio_cube_t), intent(inout) :: cub
  logical,             intent(inout) :: error
  include 'gbl_memory.inc'
  ! Local
  character(len=*), parameter :: rname='WRITE>CUBE>FITS'
  integer(kind=4), parameter :: nbits=-32  ! IEEE floating point
  type(cubeio_range_t) :: range
  !
  call cubeio_create_hfits(head,cubdef%order,cub%file%hfits,error)
  if (error)  return
  !
  call cubefitsio_image_create(cub%file%hfits,cubdef%filename,error)
  if (error)  return
  !
  ! Location of data
  range%blc(:) = 0
  range%trc(:) = 0
  if (cub%desc%iscplx) then
    call cubeio_message(seve%e,rname,'Exporting COMPLEX*4 data to FITS is not implemented')
    error = .true.
    return
  else
    call cubefitsio_image_datawrite(cub%file%hfits,cub%memo%r4,range%blc,range%trc,error)
    if (error)  continue
  endif
  !
  call cubefitsio_image_close(cub%file%hfits,error)
  if (error)  return
  !
end subroutine cubeio_write_cube_fits
!
subroutine cubeio_write_cube_data_r4(rname,cubset,cub,range,data,error)
  use gkernel_interfaces
  use gkernel_types
  use cubetools_dataformat
  use cubetools_setup_types
  use cubefitsio_image_write
  use cubeio_interfaces, except_this=>cubeio_write_cube_data_r4
  use cubeio_range
  use cubeio_cube
  use cubeio_timing
  use cubeio_messaging
  !---------------------------------------------------------------------
  ! @ private-generic cubeio_write_cube_data
  ! Wrapper around gdf_write_data (R*4 version)
  !---------------------------------------------------------------------
  character(len=*),     intent(in)    :: rname
  type(cube_setup_t),   intent(in)    :: cubset
  type(cubeio_cube_t),  intent(inout) :: cub   ! INOUT for cub%file%hgdf%blc/trc
  type(cubeio_range_t), intent(in)    :: range
  real(kind=4),         intent(inout) :: data(:,:,:)  ! INOUT for reblank mode
  logical,              intent(inout) :: error
  ! Local
  type(cputime_t) :: tmp
  integer(kind=index_length) :: i2,i3
  real(kind=4) :: bval
  !
  call gag_cputime_init(tmp)
  !
  select case (cub%file%kind)
  case (code_dataformat_fits)
    call cubefitsio_image_datawrite(cub%file%hfits,data,range%blc,range%trc,error)
    if (error)  return
    !
  case (code_dataformat_gdf)
    if (cub%file%hgdf%gil%blan_words.gt.0 .and. cub%file%hgdf%gil%eval.ge.0.) then
      bval = cub%file%hgdf%gil%bval
      do i3=1,ubound(data,3)
        do i2=1,ubound(data,2)
          where (data(:,i2,i3).ne.data(:,i2,i3))
            data(:,i2,i3) = bval  ! ZZZ We should not modify the input data buffer
                                  !     which should be intent(in). But is it worth
                                  !     making a copy?
          end where
        enddo
      enddo
    endif
    cub%file%hgdf%blc(:) = range%blc(:)
    cub%file%hgdf%trc(:) = range%trc(:)
    call gdf_write_data(cub%file%hgdf,data,error)
    if (error)  return
    !
  case default
    call cubeio_message(seve%e,rname,'No associated file on disk')
    error = .true.
    return
  end select
  !
  call gag_cputime_add(cub%time%writ,tmp)
  !
end subroutine cubeio_write_cube_data_r4
!
subroutine cubeio_write_cube_data_c4(rname,cubset,cub,range,data,error)
  use gkernel_interfaces
  use gkernel_types
  use cubetools_dataformat
  use cubetools_setup_types
  use cubeio_interfaces, except_this=>cubeio_write_cube_data_c4
  use cubeio_range
  use cubeio_cube
  use cubeio_timing
  use cubeio_messaging
  !---------------------------------------------------------------------
  ! @ private-generic cubeio_write_cube_data
  ! Wrapper around gdf_write_data (C*4 version)
  !---------------------------------------------------------------------
  character(len=*),     intent(in)    :: rname
  type(cube_setup_t),   intent(in)    :: cubset
  type(cubeio_cube_t),  intent(inout) :: cub   ! INOUT for cub%file%hgdf%blc/trc
  type(cubeio_range_t), intent(in)    :: range
  complex(kind=4),      intent(inout) :: data(:,:,:)  ! INOUT for reblank mode
  logical,              intent(inout) :: error
  ! Local
  type(cputime_t) :: tmp
  !
  call gag_cputime_init(tmp)
  !
  select case (cub%file%kind)
  case (code_dataformat_fits)
    call cubeio_message(seve%e,rname,'Not implemented: writing C*4 buffer to FITS')
    error = .true.
    return
  case (code_dataformat_gdf)
    cub%file%hgdf%blc(:) = range%blc(:)
    cub%file%hgdf%trc(:) = range%trc(:)
    call gdf_write_data(cub%file%hgdf,data,error)
    if (error)  return
  case default
    call cubeio_message(seve%e,rname,'No associated file on disk')
    error = .true.
    return
  end select
  !
  call gag_cputime_add(cub%time%writ,tmp)
  !
end subroutine cubeio_write_cube_data_c4
!
subroutine cubeio_check_output_chan_block(cubset,head,cub,fochan,lochan,error)
  use cubeio_messaging
  use cubetools_header_types
  use cubetools_setup_types
  use cubeio_interfaces, except_this=>cubeio_check_output_chan_block
  use cubeio_block
  use cubeio_cube
  !---------------------------------------------------------------------
  ! @ private
  ! Check is the current block buffer provides the fochan:lochan channel
  ! range. If not, flush the previous contents and load proper one.
  !---------------------------------------------------------------------
  type(cube_setup_t),   intent(in)    :: cubset
  type(cube_header_t),  intent(in)    :: head
  type(cubeio_cube_t),  intent(inout) :: cub
  integer(kind=chan_k), intent(in)    :: fochan
  integer(kind=chan_k), intent(in)    :: lochan
  logical,              intent(inout) :: error
  ! Local
  character(len=*), parameter :: rname='CHECK>OUTPUT>CHAN>BLOCK'
  character(len=message_length) :: mess
  integer(kind=chan_k) :: nchanperblock
  !
  ! ZZZ Check sanity of cub%file%block => should be LMV from previous calls
  ! ZZZ Assume fochan.le.lochan
  if (fochan.ge.cub%file%block%first .and. lochan.le.cub%file%block%last)  return
  !
  ! Before flushing the current buffer, ensure all the tasks are finished:
  !$OMP TASKWAIT
  !
  if (fochan.ne.cub%file%block%last+1) then
    ! This means the channel buffers are not written contiguously, in order.
    ! One of the major risk is a possible overlap of the new buffer with
    ! data already flushed on disk. This would require re-loading what is
    ! already written. BUT, on the other hand, this is not a problem if this
    ! is a region which was not used in first buffer, but will only be filled
    ! by the new one.
    call cubeio_message(seve%e,rname,'Non-contiguous output buffer is not implemented')
    write(mess,'(4(A,I0))')  &
      'Previous range: ',cub%file%block%first,'-',cub%file%block%last,  &
      ', new range: ',fochan,'-',lochan
    call cubeio_message(seve%e,rname,mess)
    error = .true.
    return
  endif
  !
  ! Need to buffer another region. Flush current contents first
  call cubeio_flush_any_block(cubset,head,cub,cub%file%block,error)
  if (error)  return
  !
  call cubeio_max_chan_block(cubset,cub,cubset%buff%block,'SET\BUFFER /BLOCK',  &
    nchanperblock,error)
  if (error)  return
  if (nchanperblock.lt.lochan-fochan+1) then
    call cubeio_message(seve%e,rname,  &
      'SET\BUFFERING /PARALLEL must be smaller than SET\BUFFERING /BLOCK')
    ! It should even be MUCH larger than the buffer block, so that several ranges
    ! can be processed in parallel by several parallelisation tasks.
    error = .true.
    return
  endif
  call cubeio_block_reallocate(cubset,cub%file%block,cub%desc%iscplx,  &
    cub%desc%nx,cub%desc%ny,nchanperblock,code_cube_imaset,error)
  if (error)  return
  cub%file%block%first = fochan
  cub%file%block%last  = min(fochan+nchanperblock-1,cub%desc%nc)
  write(mess,'(2(A,I0))')  &
    'Buffering output channel block from ',cub%file%block%first,' to ',cub%file%block%last
  call cubeio_message(ioseve%others,rname,mess)
  !
end subroutine cubeio_check_output_chan_block
!
subroutine cubeio_check_output_pix_block(cubset,head,cub,fypix,lypix,error)
  use cubeio_messaging
  use cubetools_header_types
  use cubetools_setup_types
  use cubeio_interfaces, except_this=>cubeio_check_output_pix_block
  use cubeio_block
  use cubeio_cube
  !---------------------------------------------------------------------
  ! @ private
  ! Check is the current block buffer provides the fypix:lypix Y pixel
  ! range. If not, flush the previous contents and load proper one.
  !---------------------------------------------------------------------
  type(cube_setup_t),   intent(in)    :: cubset
  type(cube_header_t),  intent(in)    :: head
  type(cubeio_cube_t),  intent(inout) :: cub
  integer(kind=pixe_k), intent(in)    :: fypix
  integer(kind=pixe_k), intent(in)    :: lypix
  logical,              intent(inout) :: error
  ! Local
  character(len=*), parameter :: rname='CHECK>OUTPUT>PIX>BLOCK'
  character(len=message_length) :: mess
  integer(kind=pixe_k) :: nyperblock
  !
  ! ZZZ Check sanity of cub%file%block => should be VLM from previous calls
  ! ZZZ Assume fypix.le.lypix
  if (fypix.ge.cub%file%block%first .and. lypix.le.cub%file%block%last)  return
  !
  ! Before flushing the current buffer, ensure all the tasks are finished:
  !$OMP TASKWAIT
  !
  if (fypix.ne.cub%file%block%last+1) then
    ! This means the Y row buffers are not written contiguously, in order.
    ! One of the major risk is a possible overlap of the new buffer with
    ! data already flushed on disk. This would require re-loading what is
    ! already written. BUT, on the other hand, this is not a problem if this
    ! is a region which was not used in first buffer, but will only be filled
    ! by the new one.
    call cubeio_message(seve%e,rname,'Non-contiguous output buffer is not implemented')
    write(mess,'(4(A,I0))')  &
      'Previous range: ',cub%file%block%first,'-',cub%file%block%last,  &
      ', new range: ',fypix,'-',lypix
    call cubeio_message(seve%e,rname,mess)
    error = .true.
    return
  endif
  !
  ! Need to buffer another region. Flush current contents first
  call cubeio_flush_any_block(cubset,head,cub,cub%file%block,error)
  if (error)  return
  !
  call cubeio_max_y_block(cubset,cub,cubset%buff%block,'SET\BUFFER /BLOCK',  &
    nyperblock,error)
  if (error)  return
  if (nyperblock.lt.lypix-fypix+1) then
    call cubeio_message(seve%e,rname,  &
      'SET\BUFFERING /PARALLEL must be smaller than SET\BUFFERING /BLOCK')
    ! It should even be MUCH larger than the buffer block, so that several ranges
    ! can be processed in parallel by several parallelisation tasks.
    error = .true.
    return
  endif
  call cubeio_block_reallocate(cubset,cub%file%block,cub%desc%iscplx,  &
    cub%desc%nx,nyperblock,cub%desc%nc,code_cube_speset,error)
  if (error)  return
  cub%file%block%first = fypix
  cub%file%block%last  = min(fypix+nyperblock-1,cub%desc%ny)
  write(mess,'(2(A,I0))')  &
    'Buffering output Y row block from ',cub%file%block%first,' to ',cub%file%block%last
  call cubeio_message(ioseve%others,rname,mess)
  !
end subroutine cubeio_check_output_pix_block
!
subroutine cubeio_check_output_any_block(cubset,head,cub,fo3,lo3,error)
  use cubeio_messaging
  use cubetools_header_types
  use cubetools_setup_types
  use cubeio_interfaces, except_this=>cubeio_check_output_any_block
  use cubeio_block
  use cubeio_cube
  !---------------------------------------------------------------------
  ! @ private
  ! Check is the current block buffer provides the fo3:lo3 data
  ! range. If not, flush the previous contents and load proper one.
  !---------------------------------------------------------------------
  type(cube_setup_t),   intent(in)    :: cubset
  type(cube_header_t),  intent(in)    :: head
  type(cubeio_cube_t),  intent(inout) :: cub
  integer(kind=data_k), intent(in)    :: fo3
  integer(kind=data_k), intent(in)    :: lo3
  logical,              intent(inout) :: error
  ! Local
  character(len=*), parameter :: rname='CHECK>OUTPUT>ANY>BLOCK'
  character(len=message_length) :: mess
  integer(kind=data_k) :: n3perblock
  !
  ! ZZZ Assume fo3.le.lo3
  if (fo3.ge.cub%file%block%first .and. lo3.le.cub%file%block%last)  return
  !
  ! Before flushing the current buffer, ensure all the tasks are finished:
  !$OMP TASKWAIT
  !
  if (fo3.ne.cub%file%block%last+1) then
    ! This means the plane buffers are not written contiguously, in order.
    ! One of the major risk is a possible overlap of the new buffer with
    ! data already flushed on disk. This would require re-loading what is
    ! already written. BUT, on the other hand, this is not a problem if this
    ! is a region which was not used in first buffer, but will only be filled
    ! by the new one.
    ! Under normal conditions, the task loop iterator is expected to
    ! properly split the accesses to the file.
    call cubeio_message(seve%e,rname,'Non-contiguous output buffer is not implemented')
    write(mess,'(4(A,I0))')  &
      'Previous range: ',cub%file%block%first,'-',cub%file%block%last,  &
      ', new range: ',fo3,'-',lo3
    call cubeio_message(seve%e,rname,mess)
    error = .true.
    return
  endif
  !
  ! Need to buffer another region. Flush current contents first
  call cubeio_flush_any_block(cubset,head,cub,cub%file%block,error)
  if (error)  return
  !
  call cubeio_max_any_block(cubset,cub,cubset%buff%block,'SET\BUFFER /BLOCK',  &
    n3perblock,error)
  if (error)  return
  if (n3perblock.lt.lo3-fo3+1) then
    call cubeio_message(seve%e,rname,  &
      'SET\BUFFERING /PARALLEL must be smaller than SET\BUFFERING /BLOCK')
    ! It should even be MUCH larger than the buffer block, so that several ranges
    ! can be processed in parallel by several parallelisation tasks.
    error = .true.
    return
  endif
  call cubeio_anyblock_reallocate(cubset,cub%file%block,cub%desc%iscplx,  &
    cub%desc%n1,cub%desc%n2,n3perblock,code_cube_unkset,error)
  if (error)  return
  cub%file%block%first = fo3
  cub%file%block%last  = min(fo3+n3perblock-1,cub%desc%n3)
  write(mess,'(2(A,I0))')  &
    'Buffering output subcube block from ',cub%file%block%first,' to ',cub%file%block%last
  call cubeio_message(ioseve%others,rname,mess)
  !
end subroutine cubeio_check_output_any_block
!
subroutine cubeio_write_any_block(cubset,head,cub,block,error)
  use cubeio_messaging
  use gkernel_interfaces
  use cubefitsio_image_write
  use cubetools_dataformat
  use cubetools_header_types
  use cubetools_setup_types
  use cubeio_interfaces, except_this=>cubeio_write_any_block
  use cubeio_header
  use cubeio_block
  use cubeio_range
  use cubeio_cube
  !---------------------------------------------------------------------
  ! @ private
  ! Write a block to file on disk
  !---------------------------------------------------------------------
  type(cube_setup_t),   intent(in)    :: cubset
  type(cube_header_t),  intent(in)    :: head
  type(cubeio_cube_t),  intent(inout) :: cub
  type(cubeio_block_t), intent(inout) :: block
  logical,              intent(inout) :: error
  ! Local
  character(len=*), parameter :: rname='WRITE>ANY>BLOCK'
  type(cubeio_range_t) :: range
  logical :: ok
  !
  ! Sanity
  ok = .false.
  if (cub%order().eq.code_cube_imaset .and. block%order.eq.code_cube_imaset)  ok = .true.
  if (cub%order().eq.code_cube_speset .and. block%order.eq.code_cube_speset)  ok = .true.
  if (                                      block%order.eq.code_cube_unkset)  ok = .true.
  if (.not.ok) then
    call cubeio_message(seve%e,rname,'Block and output cube order mismatch')
    error = .true.
    return
  endif
  if (cub%desc%iscplx.neqv.cub%file%block%iscplx) then
    call cubeio_message(seve%e,rname,'Block and output cube type mismatch (R*4/C*4)')
    error = .true.
    return
  endif
  !
  ! Update the header on disk (e.g. the extrema section might be updated after
  ! processing each block)
  select case (cub%file%kind)
  case (code_dataformat_fits)
    if (cub%desc%action.eq.code_write) then
      ! No need to close and extend (done at data write time)
      call cubeio_create_and_truncate_hfits(head,cub%order(),block%last,cub%file%hfits,error)
      if (error)  return
    else  ! code_update
      call cubeio_create_hfits(head,cub%order(),cub%file%hfits,error)
      if (error)  return
    endif
    call cubefitsio_image_header_update(cub%file%hfits,error)
    if (error)  return
    !
  case (code_dataformat_gdf)
    if (cub%desc%action.eq.code_write) then
      ! NB: the current header in memory is transfered to the file AFTER
      ! gdf_extend_image so that the new dimension is correctly applied by
      ! gdf_extend_image. This means the data is first added/updated (with
      ! consistent dimensions), then the header (remaining sections) is updated.
      if (cub%file%hgdf%loca%islo.gt.0) then  ! Close (if relevant) before reopening
        call gdf_close_image(cub%file%hgdf,error)
        if (error)  return
      endif
      call gdf_extend_image(cub%file%hgdf,block%last,error)
      if (error)  return
      call cubeio_create_and_truncate_hgdf(head,cub%desc,cub%order(),block%last,cub%file%hgdf,error)
      if (error)  return
    else  ! code_update
      call cubeio_create_hgdf(head,cub%desc,cub%order(),cub%file%hgdf,error)
      if (error)  return
    endif
    call gdf_update_header(cub%file%hgdf,error)
    if (error)  return
    !
  case default
    call cubeio_message(seve%e,rname,'No associated file on disk')
    error = .true.
    return
  end select
  !
  ! Write data in the old or extended part
  range%blc(:) = 0
  range%trc(:) = 0
  range%blc(cub%desc%i3) = block%first
  range%trc(cub%desc%i3) = block%last
  if (cub%desc%iscplx) then
    call cubeio_write_cube_data(rname,cubset,cub,range,block%c4,error)
  else
    call cubeio_write_cube_data(rname,cubset,cub,range,block%r4,error)
  endif
  if (error)  return
  !
  ! Never close the image (GIO slot), the basic assumption is that it
  ! is always opened for reuse at any time.
  !
end subroutine cubeio_write_any_block
!
subroutine cubeio_flush_block(cubset,head,cub,block,error)
  use cubetools_header_types
  use cubetools_setup_types
  use cubeio_interfaces, except_this=>cubeio_flush_block
  use cubeio_block
  use cubeio_cube
  !---------------------------------------------------------------------
  ! @ private
  ! Flush IF RELEVANT a block buffer to file on disk
  !---------------------------------------------------------------------
  type(cube_setup_t),   intent(in)    :: cubset
  type(cube_header_t),  intent(in)    :: head
  type(cubeio_cube_t),  intent(inout) :: cub
  type(cubeio_block_t), intent(inout) :: block
  logical,              intent(inout) :: error
  !
  if (cub%desc%buffered.eq.code_buffer_disk) then
    call cubeio_flush_any_block(cubset,head,cub,cub%file%block,error)
    if (error)  return
  endif
end subroutine cubeio_flush_block
!
subroutine cubeio_flush_any_block(cubset,head,cub,block,error)
  use cubeio_messaging
  use cubetools_header_types
  use cubetools_setup_types
  use cubeio_interfaces, except_this=>cubeio_flush_any_block
  use cubeio_block
  use cubeio_cube
  !---------------------------------------------------------------------
  ! @ private
  ! Flush IF RELEVANT a block of planes to file on disk
  !---------------------------------------------------------------------
  type(cube_setup_t),   intent(in)    :: cubset
  type(cube_header_t),  intent(in)    :: head
  type(cubeio_cube_t),  intent(inout) :: cub
  type(cubeio_block_t), intent(inout) :: block
  logical,              intent(inout) :: error
  ! Local
  character(len=*), parameter :: rname='FLUSH>ANY>BLOCK'
  character(len=message_length) :: mess
  character(len=8) :: what
  !
  if (block%first.le.0)      return  ! This buffer was never used
  if (.not.block%readwrite)  return  ! Read-only => nothing to write or update
  !
  select case (block%order)
  case (code_cube_imaset)
    what = 'channel'
  case (code_cube_speset)
    what = 'Y row'
  case (code_cube_unkset)
    what = 'subcube'
  case default
    what = '???'
  end select
  write(mess,'(A,A,A,I0,A,I0)')  &
    'Flushing ',trim(what),' block from ',block%first,' to ',block%last
  call cubeio_message(ioseve%others,rname,mess)
  !
  call cubeio_write_any_block(cubset,head,cub,block,error)
  if (error)  return
  !
  ! Was flushed => set as read-only so that it is not flushed again
  block%readwrite = .false.
  !
end subroutine cubeio_flush_any_block
