pf_parallel_oc.f90 Source File

Main controllers for optimal control problems


This file depends on

sourcefile~~pf_parallel_oc.f90~~EfferentGraph sourcefile~pf_parallel_oc.f90 pf_parallel_oc.f90 sourcefile~pf_hooks.f90 pf_hooks.f90 sourcefile~pf_parallel_oc.f90->sourcefile~pf_hooks.f90 sourcefile~pf_utils.f90 pf_utils.f90 sourcefile~pf_parallel_oc.f90->sourcefile~pf_utils.f90 sourcefile~pf_timer.f90 pf_timer.f90 sourcefile~pf_parallel_oc.f90->sourcefile~pf_timer.f90 sourcefile~pf_dtype.f90 pf_dtype.f90 sourcefile~pf_parallel_oc.f90->sourcefile~pf_dtype.f90 sourcefile~pf_comm.f90 pf_comm.f90 sourcefile~pf_parallel_oc.f90->sourcefile~pf_comm.f90 sourcefile~pf_interpolate.f90 pf_interpolate.f90 sourcefile~pf_parallel_oc.f90->sourcefile~pf_interpolate.f90 sourcefile~pf_pfasst.f90 pf_pfasst.f90 sourcefile~pf_parallel_oc.f90->sourcefile~pf_pfasst.f90 sourcefile~pf_restrict.f90 pf_restrict.f90 sourcefile~pf_parallel_oc.f90->sourcefile~pf_restrict.f90 sourcefile~pf_hooks.f90->sourcefile~pf_timer.f90 sourcefile~pf_hooks.f90->sourcefile~pf_dtype.f90 sourcefile~pf_utils.f90->sourcefile~pf_timer.f90 sourcefile~pf_utils.f90->sourcefile~pf_dtype.f90 sourcefile~pf_timer.f90->sourcefile~pf_dtype.f90 sourcefile~pf_comm.f90->sourcefile~pf_pfasst.f90 sourcefile~pf_interpolate.f90->sourcefile~pf_hooks.f90 sourcefile~pf_interpolate.f90->sourcefile~pf_utils.f90 sourcefile~pf_interpolate.f90->sourcefile~pf_timer.f90 sourcefile~pf_interpolate.f90->sourcefile~pf_dtype.f90 sourcefile~pf_interpolate.f90->sourcefile~pf_restrict.f90 sourcefile~pf_pfasst.f90->sourcefile~pf_hooks.f90 sourcefile~pf_pfasst.f90->sourcefile~pf_utils.f90 sourcefile~pf_pfasst.f90->sourcefile~pf_dtype.f90 sourcefile~pf_mpi.f90 pf_mpi.f90 sourcefile~pf_pfasst.f90->sourcefile~pf_mpi.f90 sourcefile~pf_quadrature.f90 pf_quadrature.f90 sourcefile~pf_pfasst.f90->sourcefile~pf_quadrature.f90 sourcefile~pf_restrict.f90->sourcefile~pf_hooks.f90 sourcefile~pf_restrict.f90->sourcefile~pf_timer.f90 sourcefile~pf_restrict.f90->sourcefile~pf_dtype.f90 sourcefile~pf_mpi.f90->sourcefile~pf_timer.f90 sourcefile~pf_mpi.f90->sourcefile~pf_dtype.f90 sourcefile~pf_quadrature.f90->sourcefile~pf_dtype.f90

Contents

Source Code


Source Code

!!  Main controllers for optimal control problems
!
! This file is part of LIBPFASST.
!
!> Module of parallel PFASST routines for optimal control problems.
module pf_mod_parallel_oc
  use pf_mod_pfasst
  use pf_mod_interpolate
  use pf_mod_restrict
  use pf_mod_utils
  use pf_mod_timer
  use pf_mod_dtype
  use pf_mod_hooks
  use pf_mod_comm
  implicit none
