Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds extra metadata to fields about their origin #241

Merged
merged 19 commits into from
Dec 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
d9da2ea
add extra metadata to fields
uturuncoglu May 2, 2024
3af40d8
add nested state support
uturuncoglu Aug 19, 2024
fc5246c
Merge remote-tracking branch 'origin/develop' into feature/provider_m…
uturuncoglu Aug 19, 2024
ac01fa3
change the new attribute name based on discussion
uturuncoglu Aug 21, 2024
1e3d81d
Merge remote-tracking branch 'origin/develop' into feature/provider_m…
uturuncoglu Nov 20, 2024
f51a999
add documentation
uturuncoglu Nov 22, 2024
6994904
add doc for field mirroring
uturuncoglu Nov 22, 2024
45bbc28
Merge remote-tracking branch 'origin/develop' into feature/provider_m…
uturuncoglu Dec 4, 2024
5ba55b0
Merge remote-tracking branch 'origin/develop' into feature/provider_m…
uturuncoglu Dec 5, 2024
285ff05
Implement ESMF_InfoLog() for more standard (and convenient) way to log
theurich Dec 6, 2024
e190c6e
Utilize InfoLog() inside StateLog()... at least during development.
theurich Dec 6, 2024
8ee8379
Revert back to version before CompName was added - which is not needed.
theurich Dec 6, 2024
4c8fd1e
Implement Field mirroring with NameSpace transfer.
theurich Dec 6, 2024
e569cec
Add the option to create nested State for NameSpace handling on a
theurich Dec 6, 2024
a5d550d
Turn off InfoLog() under StateLog().
theurich Dec 6, 2024
b4ab77b
Merge pull request #328 from esmf-org/feature/provider_metadata-names…
uturuncoglu Dec 8, 2024
823cf51
Switch to the new "transferAllWithNamespace" option for the
theurich Dec 9, 2024
e7c7b12
Correct casing of "transferAll" option.
theurich Dec 9, 2024
323578c
Correctly case the "transferNone" option in documentation.
theurich Dec 9, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 67 additions & 1 deletion src/Infrastructure/Base/interface/ESMF_Info.F90
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,7 @@ module ESMF_InfoMod
public ESMF_InfoSetDirty
public ESMF_InfoIsSet
public ESMF_InfoIsPresent
public ESMF_InfoLog
public ESMF_InfoPrint
public ESMF_InfoDump
public ESMF_InfoUpdate
Expand Down Expand Up @@ -2495,6 +2496,71 @@ end function ESMF_InfoIsSet

!------------------------------------------------------------------------------

#undef ESMF_METHOD
#define ESMF_METHOD "ESMF_InfoLog()"
!BOP
! !IROUTINE: ESMF_InfoLog - Log contents of an Info object
!
! !INTERFACE:
subroutine ESMF_InfoLog(info, keywordEnforcer, prefix, logMsgFlag, rc)
! !ARGUMENTS:
type(ESMF_Info), intent(in) :: info
type(ESMF_KeywordEnforcer), optional:: keywordEnforcer ! must use keywords below
character(len=*), intent(in), optional :: prefix
type(ESMF_LogMsg_Flag), intent(in), optional :: logMsgFlag
integer, intent(out), optional :: rc
!
! !DESCRIPTION:
! Write information about {\tt info} object to the ESMF default Log.
!
! The arguments are:
! \begin{description}
! \item[info]
! {\tt ESMF\_Info} object logged.
! \item [{[prefix]}]
! String to prefix the log message. Default is no prefix.
! \item [{[logMsgFlag]}]
! Type of log message generated. See section \ref{const:logmsgflag} for
! a list of valid message types. Default is {\tt ESMF\_LOGMSG\_INFO}.
! \item[{[rc]}]
! Return code; equals {\tt ESMF\_SUCCESS} if there are no errors.
! \end{description}
!
!EOP
!------------------------------------------------------------------------------
integer :: localrc
character(:), allocatable :: output, local_preString

! initialize return code; assume routine not implemented
localrc = ESMF_RC_NOT_IMPL
if (present(rc)) rc = ESMF_RC_NOT_IMPL

!TODO: This should really be implemented on the C++ side where we could
!TODO: correctly deal with line breaks and prepend the prefix string on
!TODO: each line, much like for ESMF_HConfigLog().
!TODO: For now implemented quickly on the Fortran side to make available.

if (present(prefix)) then
local_preString = prefix
else
local_preString = ""
endif

output = ESMF_InfoDump(info, indent=2, rc=localrc)
if (ESMF_LogFoundError(localrc, ESMF_ERR_PASSTHRU, &
ESMF_CONTEXT, rcToReturn=rc)) return

