GPU path: one device block per element computes the modal transform, the raw smoothness ratio and the gate energy; those are copied back to the host, where the shared second phase applies the amplitude gate, the log10 and the thresholds.
The second phase is host-side (rather than a second device kernel) because the amplitude gate needs a field-wide energy scale - a reduction, and an MPI collective when the mesh is decomposed - that an element-local device block cannot supply. Running it through the same FinalizeIndicator the portable backend uses also makes the flags identical on CPU and GPU by construction. The extra traffic is one per-element array each way, once per adaptation epoch; nothing is added to the time-stepping loop.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(RefinementIndicator3D), | intent(inout) | :: | this | |||
| class(Scalar3D), | intent(in) | :: | solution | |||
| integer, | intent(in) | :: | ivar | |||
| integer, | intent(in), | optional | :: | comm | ||
| real(kind=prec), | intent(in), | optional | :: | gate(:) |
subroutine Estimate_RefinementIndicator3D(this,solution,ivar,comm,gate)
!! GPU path: one device block per element computes the modal transform, the raw smoothness
!! ratio and the gate energy; those are copied back to the host, where the shared second phase
!! applies the amplitude gate, the log10 and the thresholds.
!!
!! The second phase is host-side (rather than a second device kernel) because the amplitude
!! gate needs a field-wide energy scale - a reduction, and an MPI collective when the mesh is
!! decomposed - that an element-local device block cannot supply. Running it through the same
!! FinalizeIndicator the portable backend uses also makes the flags identical on CPU and GPU
!! by construction. The extra traffic is one per-element array each way, once per adaptation
!! epoch; nothing is added to the time-stepping loop.
implicit none
class(RefinementIndicator3D),intent(inout) :: this
class(Scalar3D),intent(in) :: solution
integer,intent(in) :: ivar
integer,intent(in),optional :: comm
real(prec),intent(in),optional :: gate(:)
! Local
integer :: iel
real(prec) :: energyScale
call CheckEstimateArguments(this,solution,ivar,gate)
call ResolveEnergyWeights(this,solution%nVar,ivar)
if(this%nVarWeights_gpu /= this%nVarWeights) then
if(c_associated(this%energyWeight_gpu)) call gpuCheck(hipFree(this%energyWeight_gpu))
call gpuCheck(hipMalloc(this%energyWeight_gpu,sizeof(this%energyWeight)))
this%nVarWeights_gpu = this%nVarWeights
endif
call gpuCheck(hipMemcpy(this%energyWeight_gpu,c_loc(this%energyWeight), &
sizeof(this%energyWeight),hipMemcpyHostToDevice))
! First phase on the device. indicator_gpu transiently holds the RAW ratio S_e, not log10(S_e).
call RefinementIndicator_3D_gpu(this%Pmodal_gpu,solution%interior_gpu, &
this%energyWeight_gpu, &
this%indicator_gpu,this%gate_gpu, &
this%N,solution%nVar,ivar,this%nElem)
call this%UpdateHost()
if(present(gate)) then
do iel = 1,this%nElem
this%gate(iel) = gate(iel)
enddo
endif
call ResolveEnergyScale(this,energyScale,comm)
call FinalizeIndicator(this,energyScale)
! Re-establish the device mirrors of indicator/flag/gate, which the host phase has just
! rewritten, so the device copies are never left stale.
call this%UpdateDevice()
endsubroutine Estimate_RefinementIndicator3D