contains

  subroutine pf_predictor_oc(pf, t0, dt, flags)
    type(pf_pfasst_t), intent(inout), target :: pf     !! PFASST main data structure
    real(pfdp),        intent(in   )         :: t0     !! Initial time of this processor
    real(pfdp),        intent(in   )         :: dt     !! time step
    integer,           intent(in   ), optional :: flags(:)  !!  User defined flags

    class(pf_level_t), pointer :: c_lev_p
    class(pf_level_t), pointer :: f_lev_p
    integer                   :: k               !!  Loop indices
    integer                   :: level_index     !!  Local variable for looping over levels
    real(pfdp)                :: t0k             !!  Initial time at time step k
    integer :: which, dir, send_tag 
 
    which = 1                   ! standard: predict and sweep forward-in-time
    dir = 1                     ! for MPI communication, standard is forward-in-time
    if(present(flags)) then
       if(flags(1)==2) then
          which = 2                ! if we are computing an adjoint, predict and sweep backward-in-time
          dir = 2                  ! communication has to be backwards as well
       end if
       if(flags(1)==0) which = 0   ! sweep forward and backward simultaneously on two components, communication only forwards
    end if
    call call_hooks(pf, 1, PF_PRE_PREDICTOR)
    call start_timer(pf, TPREDICTOR)
    
    if (pf%debug) print*, 'DEBUG --', pf%rank, 'beginning predictor'

    !! Step 1. Getting the  initial condition on the finest level at each processor
    !!         If we are doing multiple levels, then we need to coarsen to fine level
    f_lev_p => pf%levels(pf%nlevels)
    if (pf%q0_style < 2) then  !  Spread q0 to all the nodes
       if( (which == 0) .or. (which == 1)) call f_lev_p%ulevel%sweeper%spreadq0(f_lev_p, t0, 1, pf%state%step+1)
       if( (which == 0) .or. (which == 2)) call f_lev_p%ulevel%sweeper%spreadq0(f_lev_p, t0+dt, 2, pf%state%step+1)
    endif


    !!  Step 2:   Proceed fine to coarse levels coarsening the fine solution and computing tau correction
    if (pf%debug) print*,  'DEBUG --', pf%rank, 'do coarsen  in predictor'    
    if (pf%nlevels > 1) then  
       do level_index = pf%nlevels, 2, -1
          f_lev_p => pf%levels(level_index);
          c_lev_p => pf%levels(level_index-1)
          call pf_residual(pf, f_lev_p, dt, which)  
          if( (which == 0) .or. (which == 1)) call f_lev_p%ulevel%restrict(f_lev_p, c_lev_p, f_lev_p%q0, c_lev_p%q0, t0, 1)
          if( (which == 0) .or. (which == 2)) call f_lev_p%ulevel%restrict(f_lev_p, c_lev_p, f_lev_p%qend, c_lev_p%qend, t0+dt, 2)
          call restrict_time_space_fas(pf, t0, dt, level_index, which)  !  Restrict
          call save(c_lev_p, which)
       end do  !  level_index = pf%nlevels, 2, -1
    end if
    level_index = 1
    c_lev_p => pf%levels(1)

    ! Step 3. Do the "Burn in" step on the coarse level to make the coarse values consistent
    !         (this is skipped if the fine initial conditions are already consistent)
    ! The first processor does nothing, the second does one set of sweeps, the 2nd two, etc
    ! Hence, this is skipped completely if nprocs=1
    if (pf%q0_style .eq. 0) then  !  The coarse level needs burn in
       if (pf%debug) print*,  'DEBUG --', pf%rank, 'do burnin in pred', ' RK_pred', pf%RK_pred, ' PFASST_pred', pf%PFASST_pred
       !! If RK_pred is true, just do some RK_steps
       if (pf%RK_pred) then  !  Use Runge-Kutta to get the coarse initial data
          !  Get new initial conditions
          call pf_recv(pf, c_lev_p, 100000+pf%rank, .true., dir)

          !  Do a RK_step
          call c_lev_p%ulevel%sweeper%sweep(pf, level_index, t0, dt, 1, which )
          !  Send forward/backward
          if (dir == 1) send_tag = 100000+pf%rank+1
          if (dir == 2) send_tag = 100000+pf%rank-1
          call pf_send(pf, c_lev_p, send_tag, .false., dir)
       else  !  Normal PFASST burn in
          do k = 1, pf%rank + 1
             pf%state%iter = -k
             t0k = t0-(pf%rank)*dt + (k-1)*dt   ! Remember t0=pf%rank*dt is the beginning of this time slice so t0-(pf%rank)*dt is 0
                                                ! and we iterate up to the correct time step.
                                                ! for optimal control problem t, t0k has no influence on f_eval, so there this does something else

             ! Get new initial value (skip on first iteration)
             if (k > 1) then
                if ((which == 0) .or. (which == 1)) call c_lev_p%q0%copy(c_lev_p%qend, 1)
