Transfer the staged pre-regrid solution onto the regridded mesh through plan, filling the rank-local element range [eFirst,eLast] of the new solution.
uGlobal is optional and supplies old-field data the caller has already assembled; when absent the locally staged copy from StageSolutionForTransfer is used, which is the whole field on a single rank. oldFirst is the global old element index of uGlobal's first element: absent (or 1) means uGlobal is the whole global old field (the Stage-5 v1 allgather path), while the point-to-point migration (v2) passes the window it received together with the window's first global old element index.
[eFirst,eLast] must be this rank's WHOLE new element range, so that eLast-eFirst+1 equals solution%nElem. The portable apply below writes uNew as an exactly-shaped array, so a sub-range of a larger field would place every variable after the first at the wrong stride; the device kernel is indifferent, because it is told the field's element stride separately. Do not rely on that difference - the contract is the whole range on both backends.
This base implementation runs the portable host transfer and uploads the result; a device-resident transfer override is the 3-D analogue of the 2-D Stage 6a optimization.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(DGModel3D_t), | intent(inout), | target | :: | this | ||
| type(TransferPlan3D), | intent(in), | target | :: | plan | ||
| type(Lagrange), | intent(in) | :: | interp | |||
| integer, | intent(in) | :: | eFirst | |||
| integer, | intent(in) | :: | eLast | |||
| real(kind=prec), | intent(in), | optional | contiguous | :: | uGlobal(:,:,:,:,:) | |
| integer, | intent(in), | optional | :: | oldFirst |
subroutine ApplyTransferPlan_DGModel3D_t(this,plan,interp,eFirst,eLast,uGlobal,oldFirst)
!! Transfer the staged pre-regrid solution onto the regridded mesh through plan, filling the
!! rank-local element range [eFirst,eLast] of the new solution.
!!
!! uGlobal is optional and supplies old-field data the caller has already assembled; when
!! absent the locally staged copy from StageSolutionForTransfer is used, which is the whole
!! field on a single rank. oldFirst is the global old element index of uGlobal's first
!! element: absent (or 1) means uGlobal is the whole global old field (the Stage-5 v1
!! allgather path), while the point-to-point migration (v2) passes the window it received
!! together with the window's first global old element index.
!!
!! [eFirst,eLast] must be this rank's WHOLE new element range, so that eLast-eFirst+1 equals
!! solution%nElem. The portable apply below writes uNew as an exactly-shaped array, so a
!! sub-range of a larger field would place every variable after the first at the wrong stride;
!! the device kernel is indifferent, because it is told the field's element stride separately.
!! Do not rely on that difference - the contract is the whole range on both backends.
!!
!! This base implementation runs the portable host transfer and uploads the result; a
!! device-resident transfer override is the 3-D analogue of the 2-D Stage 6a optimization.
implicit none
! target on this: the migrated window is a flat component and the windowed apply is fed
! through a pointer remapped onto it (below), which requires the target attribute here.
class(DGModel3D_t),intent(inout),target :: this
! target: a device override takes c_loc of the plan's arrays to upload them, which requires
! the POINTER or TARGET attribute. Declared here too so any override's characteristics match.
type(TransferPlan3D),intent(in),target :: plan
type(Lagrange),intent(in) :: interp
integer,intent(in) :: eFirst
integer,intent(in) :: eLast
real(prec),intent(in),optional,contiguous :: uGlobal(:,:,:,:,:)
integer,intent(in),optional :: oldFirst
! Local
integer :: o1,o2,Np,perElem
real(prec),pointer :: uWin(:,:,:,:,:)
if(present(uGlobal)) then
o1 = 1
if(present(oldFirst)) o1 = oldFirst
o2 = o1+size(uGlobal,4)-1
call ApplyTransferPlanWindow(plan,interp,this%nvar,uGlobal,o1,o2,eFirst,eLast, &
this%solution%interior)
elseif(this%winStageN > 0) then
if(allocated(this%transferStage)) then
! Two sources for one apply. The GPU override rejects the same combination; keeping the
! guard rails symmetric between the backends is the point, not the reachability - the
! controller never stages and migrates in the same epoch.
print*,__FILE__,':',__LINE__, &
' : Error : ApplyTransferPlan has both a staged local field and a migrated window.'
stop 1
endif
! The multi-rank path: MigrateOldWindow left this rank's window of the old field in
! winStage. This is the same windowed apply as the uGlobal branch above, reading model
! state rather than a caller-supplied array, which is what lets the GPU backend hold the
! window in device memory and override only the migration and the apply.
Np = interp%N+1
perElem = Np*Np*Np
o1 = this%winStageFirst
o2 = o1+this%winStageN-1
uWin(1:Np,1:Np,1:Np,o1:o2,1:this%nvar) => this%winStage(1:perElem*this%winStageN*this%nvar)
call ApplyTransferPlanWindow(plan,interp,this%nvar,uWin,o1,o2,eFirst,eLast, &
this%solution%interior)
uWin => null()
else
if(.not. allocated(this%transferStage)) then
print*,__FILE__,':',__LINE__, &
' : Error : ApplyTransferPlan called without a staged solution or a migrated window.'
stop 1
endif
call ApplyTransferPlanRange(plan,interp,this%nvar,this%transferStage,eFirst,eLast, &
this%solution%interior)
endif
call this%solution%UpdateDevice()
if(allocated(this%transferStage)) deallocate(this%transferStage)
! The window buffer is retained (grow-only) but its marker is cleared, so a second apply
! without a fresh migration fails the guard rather than reusing a stale window.
this%winStageN = 0
endsubroutine ApplyTransferPlan_DGModel3D_t