call ESMF_LogWrite(local_preString//output, logMsgFlag, rc=localrc)
if (ESMF_LogFoundError(localrc, ESMF_ERR_PASSTHRU, &
ESMF_CONTEXT, rcToReturn=rc)) return

! return successfully
if (present(rc)) rc = ESMF_SUCCESS

end subroutine ESMF_InfoLog

!------------------------------------------------------------------------------

#undef ESMF_METHOD
#define ESMF_METHOD "ESMF_InfoPrint()"
!BOP
Expand Down Expand Up @@ -3929,4 +3995,4 @@ subroutine ESMF_InfoWriteJSON(info, filename, keywordEnforcer, rc)
if (present(rc)) rc = ESMF_SUCCESS
end subroutine ESMF_InfoWriteJSON

end module ESMF_InfoMod !=====================================================
end module ESMF_InfoMod !=====================================================
16 changes: 16 additions & 0 deletions src/Superstructure/State/src/ESMF_StateAPI.cppF90
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ module ESMF_StateAPIMod
use ESMF_IOUtilMod
use ESMF_UtilMod
use ESMF_UtilStringMod
use ESMF_InfoMod

implicit none

Expand Down Expand Up @@ -1852,6 +1853,21 @@ type(ESMF_KeywordEnforcer), optional:: keywordEnforcer ! must use keywords below
ESMF_ERR_PASSTHRU, &
ESMF_CONTEXT, rcToReturn=rc)) return

#if 0
!TODO: need a way to indicate from calling side that info should be logged
block
type(ESMF_Info) :: info
call ESMF_InfoGetFromBase(stateR%statep%base, info=info, rc=localrc)
if (ESMF_LogFoundError(localrc, &
ESMF_ERR_PASSTHRU, &
ESMF_CONTEXT, rcToReturn=rc)) return
call ESMF_InfoLog(info, prefix=prefix, rc=localrc)
if (ESMF_LogFoundError(localrc, &
ESMF_ERR_PASSTHRU, &
ESMF_CONTEXT, rcToReturn=rc)) return
end block
#endif

if (itemCount > 0) then
allocate(itemNameList(itemCount))
allocate(itemTypeList(itemCount))
Expand Down
10 changes: 6 additions & 4 deletions src/addon/NUOPC/doc/NUOPC_FieldMirror.tex
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@

\label{FieldMirror}

In some cases it is helpful for a NUOPC component to automatically mirror or match the set of fields advertised by another component. One purpose of this is to automatically resolve the import data dependencies of a component, by setting up a component that exactly provides all of the needed fields. This is currently used in the NUOPC Component Explorer: when driving a child NUOPC Model with required import fields, the Component Explorer uses the field mirroring capability to advertise in the driver-self export State the exact set of fields advertised in the child NUOPC Model. This ensures that the entire Initialize Phase Sequence will complete (because all dependencies are satisfied) and all phases can be exercised by the Component Explorer.
In some cases it is useful for a NUOPC component to match the set of fields advertised by another component, e.g. in order to connect to every field. NUOPC provides the concept of {\em field mirroring} that allows automatic matching by "mirroring" the fields of another component in their import- or exportState into their own States. One purpose of this is to automatically resolve the import data dependencies of a component, by setting up a component that exactly provides all of the needed fields.

The field mirror capability is also useful with NUOPC Mediators since these components often exactly reflect, in separate States, the sets of fields of each of the connected components. The field mirroring capability, therefore, can be used to ensure that a Mediator is always capable of accepting fields from connected components, and removes the need to specify field lists in multiple places, i.e., both within a set of Model components connected to a Mediator and within the Mediator itself.

To access the field mirror capability, a component sets the {\tt FieldTransferPolicy} attribute during {\tt label\_Advertise}. The attribute is set on the Import- and/or Export- States to trigger field mirroring for each state, respectively. The default value of "TransferNone" indicates that no fields should be mirrored. The other option, "TransferAll", indicates that fields should be mirrored in the State of a connected component.
To access the field mirror capability, a component sets the {\tt FieldTransferPolicy} attribute during {\tt label\_Advertise}. The attribute is set on the Import- and/or Export- States to trigger field mirroring for each state, respectively. The default value of "transferNone" indicates that no fields should be mirrored. The other available options are "transferAll" and "transferAllWithNamespace". Both options mirror transfer all of the fields from all of the connected States into the State that carries the attribute. The "transferAll" option results in flat structure with all of the mirrored fields added directly to the acceptor State. A flat structure like this is typically the preferred situation for an ExportState, where the same fields might be connected to multiple consumer components. The "transferAllWithNamespace" option also mirrors all of the field from the connected State, but creates separate Namespaces for each connection, placing the associated mirrored fields into the respective nested State. A nested structure like this useful for an ImportState where connections are being made with multiple producer components. In this case the consumer component can query the "Namespace" attribute of each nested State to infer the component label of the associated producer components.