!                 if ((which == 0) .or. (which == 2)) call c_lev_p%qend%copy(c_lev_p%q0, 2) ! for which==0, we solve with zero terminal conditions,
                                                                                            ! but q0,2 is not zero (source term due to state sweeps)
                if (which == 2) call c_lev_p%qend%copy(c_lev_p%q0, 2)
                ! If we are doing PFASST_pred, we use the old values at nodes, otherwise spread q0
                if (.not. pf%PFASST_pred) then
                   if( (which == 0) .or. (which == 1)) call c_lev_p%ulevel%sweeper%spreadq0(c_lev_p, t0k, 1, pf%state%step+1)
!                    if( (which == 0) .or. (which == 2)) call c_lev_p%ulevel%sweeper%spreadq0(c_lev_p, t0k+dt, 2, pf%state%step+1)
                   if( which == 2) call c_lev_p%ulevel%sweeper%spreadq0(c_lev_p, t0k+dt, 2, pf%state%step+1)
                end if
             end if
             !  Do some sweeps
             if( which == 0 .or. which == 1 ) call c_lev_p%ulevel%sweeper%sweep(pf, level_index, t0k, dt, pf%nsweeps_burn, 1)
             if( which == 2 )                 call c_lev_p%ulevel%sweeper%sweep(pf, level_index, t0k, dt, pf%nsweeps_burn, 2)
          end do
       endif  !  RK_pred
    end if  ! (q0_style .eq. 0)

    ! Step 4: Now we have everyone burned in, so do some coarse sweeps
    if (pf%nlevels > 1) then
      pf%state%pstatus = PF_STATUS_ITERATING
      pf%state%status  = PF_STATUS_ITERATING
      if (pf%debug) print*,  'DEBUG --', pf%rank, 'do sweeps  in predictor', ' Pipeline_pred', pf%Pipeline_pred    
      level_index=1
      c_lev_p => pf%levels(level_index)

      if (pf%Pipeline_pred) then
        do k = 1, c_lev_p%nsweeps_pred
          pf%state%iter =-(pf%rank + 1) -k

          !  Get new initial conditions          
          call pf_recv(pf, c_lev_p, c_lev_p%index*110000+pf%rank+k, .true., dir)

          !  Do a sweep
          call c_lev_p%ulevel%sweeper%sweep(pf, c_lev_p%index, t0, dt, 1, which)
          !  Send forward/backward
          if (dir == 1) send_tag = c_lev_p%index*1110000+pf%rank+1+k
          if (dir == 2) send_tag = c_lev_p%index*1110000+pf%rank-1+k
          call pf_send(pf, c_lev_p, send_tag, .false., dir)
       end do ! k = 1, c_lev_p%nsweeps_pred-1
      else  !  Don't pipeline
        !  Get new initial conditions
        call pf_recv(pf, c_lev_p, c_lev_p%index*100000+pf%rank, .true., dir)
 
        !  Do sweeps
        if(which == 0 .or. which == 1) call c_lev_p%ulevel%sweeper%sweep(pf, level_index, t0, dt, c_lev_p%nsweeps_pred, 1) !which ! why only state?
        if(which == 2)                 call c_lev_p%ulevel%sweeper%sweep(pf, level_index, t0, dt, c_lev_p%nsweeps_pred, 2) !which
        !  Send forward/backward
        if (dir == 1) send_tag = c_lev_p%index*100000+pf%rank+1
        if (dir == 2) send_tag = c_lev_p%index*100000+pf%rank-1
        call pf_send(pf, c_lev_p, send_tag, .false., dir)
      endif  ! (Pipeline_pred .eq. .true) then
    end if ! pf%nlevels > 1

    !  Step 5:  Return to fine level sweeping on any level in between coarsest and finest
    if (pf%debug) print*,  'DEBUG --', pf%rank, 'returning to fine level in predictor'
    do level_index = 2, pf%nlevels  !  Will do nothing with one level
       f_lev_p => pf%levels(level_index);
       c_lev_p => pf%levels(level_index-1)
       call interpolate_time_space(pf, t0, dt, level_index, c_lev_p%Finterp, which)
       if ((which == 0) .or. (which == 1)) call interpolate_q0(pf, f_lev_p, c_lev_p, 1)
       if (which == 2)                     call interpolate_qend(pf, f_lev_p, c_lev_p) ! for which==0, qend never changes, so don't need to interpolate
       !  Do sweeps on level unless we are at the finest level
       if (level_index < pf%nlevels) then
          call f_lev_p%ulevel%sweeper%sweep(pf, level_index, t0, dt, f_lev_p%nsweeps_pred, which)
       end if
    end do

    call end_timer(pf, TPREDICTOR)
    call call_hooks(pf, -1, PF_POST_PREDICTOR)

    pf%state%iter   = 0
    pf%state%status = PF_STATUS_ITERATING
    pf%state%pstatus = PF_STATUS_ITERATING

    if (pf%debug) print*,  'DEBUG --', pf%rank, 'ending predictor'    
  end subroutine pf_predictor_oc


  !> Subroutine to test residuals to determine if the current processor has converged.
  subroutine pf_check_residual_oc(pf, residual_converged)
    type(pf_pfasst_t), intent(inout) :: pf
    logical,           intent(out)   :: residual_converged  !! Return true if residual is below tolerances
    
    residual_converged = .false.

    ! Check to see if relative tolerance is met
    if (pf%levels(pf%nlevels)%residual_rel < pf%rel_res_tol) then
       if (pf%debug) print*, 'DEBUG --', pf%rank, ' residual relative tol met',pf%levels(pf%nlevels)%residual_rel
       residual_converged = .true.
    end if
    ! Check to see if relative tolerance is met       
    if   (pf%levels(pf%nlevels)%residual     < pf%abs_res_tol)  then
       if (pf%debug) print*, 'DEBUG --',pf%rank, 'residual tol met',pf%levels(pf%nlevels)%residual
       residual_converged = .true.
    end if

  end subroutine pf_check_residual_oc
  
  !>
  !> Test residuals to determine if the current processor has converged,
  !> adapted to optimal control. Can probably be removed, when pf_pfasst_block_oc
  !> is changed to use pf_check_convergence of pf_check_convergence_old.
  !>
  !> Note that if the previous processor hasn't converged yet
  !> (pstatus), the current processor hasn't converged yet either,
  !> regardless of the residual.
  !>
