subroutine ESMF_StateReconcile(state, keywordEnforcer, vm, checkflag, rc)
!
! !ARGUMENTS:
type(ESMF_State), intent(inout) :: state
type(ESMF_KeywordEnforcer), optional:: keywordEnforcer ! must use keywords below
type(ESMF_VM), intent(in), optional :: vm
logical, intent(in), optional :: checkflag
integer, intent(out), optional :: rc
!
! !DESCRIPTION:
!
! Must be called for any {\tt ESMF\_State} which contains ESMF objects
! that have not been created on all the PETs of {\tt vm}.
! For example, if a coupler component is operating on objects
! which were created by another component that ran on only a subset
! of the coupler PETs, the coupler must make this call first
! before operating with any of the objects held by the {\tt ESMF\_State}.
! After calling {\tt ESMF\_StateReconcile()} all PETs will have
! a common view of all objects contained in this {\tt ESMF\_State}.
!
! The Info metadata keys of reconciled objects are also reconciled. This
! means that after reconciliation, every object in {\tt state} holds a
! consistent set of Info {\em keys} across all the PETs of {\tt vm}.
! Notice however, that no guarantee is made with respect to the Info
! {\em value} that is associated with reconciled Info keys.
!
! The Info metadata keys of the {\tt state} object itself are also reconciled
! for most common cases. The only exception is for the case where Info keys
! were added to {\tt state} under a component that is executing on a subset
! of PETs, and no actual object created under such component was added to
! {\tt state}.
!
! This call is collective across the specified VM.
!
! The arguments are:
! \begin{description}
! \item[state]
! {\tt ESMF\_State} to reconcile.
! \item[{[vm]}]
! {\tt ESMF\_VM} across which to reconcile. The default is the current VM.
! \item [{[checkflag]}]
! If set to {\tt .TRUE.} the reconciled State object is checked for
! consistency across PETs before returning. Any detected issues are
! indicated in {\tt rc}. Set {\tt checkflag} to {\tt .FALSE.} in order
! to achieve highest performance. The default is {\tt .FALSE.}.
! \item[{[rc]}]
! Return code; equals {\tt ESMF\_SUCCESS} if there are no errors.
! \end{description}
!EOP
integer :: localrc
type(ESMF_VM) :: localvm
logical :: isNoop, isFlag, localCheckFlag
logical, parameter :: profile = .false.
! check input variables
ESMF_INIT_CHECK_DEEP(ESMF_StateGetInit,state,rc)
ESMF_INIT_CHECK_DEEP(ESMF_VMGetInit,vm,rc)
! Initialize return code; assume routine not implemented
if (present(rc)) rc = ESMF_RC_NOT_IMPL
localrc = ESMF_RC_NOT_IMPL
localCheckFlag = .false. ! default
#if 0
! Activate this when working on StateReoncile, so default is to check result
localCheckFlag = .true. ! force checking by default
#endif
if (present(checkFlag)) localCheckFlag = checkFlag
if (present (vm)) then
localvm = vm
else
call ESMF_VMGetCurrent(vm=localvm, rc=localrc)
if (ESMF_LogFoundError(localrc, ESMF_ERR_PASSTHRU, ESMF_CONTEXT, &
rcToReturn=rc)) return
end if
#ifdef RECONCILE_LOG_on
block
character(ESMF_MAXSTR) :: stateName
call ESMF_StateGet(state, name=stateName, rc=localrc)
if (ESMF_LogFoundError(localrc, ESMF_ERR_PASSTHRU, ESMF_CONTEXT, &
rcToReturn=rc)) return
call ESMF_LogWrite("StateReconcile() for State: "//trim(stateName), &
ESMF_LOGMSG_DEBUG, rc=localrc)
if (ESMF_LogFoundError(localrc, ESMF_ERR_PASSTHRU, ESMF_CONTEXT, &
rcToReturn=rc)) return
end block
#endif
#if 0
block
type(ESMF_InfoDescribe) :: idesc
! Log a JSON State representation -----------------------------------------
call idesc%Initialize(createInfo=.true., addObjectInfo=.true., rc=localrc)
if (ESMF_LogFoundError(localrc, ESMF_ERR_PASSTHRU, ESMF_CONTEXT, rcToReturn=rc)) return
call idesc%Update(state, "", rc=localrc)
if (ESMF_LogFoundError(localrc, ESMF_ERR_PASSTHRU, ESMF_CONTEXT, rcToReturn=rc)) return
call ESMF_LogWrite("state_json_before_reconcile="// &
ESMF_InfoDump(idesc%info, indent=2), ESMF_LOGMSG_DEBUG, rc=localrc)
if (ESMF_LogFoundError(localrc, ESMF_ERR_PASSTHRU, ESMF_CONTEXT, rcToReturn=rc)) return
call idesc%Destroy(rc=localrc)
if (ESMF_LogFoundError(localrc, ESMF_ERR_PASSTHRU, ESMF_CONTEXT, rcToReturn=rc)) return
end block
#endif
#if 0
! cleaner timings below, eliminating issue due to different times PETs enter
! BUT: only enable this for testing purposes
call ESMF_VMBarrier(localvm, rc=localrc)
#endif
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
! Determine whether there is anything to be Reconciled at all. !
! If not then return as quickly as possible. !
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
if (profile) then
call ESMF_TraceRegionEnter("ESMF_StateReconcileIsNoop", rc=localrc)
if (ESMF_LogFoundError(localrc, ESMF_ERR_PASSTHRU, ESMF_CONTEXT, &
rcToReturn=rc)) return
endif
call ESMF_StateReconcileIsNoop(state, vm=localvm, isNoop=isNoop, rc=localrc)
if (ESMF_LogFoundError(localrc, ESMF_ERR_PASSTHRU, ESMF_CONTEXT, &
rcToReturn=rc)) return
if (profile) then
call ESMF_TraceRegionExit("ESMF_StateReconcileIsNoop", rc=localrc)
if (ESMF_LogFoundError(localrc, ESMF_ERR_PASSTHRU, ESMF_CONTEXT, &
rcToReturn=rc)) return
endif
#ifdef RECONCILE_LOG_on
block
character(160):: msgStr
write(msgStr,*) "StateReconcile() isNoop: ", isNoop
call ESMF_LogWrite(msgStr, ESMF_LOGMSG_DEBUG, rc=localrc)
if (ESMF_LogFoundError(localrc, ESMF_ERR_PASSTHRU, ESMF_CONTEXT, &
rcToReturn=rc)) return
end block
#endif
if (isNoop) then
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
! Quick exit for Noop, but still must reconcile State level Info !
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!TODO: Only need to do the State level Info reconcile here for a very
!TODO: specific Noop case where no objects were added under a sub context,
!TODO: but top level State Attributes were set under the sub context.
!TODO: This is tricky to detect here, and to figure out which PET(s)
!TODO: to use for correct rootPets!!
!TODO: Luckily this is a very special edge case... and for now get away
!TODO: not handling it... but it could some day cause an issue!!!
else
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
! Go on to reconcile the State !
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
if (profile) then
call ESMF_TraceRegionEnter("ESMF_StateReconcile_driver", rc=localrc)
if (ESMF_LogFoundError(localrc, ESMF_ERR_PASSTHRU, ESMF_CONTEXT, &
rcToReturn=rc)) return
endif
call ESMF_StateReconcile_driver(state, vm=localvm, rc=localrc)
if (ESMF_LogFoundError(localrc, ESMF_ERR_PASSTHRU, ESMF_CONTEXT, &
rcToReturn=rc)) return
if (profile) then
call ESMF_TraceRegionExit("ESMF_StateReconcile_driver", rc=localrc)
if (ESMF_LogFoundError(localrc, ESMF_ERR_PASSTHRU, ESMF_CONTEXT, &
rcToReturn=rc)) return
endif
endif
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
! Conditionally check the reconciled State for consistency across PETs !
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
if (localCheckFlag) then
if (profile) then
call ESMF_TraceRegionEnter("JSON cross PET check", rc=localrc)
if (ESMF_LogFoundError(localrc, ESMF_ERR_PASSTHRU, ESMF_CONTEXT, &
rcToReturn=rc)) return
endif
block
type(ESMF_InfoDescribe) :: idesc
character(:), allocatable :: jsonStr, testStr
integer :: size(1), localPet
! Log a JSON State representation -----------------------------------------
call idesc%Initialize(createInfo=.true., addObjectInfo=.true., rc=localrc)
if (ESMF_LogFoundError(localrc, ESMF_ERR_PASSTHRU, ESMF_CONTEXT, rcToReturn=rc)) return
call idesc%Update(state, "", rc=localrc)
if (ESMF_LogFoundError(localrc, ESMF_ERR_PASSTHRU, ESMF_CONTEXT, rcToReturn=rc)) return
jsonStr = "state_json_after_reassemble="//&
ESMF_InfoDump(idesc%info, indent=2, rc=localrc)
if (ESMF_LogFoundError(localrc, ESMF_ERR_PASSTHRU, ESMF_CONTEXT, rcToReturn=rc)) return
#ifdef RECONCILE_LOG_on
call ESMF_LogWrite(jsonStr, ESMF_LOGMSG_DEBUG, rc=localrc)
if (ESMF_LogFoundError(localrc, ESMF_ERR_PASSTHRU, ESMF_CONTEXT, rcToReturn=rc)) return
#endif
call idesc%Destroy(rc=localrc)
if (ESMF_LogFoundError(localrc, ESMF_ERR_PASSTHRU, ESMF_CONTEXT, rcToReturn=rc)) return
#if 1
! check match across all PETs of VM
size(1) = len(jsonStr)
call ESMF_VMBroadcast(localvm, size, count=1, rootPet=0, rc=localrc)
if (ESMF_LogFoundError(localrc, ESMF_ERR_PASSTHRU, ESMF_CONTEXT, rcToReturn=rc)) return
call ESMF_VMGet(localvm, localPet=localPet, rc=localrc)
if (ESMF_LogFoundError(localrc, ESMF_ERR_PASSTHRU, ESMF_CONTEXT, rcToReturn=rc)) return
if (localPet==0) then
call ESMF_VMBroadcast(localvm, jsonStr, count=size(1), rootPet=0, rc=localrc)
if (ESMF_LogFoundError(localrc, ESMF_ERR_PASSTHRU, ESMF_CONTEXT, rcToReturn=rc)) return
else
allocate(character(len=size(1))::testStr)
call ESMF_VMBroadcast(localvm, testStr, count=size(1), rootPet=0, rc=localrc)
if (ESMF_LogFoundError(localrc, ESMF_ERR_PASSTHRU, ESMF_CONTEXT, rcToReturn=rc)) return
if (testStr/=jsonStr) then
! not a perfect match -> see if the differences are acceptable
! these are differences in the values of attributes, which show up in
! the bundled esmf and nuopc test cases... these diffs are begnin.
isFlag = ESMF_UtilStringDiffMatch(jsonStr, testStr, &
minusStringList = ["None ", &
"All ", &
"1 ", &
"2 ", &
" ", &
" ", &
"M ", &
"DEF ", &
"UL ", &
" ", &
"driverChild " &
], &
plusStringList = ["All ", &
"None ", &
"2 ", &
"1 ", &
"DEF ", &
"UL ", &
" ", &
" ", &
" ", &
"M ", &
"DEFAULT" &
], rc=localrc)
if (ESMF_LogFoundError(localrc, ESMF_ERR_PASSTHRU, ESMF_CONTEXT, &
rcToReturn=rc)) return
if (.not.isFlag) then
! found unexpected/unacceptable differences
call ESMF_LogSetError(ESMF_RC_INTNRL_INCONS, &
msg="StateReconcile() failed!! Not all PETs hold same content!!", &
ESMF_CONTEXT, rcToReturn=rc)
return
endif
endif
endif
#endif
end block
if (profile) then
call ESMF_TraceRegionExit("JSON cross PET check", rc=localrc)
if (ESMF_LogFoundError(localrc, ESMF_ERR_PASSTHRU, ESMF_CONTEXT, &
rcToReturn=rc)) return
endif
endif
if (present(rc)) rc = ESMF_SUCCESS
end subroutine ESMF_StateReconcile