Each Connector consider the {\tt FieldTransferPolicy} Attribute on both its import and export States. If {\em both} States have a {\tt FieldTransferPolicy} of "TransferAll", then fields are transferred between the States in both directions (i.e., import to export and export to import). The transfer process works as follows: First, the {\tt TransferOfferGoemObject} attribute is reversed between the providing side and accepting side. Intuitively, if a field from the providing component is to be mirrored and it can provide its own geometric object, then the mirrored field on the accepting side should be set to accept a geometric object. Then, the field to be mirrored is advertised in the accepting State using a call to {\tt NUOPC\_Advertise()} such that the mirrored field shares the same Standard Name.
Each Connector considers the {\tt FieldTransferPolicy} attribute on both its import and export States. Each State that has the {\tt FieldTransferPolicy} attribute set to "transferAll" or "transferAllWithNamespace", will have then fields of the respecive other State mirror transferred. If {\em both} States have the {\tt FieldTransferPolicy} attribute set to trigger the mirror transfer, then fields are mirrored in both directions (i.e. import to export and export to import).

Components have the opportunity, using specialiozation point {\tt label\_ModifyAdvertised}, to modify any of the mirrored Fields in their Import/ExportState. After this the initialization sequence continues as usual. Since fields to be mirrored have been advertised with matching Standard Names, the field pairing algorithm will now match them in the usual way thereby establishing a connection between the original and mirrored fields.
The transfer process works as follows: First, the {\tt TransferOfferGoemObject} attribute is reversed between the providing side and accepting side. This is because if a field from the providing component is to be mirrored and it {\em can} provide its own geometric object, then the mirrored field on the accepting side should be set to {\em accept} a geometric object. The mirrored field is advertised in the accepting State using a call to {\tt NUOPC\_Advertise()} such that the mirrored field shares the same StandardName.

Components have the opportunity to modify or remove any of the mirrored Fields in their Import/ExportState by using the {\tt label\_ModifyAdvertised} specialization point. After this point the initialization sequence continues as usual. Since the mirrored fields have been advertised with matching StandardName attribute, the field pairing algorithm now matches them in the usual manner, thereby establishing a connection between the original and the mirrored fields.
2 changes: 1 addition & 1 deletion src/addon/NUOPC/doc/NUOPC_Field_metadata.tex
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
using the JSON Pointer "/NUOPC/Instance/" prefix followed by the "Attribute name"
as per the table below. E.g. "StandardName" is accessed using {\tt key="/NUOPC/Instance/StandardName"}.

\begin{longtable}{|p{.22\textwidth}|p{.6\textwidth}|p{.2\textwidth}|}
\begin{longtable}{|p{.3\textwidth}|p{.4\textwidth}|p{.3\textwidth}|}
\hline\hline
{\bf Attribute name} & {\bf Definition} & {\bf Controlled vocabulary}\\
\hline\hline
Expand Down
4 changes: 2 additions & 2 deletions src/addon/NUOPC/doc/NUOPC_State_metadata.tex
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
using the JSON Pointer "/NUOPC/Instance/" prefix followed by the "Attribute name"
as per the table below. E.g. "Namespace" is accessed using {\tt key="/NUOPC/Instance/Namespace"}.