!   subroutine pf_check_convergence_oc(pf, k, residual,converged, flags)
  subroutine pf_check_convergence_oc(pf, send_tag, flags)
    type(pf_pfasst_t), intent(inout) :: pf
    integer,           intent(in)    :: send_tag
!     real(pfdp),        intent(inout) :: residual
!     integer,           intent(in)    :: k
!     logical,           intent(out)   :: converged   !!  True if this processor is done
    integer, optional, intent(in)    :: flags
!     real(pfdp)     :: residual1
    integer :: dir, which
    logical :: residual_converged, converged
    
    converged = .false.

    
    ! shortcut for fixed block mode
    if (pf%abs_res_tol == 0 .and. pf%rel_res_tol == 0) then
       pf%state%pstatus = PF_STATUS_ITERATING
       pf%state%status  = PF_STATUS_ITERATING
       return
    end if
    
    ! in first sweep: always continue
    if (pf%state%iter == 1) then
       pf%state%pstatus = PF_STATUS_ITERATING
       pf%state%status  = PF_STATUS_ITERATING
       return
    end if
    
    which = 1
    if (present(flags)) which = flags
    ! send forward by default, even if sweeping on both components; send backwards if sweeping on p only
    dir = 1
    if(which == 2) dir = 2

    call call_hooks(pf, 1, PF_PRE_CONVERGENCE)
    
    !> Check to see if tolerances are met
    call pf_check_residual_oc(pf, residual_converged)
    
    !>  Until I hear the previous processor is done, recieve it's status
    if (pf%state%pstatus /= PF_STATUS_CONVERGED) call pf_recv_status(pf, send_tag, dir)

    !>  Check to see if I am converged
    converged = .false.
    if (residual_converged) then
       if (pf%rank == 0 .and. dir==1) then
          converged = .true.
       elseif (pf%rank == pf%comm%nproc-1 .and. dir==2) then
          converged = .true.
       else  !  I am not the first/last processor, so I need to check the previous one
          if (pf%state%pstatus == PF_STATUS_CONVERGED) converged = .true.
       end if
    end if ! (residual_converged)


    !> Assign status and send it forward
    if (converged) then
       if (pf%state%status == PF_STATUS_ITERATING) then
          !  If I am converged for the first time
          !  then flip my flag and send the last status update
          pf%state%status = PF_STATUS_CONVERGED
          call pf_send_status(pf, send_tag, dir)
       end if
    else
       !  I am not converged, send the news
       pf%state%status = PF_STATUS_ITERATING
       call pf_send_status(pf, send_tag, dir)
    end if
    
    call call_hooks(pf, 1, PF_POST_CONVERGENCE)
    
    !!! old code below
    ! Check to see if tolerances are met   
!     residual1 = pf%levels(pf%nlevels)%residual
!     if (pf%state%status == PF_STATUS_ITERATING .and. residual > 0.0d0) then
!        if ( (abs(1.0_pfdp - abs(residual1/residual)) < pf%rel_res_tol) .or. &
!             (abs(residual1)                          < pf%abs_res_tol) ) then
!           pf%state%status = PF_STATUS_CONVERGED
!        end if
!     end if
!         
!     !->why? how to do that more cleanly?
!     if (pf%state%status == PF_STATUS_ITERATING .and. residual >= 0.0d0) then    
!                 ! if do_mixed, adjoint on last time step will be constant zero, so residual will be zero
!                 ! need to stop in that case as well, but not in the very first iteration
!       if( abs(residual1) < pf%abs_res_tol ) then
!           pf%state%status = PF_STATUS_CONVERGED
!       end if
!     end if
!     !!-
!     
!     residual = residual1
! 
!     call call_hooks(pf, 1, PF_PRE_CONVERGENCE)
!     if (pf%state%pstatus /= PF_STATUS_CONVERGED) call pf_recv_status(pf, 1+k, dir)
! 
!     if (pf%rank /= 0 .and. pf%state%pstatus == PF_STATUS_ITERATING .and. dir == 1) &
!          pf%state%status = PF_STATUS_ITERATING
!     if (pf%rank /= pf%comm%nproc-1 .and. pf%state%pstatus == PF_STATUS_ITERATING .and. dir == 2) &
!          pf%state%status = PF_STATUS_ITERATING
!          
! !     if (pf%state%status .ne. PF_STATUS_CONVERGED) 
!     call pf_send_status(pf, 1+k, dir)
!     call call_hooks(pf, 1, PF_POST_CONVERGENCE)
! 
!     ! XXX: this ain't so pretty, perhaps we should use the
!     ! 'nmoved' thinger to break this cycle if everyone is
!     ! done...
! 
!     if (pf%state%status == PF_STATUS_CONVERGED) then
!        converged = .true.
!        return
!     end if
! 
!     if (0 == pf%comm%nproc) then
!        pf%state%status = PF_STATUS_PREDICTOR
!        converged = .true.
!        return
!     end if

  end subroutine pf_check_convergence_oc


  !>  Routine to do the pfasst iterations for optimal control problems on one block of processors until completion.
  !>  Each processor will do either a fixed number of iterations, or iterate until a tolerance is met
  !>  On calling, it is assumed that the levels are already loaded with the initial guesses
  !> 
  subroutine pf_pfasst_block_oc(pf, dt, nsteps, predict, flags, step)
    type(pf_pfasst_t), intent(inout), target :: pf
    real(pfdp),        intent(in)    :: dt
    integer,           intent(in)    :: nsteps 
    logical,           intent(in)    :: predict
    integer, optional, intent(in)    :: flags    !0 (default): sweep on y and p, 1: just y, 2: just p
    integer, optional, intent(in)    :: step
    ! not yet clear how to handle send and receive for forward and backward combined 

    type(pf_level_t), pointer :: fine_lev_p, coarse_lev_p
    integer                   :: k, j, l, which, pred_flags(1), dir, ierror !dir to choose forward or backward send 
    real(pfdp)                :: residual

    logical :: converged, qbroadcast
    logical :: did_post_step_hook

    call start_timer(pf, TTOTAL)

    which = 1
    if (present(flags)) which = flags
    ! send forward by default, even if sweeping on both components; send backwards if sweeping on p only
    dir = 1
    if(which == 2) dir = 2
    pred_flags(1) = which
    
    if( present(step) ) then
      pf%state%step    = step
    else
      pf%state%step    = pf%rank
    end if
      
    pf%state%dt      = dt
    pf%state%proc    = pf%rank+1
    pf%state%t0      = pf%state%step * dt
    pf%state%iter    = -1
    pf%state%cycle   = -1
!     pf%state%itcnt   = 0
    pf%state%mysteps = 0
    pf%state%status  = PF_STATUS_PREDICTOR
    pf%state%pstatus = PF_STATUS_PREDICTOR
    pf%comm%statreq  = -66
    pf%state%nsteps  = nsteps
    

    residual = -1
    did_post_step_hook = .false.
    
!    call pf%results%initialize(nsteps, pf%niters, pf%comm%nproc, pf%nlevels)
    