\begin{longtable}{|p{.22\textwidth}|p{.6\textwidth}|p{.2\textwidth}|}
\begin{longtable}{|p{.3\textwidth}|p{.4\textwidth}|p{.3\textwidth}|}
\hline\hline
{\bf Attribute name} & {\bf Definition} & {\bf Controlled vocabulary}\\
\hline\hline
{\tt Namespace} & String value holding the namespace of all the objects contained in the State.& {\em no restriction}\\ \hline
{\tt FieldTransferPolicy} & String value indicating to Connector to transfer/mirror Fields. & transferNone,\newline transferAll\\ \hline
{\tt FieldTransferPolicy} & String value indicating to Connector to transfer/mirror Fields. & transferNone,\newline transferAll,\newline transferAllWithNamespace\\ \hline
\hline
\end{longtable}
50 changes: 32 additions & 18 deletions src/addon/NUOPC/src/NUOPC_Base.F90
Original file line number Diff line number Diff line change
Expand Up @@ -154,12 +154,13 @@ module NUOPC_Base
! !IROUTINE: NUOPC_AddNamespace - Add a nested state with Namespace to a State
! !INTERFACE:
subroutine NUOPC_AddNamespace(state, Namespace, nestedStateName, &
nestedState, rc)
nestedState, vm, rc)
! !ARGUMENTS:
type(ESMF_State), intent(inout) :: state
character(len=*), intent(in) :: Namespace
character(len=*), intent(in), optional :: nestedStateName
type(ESMF_State), intent(out), optional :: nestedState
type(ESMF_VM), intent(in), optional :: vm
integer, intent(out), optional :: rc
! !DESCRIPTION:
! Add a Namespace to {\tt state}. Namespaces are implemented via nested
Expand All @@ -178,6 +179,10 @@ subroutine NUOPC_AddNamespace(state, Namespace, nestedStateName, &
! Name of the nested state. Defaults to {\tt Namespace}.
! \item[{[nestedState]}]
! Optional return of the newly created nested state.
! \item[{[vm]}]
! If present, the nested State created to hold the namespace is created on
! the specified {\tt ESMF\_VM} object. The default is to create the nested
! State on the VM of the current component context.
! \item[{[rc]}]
! Return code; equals {\tt ESMF\_SUCCESS} if there are no errors.
! \end{description}
Expand All @@ -189,9 +194,10 @@ subroutine NUOPC_AddNamespace(state, Namespace, nestedStateName, &
type(ESMF_State) :: nestedS
character(len=80) :: nestedSName
type(ESMF_StateIntent_Flag) :: stateIntent

logical :: stateIsCreated

if (present(rc)) rc = ESMF_SUCCESS

call ESMF_StateGet(state, stateIntent=stateIntent, rc=localrc)
if (ESMF_LogFoundError(rcToCheck=localrc, msg=ESMF_LOGERR_PASSTHRU, &
line=__LINE__, file=FILENAME, rcToReturn=rc)) return ! bail out
Expand All @@ -201,31 +207,39 @@ subroutine NUOPC_AddNamespace(state, Namespace, nestedStateName, &
else
nestedSName = trim(Namespace)
endif

nestedS = ESMF_StateCreate(name=nestedSName, stateIntent=stateIntent, &
rc=localrc)
if (ESMF_LogFoundError(rcToCheck=localrc, msg=ESMF_LOGERR_PASSTHRU, &
line=__LINE__, file=FILENAME, rcToReturn=rc)) return ! bail out

call NUOPC_InitAttributes(nestedS, rc=localrc)
vm=vm, rc=localrc)
if (ESMF_LogFoundError(rcToCheck=localrc, msg=ESMF_LOGERR_PASSTHRU, &
line=__LINE__, file=FILENAME, rcToReturn=rc)) return ! bail out

call NUOPC_SetAttribute(nestedS, name="Namespace", &
value=trim(Namespace), rc=localrc)
if (ESMF_LogFoundError(rcToCheck=localrc, msg=ESMF_LOGERR_PASSTHRU, &
line=__LINE__, file=FILENAME, rcToReturn=rc)) return ! bail out

call ESMF_StateAdd(state, (/nestedS/), rc=localrc)
stateIsCreated = ESMF_StateIsCreated(nestedS, rc=rc)
if (ESMF_LogFoundError(rcToCheck=localrc, msg=ESMF_LOGERR_PASSTHRU, &
line=__LINE__, file=FILENAME, rcToReturn=rc)) return ! bail out

if (stateIsCreated) then

call NUOPC_InitAttributes(nestedS, rc=localrc)
if (ESMF_LogFoundError(rcToCheck=localrc, msg=ESMF_LOGERR_PASSTHRU, &
line=__LINE__, file=FILENAME, rcToReturn=rc)) return ! bail out

call NUOPC_SetAttribute(nestedS, name="Namespace", &
value=trim(Namespace), rc=localrc)
if (ESMF_LogFoundError(rcToCheck=localrc, msg=ESMF_LOGERR_PASSTHRU, &
line=__LINE__, file=FILENAME, rcToReturn=rc)) return ! bail out

call ESMF_StateAdd(state, (/nestedS/), rc=localrc)
if (ESMF_LogFoundError(rcToCheck=localrc, msg=ESMF_LOGERR_PASSTHRU, &
line=__LINE__, file=FILENAME, rcToReturn=rc)) return ! bail out

endif

if (present(nestedState)) &
nestedState = nestedS

end subroutine
!---------------------------------------------------------------------
!-----------------------------------------------------------------------------

!-----------------------------------------------------------------------------
!BOP
! !IROUTINE: NUOPC_AddNestedState - Add a nested state to a state with NUOPC attributes
Expand Down
Loading
Loading