!     do k = 1, 666666666 
! 
!        qbroadcast = .false.
! 
!        if (pf%state%status == PF_STATUS_CONVERGED .and. .not. did_post_step_hook) then
!          call call_hooks(pf, -1, PF_POST_STEP)
!          did_post_step_hook = .true.
!          pf%state%itcnt = pf%state%itcnt + pf%state%iter
!          pf%state%mysteps = pf%state%mysteps + 1
!          exit
!        end if
! 
!        ! jump to next block if we've reached the max iteration count
!        if (pf%state%iter >= pf%niters) then
! !           print *, pf%rank, 'pf%state%iter >= pf%niters'
!           if (.not. did_post_step_hook) then
!             call call_hooks(pf, -1, PF_POST_STEP)
!             pf%state%itcnt = pf%state%itcnt + pf%state%iter
!             pf%state%mysteps = pf%state%mysteps + 1
!           end if
!           did_post_step_hook = .false.
! 
!           pf%state%step = pf%state%step + pf%comm%nproc
!           pf%state%t0   = pf%state%step * dt
!           
!           if (pf%state%step >= pf%state%nsteps) exit  ! for optimal control this exit should always happen
!           
!           pf%state%status = PF_STATUS_PREDICTOR
!           !pf%state%block  = pf%state%block + 1
!           residual = -1
!           qbroadcast = .true.
!        end if
! 
!        if (k > 1 .and. qbroadcast) then
!           if (pf%comm%nproc > 1) then
!              stop "broadcast not supported" 
!              !fine_lev_p => pf%levels(pf%nlevels)
!              !call pf%comm%wait(pf, pf%nlevels)
!              !call fine_lev_p%encap%pack(fine_lev_p%send, fine_lev_p%qend)
!              !call pf_broadcast(pf, fine_lev_p%send, fine_lev_p%nvars, pf%comm%nproc-1)
!              !call fine_lev_p%encap%unpack(fine_lev_p%q0,fine_lev_p%send)
!           else
!              stop "we should not be here I guess"
!              ! for sequential optimal control, we need to save the Q(m) values for state solution
!              ! and load them when solving the adjoint
!              ! additionally, state solution is needed for objective, adjoint for gradient
!              
!              !print *, 'copying initial/terminal value'
!              fine_lev_p => pf%levels(pf%nlevels)
!              if ((which .eq. 0) .or. (which .eq. 1)) call fine_lev_p%q0%copy(fine_lev_p%qend, 1)
!              if (which .eq. 2) call fine_lev_p%qend%copy(fine_lev_p%q0, 2)
!           end if
!        end if

!       if (pf%state%status == PF_STATUS_PREDICTOR) then
!         !print *, 'pf%state%status == PF_STATUS_PREDICTOR', pf%state%t0, dt, which
        if (predict) then
          !print *, 'calling predictor'
           call pf_predictor_oc(pf, pf%state%t0, dt, pred_flags)
        else
           pf%state%iter = 0
           pf%state%status  = PF_STATUS_ITERATING
           pf%state%pstatus = PF_STATUS_ITERATING
        end if
!       end if    
        call call_hooks(pf, -1, PF_POST_ITERATION)

!       pf%state%iter  = pf%state%iter + 1
! 
! !       exit! just do predictor
!       
!       call start_timer(pf, TITERATION)
!       call call_hooks(pf, -1, PF_PRE_ITERATION)
!       
!       if (pf%state%status /= PF_STATUS_CONVERGED) then
!           fine_lev_p => pf%levels(pf%nlevels)
!           call fine_lev_p%ulevel%sweeper%sweep(pf, pf%nlevels, pf%state%t0, dt, fine_lev_p%nsweeps, which)
!        end if
!       
!       ! check convergence  (should always be not converged)
!       call pf_check_convergence_oc(pf, k,  residual, converged, dir)
!       
!       if (pf%state%step >= pf%state%nsteps) exit
!       
!       if (.not. converged) then
!         !   non-blocking receive at all but the coarsest level
!         do l = 2, pf%nlevels
!           fine_lev_p => pf%levels(l)
!           call pf_post(pf, fine_lev_p, fine_lev_p%index*10000+k, dir)
!         end do
! 
!         if (pf%state%status /= PF_STATUS_CONVERGED) then
!           fine_lev_p => pf%levels(pf%nlevels)
!           call pf_send(pf, fine_lev_p, fine_lev_p%index*10000+k, .false., dir)
!           if (pf%nlevels > 1) then
!             coarse_lev_p => pf%levels(pf%nlevels-1)
!             call restrict_time_space_fas(pf, pf%state%t0, dt, pf%nlevels, which)
!             call save(coarse_lev_p, which)
!           end if             
!         end if
!         
!         call pf_v_cycle_oc(pf, k, pf%state%t0, dt, which)
!         call call_hooks(pf, -1, PF_POST_ITERATION)
!         call end_timer(pf, TITERATION)
!       end if
!     end do  !  Niter loop
! 
!     pf%state%iter = -1
!     call end_timer(pf, TTOTAL)
           
    k = 1 ! always one block
    do j = 1, pf%niters
      call start_timer(pf, TITERATION)
      call call_hooks(pf, -1, PF_PRE_ITERATION)

      pf%state%iter = j

      !  Do a v_cycle
!       call pf_v_cycle(pf, k, pf%state%t0, dt, 1 ,pf%nlevels)
      call pf_v_cycle_oc(pf, j, pf%state%t0, dt, 1, pf%nlevels,  which)

      !  Check for convergence
      call pf_check_convergence_oc(pf, send_tag=1111*k+j, flags=dir)
      if (pf%save_results) pf%results%residuals(pf%state%iter, k, pf%nlevels) = pf%levels(pf%nlevels)%residual

      call call_hooks(pf, -1, PF_POST_ITERATION)
      call end_timer(pf, TITERATION)  
      
      !  If we are converged, exit block
      if (pf%state%status == PF_STATUS_CONVERGED)  exit
    end do  !  Loop over the iteration in this block
    call call_hooks(pf, -1, PF_POST_CONVERGENCE)
    call call_hooks(pf, -1, PF_POST_STEP)
    pf%state%itcnt = pf%state%itcnt + pf%state%iter
    
    if (pf%save_results) call pf%results%dump(pf%results)
    
    call end_timer(pf, TTOTAL)
  end subroutine pf_pfasst_block_oc

  subroutine pf_v_cycle_oc(pf, iteration, t0, dt, level_index_c,level_index_f, flags)
  ! Execute a V-cycle between levels nfine and ncoarse

    type(pf_pfasst_t), intent(inout), target :: pf
    real(pfdp),        intent(in)    :: t0, dt
    integer,           intent(in)    :: iteration
    integer,           intent(in)    :: level_index_c  !! Coarsest level of V-cycle
    integer,           intent(in)    :: level_index_f  !! Finest level of V-cycle
    integer, optional, intent(in)    :: flags

    type(pf_level_t), pointer :: f_lev_p, c_lev_p
    integer :: level_index, j, which, dir

    which = 1
    if(present(flags)) which = flags
    ! send forward by default, even if sweeping on both components; send backwards if sweeping on p only
    dir = 1
    if(which == 2) dir = 2 !

!     !  For a single level, just get new initial conditions and return
!     if (pf%nlevels == 1) then
!        f_lev_p => pf%levels(1)
!        call pf_recv(pf, f_lev_p, f_lev_p%index*10000+iteration, .true., dir)
!        return
!     end if

    !>  Post the nonblocking receives on the all the levels that will be recieving later
    !>    (for single level this will be skipped)
    do level_index = level_index_c+1, level_index_f
       f_lev_p => pf%levels(level_index)
       call pf_post(pf, f_lev_p, f_lev_p%index*10000+iteration, dir)
    end do
    
!     !
!     ! down (fine to coarse)
!     !
!     do level_index = pf%nlevels-1, 2, -1
!       f_lev_p => pf%levels(level_index);
!       c_lev_p => pf%levels(level_index-1)
!       call f_lev_p%ulevel%sweeper%sweep(pf, level_index, t0, dt, f_lev_p%nsweeps, which)
!       call pf_send(pf, f_lev_p, level_index*10000+iteration, .false., dir)
!       call restrict_time_space_fas(pf, t0, dt, level_index, which)
!       call save(c_lev_p, which)
!     end do
    !> move from fine to coarse doing sweeps 
    do level_index = level_index_f, level_index_c+1, -1
       f_lev_p => pf%levels(level_index);
       c_lev_p => pf%levels(level_index-1)
       call f_lev_p%ulevel%sweeper%sweep(pf, level_index, t0, dt, f_lev_p%nsweeps, which)
       call pf_send(pf, f_lev_p, level_index*10000+iteration, .false., dir)
       call restrict_time_space_fas(pf, t0, dt, level_index, which)
       call save(c_lev_p, which)
    end do

!     !
!     ! bottom  (coarsest level)
!     !
!     level_index=1
!     f_lev_p => pf%levels(level_index)
!     if (pf%pipeline_pred) then
!        do j = 1, f_lev_p%nsweeps
!           call pf_recv(pf, f_lev_p, f_lev_p%index*10000+iteration+j, .true., dir)
!           call f_lev_p%ulevel%sweeper%sweep(pf, level_index, t0, dt, 1, which)
!           call pf_send(pf, f_lev_p, f_lev_p%index*10000+iteration+j, .false., dir)
!        end do
!     else
! !       if (which == 0) then
! !         call pf_recv(pf, f_lev_p, f_lev_p%index*10000+iteration, .true., dir)
! !         call f_lev_p%ulevel%sweeper%sweep(pf, level_index, t0, dt, f_lev_p%nsweeps, 1)
! !         call pf_send(pf, f_lev_p, level_index*10000+iteration, .false., dir)
! !         call f_lev_p%ulevel%sweeper%sweep(pf, level_index, t0, dt, f_lev_p%nsweeps, 2) ! this interferes with skipping y sweeps: have to check 
! !                                                                                        ! state residual in case of which==1 in sweeper as well
! !       else
!         call pf_recv(pf, f_lev_p, f_lev_p%index*10000+iteration, .true., dir)
!         call f_lev_p%ulevel%sweeper%sweep(pf, level_index, t0, dt, f_lev_p%nsweeps, which)
!         call pf_send(pf, f_lev_p, level_index*10000+iteration, .false., dir)
! !       endif
!     endif
    ! Do the coarsest level
    level_index=level_index_c
    f_lev_p => pf%levels(level_index)
    if (pf%pipeline_pred) then
       do j = 1, f_lev_p%nsweeps
          call pf_recv(pf, f_lev_p, f_lev_p%index*10000+iteration+j, .true., dir)
          call f_lev_p%ulevel%sweeper%sweep(pf, level_index, t0, dt, 1, which)
          call pf_send(pf, f_lev_p, f_lev_p%index*10000+iteration+j, .false., dir)
       end do
    else
       call pf_recv(pf, f_lev_p, f_lev_p%index*10000+iteration, .true., dir)
       call f_lev_p%ulevel%sweeper%sweep(pf, level_index, t0, dt, f_lev_p%nsweeps, which)
       call pf_send(pf, f_lev_p, level_index*10000+iteration, .false., dir)
    endif
    
!     !
!     ! up  (coarse to fine)
!     !
!     do level_index = 2, pf%nlevels
!       f_lev_p => pf%levels(level_index);
!       c_lev_p => pf%levels(level_index-1)
!       call interpolate_time_space(pf, t0, dt, level_index, c_lev_p%Finterp, which)
!       call pf_recv(pf, f_lev_p, level_index*10000+iteration, .false., dir)
! 
!        if (pf%rank /= 0) then
!           ! interpolate increment to q0 -- the fine initial condition
!           ! needs the same increment that Q(1) got, but applied to the
!           ! new fine initial condition
!           if ((which .eq. 0) .or. (which .eq. 1)) call interpolate_q0(pf, f_lev_p, c_lev_p, 1)
!        end if
!        if (pf%rank /= pf%comm%nproc-1) then
!           if (which .eq. 2) call interpolate_qend(pf, f_lev_p, c_lev_p)
!        end if
! 
!        if (level_index < pf%nlevels) then
!           call call_hooks(pf, level_index, PF_PRE_SWEEP)
!           ! compute residual
!           ! do while residual > tol and j < nswps
!           ! assuming residual computed at end of sweep 
!           call f_lev_p%ulevel%sweeper%sweep(pf, level_index, t0, dt, f_lev_p%nsweeps, which)
!        end if
!     end do
    
    ! Now move coarse to fine interpolating and sweeping
    do level_index = level_index_c+1,level_index_f
       f_lev_p => pf%levels(level_index);
       c_lev_p => pf%levels(level_index-1)
       call interpolate_time_space(pf, t0, dt, level_index, c_lev_p%Finterp, which)
       call pf_recv(pf, f_lev_p, level_index*10000+iteration, .false., dir)
       
       if (pf%rank /= 0) then
          ! interpolate increment to q0 -- the fine initial condition
          ! needs the same increment that Q(1) got, but applied to the
          ! new fine initial condition
          if ((which .eq. 0) .or. (which .eq. 1)) call interpolate_q0(pf, f_lev_p, c_lev_p, 1)
       end if
       if (pf%rank /= pf%comm%nproc-1) then
          if (which .eq. 2)                       call interpolate_qend(pf, f_lev_p, c_lev_p)
       end if

       ! don't sweep on the finest level since that is only done at beginning
       if (level_index < level_index_f) then
          call f_lev_p%ulevel%sweeper%sweep(pf, level_index, t0, dt, f_lev_p%nsweeps, which)
       else  !  compute residual for diagnostics since we didn't sweep
          call pf_residual(pf, f_lev_p, dt, which)
       end if
    end do


  end subroutine pf_v_cycle_oc

end module pf_mod_parallel_oc