From 907001b488a226f88486216c1f82fd23011f73b3 Mon Sep 17 00:00:00 2001 From: Jules Kouatchou Date: Fri, 10 May 2024 11:06:25 -0400 Subject: [PATCH] Create the folder ComponentSpecParser that has the submodules for mapl3g_ComponentSpecParser --- generic3g/CMakeLists.txt | 1 + generic3g/ComponentSpecParser/CMakeLists.txt | 10 + generic3g/ComponentSpecParser/parse_child.F90 | 64 ++++ .../ComponentSpecParser/parse_children.F90 | 46 +++ .../parse_component_spec.F90 | 31 ++ .../ComponentSpecParser/parse_connections.F90 | 143 ++++++++ .../parse_geometry_spec.F90 | 85 +++++ .../ComponentSpecParser/parse_setservices.F90 | 31 ++ .../ComponentSpecParser/parse_var_specs.F90 | 323 ++++++++++++++++++ 9 files changed, 734 insertions(+) create mode 100644 generic3g/ComponentSpecParser/CMakeLists.txt create mode 100644 generic3g/ComponentSpecParser/parse_child.F90 create mode 100644 generic3g/ComponentSpecParser/parse_children.F90 create mode 100644 generic3g/ComponentSpecParser/parse_component_spec.F90 create mode 100644 generic3g/ComponentSpecParser/parse_connections.F90 create mode 100644 generic3g/ComponentSpecParser/parse_geometry_spec.F90 create mode 100644 generic3g/ComponentSpecParser/parse_setservices.F90 create mode 100644 generic3g/ComponentSpecParser/parse_var_specs.F90 diff --git a/generic3g/CMakeLists.txt b/generic3g/CMakeLists.txt index 7abab8bfac91..b635ee93bcaf 100644 --- a/generic3g/CMakeLists.txt +++ b/generic3g/CMakeLists.txt @@ -64,6 +64,7 @@ add_subdirectory(registry) add_subdirectory(connection) add_subdirectory(actions) add_subdirectory(couplers) +add_subdirectory(ComponentSpecParser) target_include_directories (${this} PUBLIC $) diff --git a/generic3g/ComponentSpecParser/CMakeLists.txt b/generic3g/ComponentSpecParser/CMakeLists.txt new file mode 100644 index 000000000000..cbc48f31b2d9 --- /dev/null +++ b/generic3g/ComponentSpecParser/CMakeLists.txt @@ -0,0 +1,10 @@ +target_sources(MAPL.generic3g PRIVATE + + parse_child.F90 + parse_children.F90 + parse_connections.F90 + parse_var_specs.F90 + parse_geometry_spec.F90 + parse_component_spec.F90 + parse_setservices.F90 +) diff --git a/generic3g/ComponentSpecParser/parse_child.F90 b/generic3g/ComponentSpecParser/parse_child.F90 new file mode 100644 index 000000000000..6373259e552d --- /dev/null +++ b/generic3g/ComponentSpecParser/parse_child.F90 @@ -0,0 +1,64 @@ +#include "MAPL_ErrLog.h" + +submodule (mapl3g_ComponentSpecParser) parse_child_smod + +contains + + module function parse_child(hconfig, rc) result(child) + type(ChildSpec) :: child + type(ESMF_HConfig), intent(in) :: hconfig + integer, optional, intent(out) :: rc + + integer :: status + class(AbstractUserSetServices), allocatable :: setservices + + character(*), parameter :: dso_keys(*) = [character(len=9) :: 'dso', 'DSO', 'sharedObj', 'sharedobj'] + character(*), parameter :: userProcedure_keys(*) = [character(len=10) :: 'SetServices', 'setServices', 'setservices'] + integer :: i + character(:), allocatable :: dso_key, userProcedure_key, try_key + logical :: dso_found, userProcedure_found + logical :: has_key + logical :: has_config_file + character(:), allocatable :: sharedObj, userProcedure, config_file + + + dso_found = .false. + ! Ensure precisely one name is used for dso + do i = 1, size(dso_keys) + try_key = trim(dso_keys(i)) + has_key = ESMF_HconfigIsDefined(hconfig, keyString=try_key, _RC) + if (has_key) then + _ASSERT(.not. dso_found, 'multiple specifications for dso in hconfig for child') + dso_found = .true. + dso_key = try_key + end if + end do + _ASSERT(dso_found, 'Must specify a dso for hconfig of child') + sharedObj = ESMF_HconfigAsString(hconfig, keyString=dso_key, _RC) + + userProcedure_found = .false. + do i = 1, size(userProcedure_keys) + try_key = userProcedure_keys(i) + if (ESMF_HconfigIsDefined(hconfig, keyString=try_key)) then + _ASSERT(.not. userProcedure_found, 'multiple specifications for dso in hconfig for child') + userProcedure_found = .true. + userProcedure_key = try_key + end if + end do + userProcedure = 'setservices_' + if (userProcedure_found) then + userProcedure = ESMF_HconfigAsString(hconfig, keyString=userProcedure_key,_RC) + end if + + has_config_file = ESMF_HconfigIsDefined(hconfig, keyString='config_file', _RC) + if (has_config_file) then + config_file = ESMF_HconfigAsString(hconfig, keyString='config_file',_RC) + end if + + setservices = user_setservices(sharedObj, userProcedure) + child = ChildSpec(setservices, config_file=config_file) + + _RETURN(_SUCCESS) + end function parse_child + +end submodule parse_child_smod diff --git a/generic3g/ComponentSpecParser/parse_children.F90 b/generic3g/ComponentSpecParser/parse_children.F90 new file mode 100644 index 000000000000..9ae41f2e61ec --- /dev/null +++ b/generic3g/ComponentSpecParser/parse_children.F90 @@ -0,0 +1,46 @@ +#include "MAPL_ErrLog.h" + +submodule (mapl3g_ComponentSpecParser) parse_children_smod + +contains + + module function parse_children(hconfig, rc) result(children) + type(ChildSpecMap) :: children + type(ESMF_HConfig), intent(in) :: hconfig + integer, optional, intent(out) :: rc + + integer :: status + logical :: has_children + logical :: is_map + type(ESMF_HConfig) :: children_cfg, child_cfg + type(ESMF_HConfigIter) :: iter, iter_begin, iter_end + type(ChildSpec) :: child_spec + character(:), allocatable :: child_name + + + has_children = ESMF_HConfigIsDefined(hconfig, keyString=COMPONENT_CHILDREN_SECTION, _RC) + _RETURN_UNLESS(has_children) + + children_cfg = ESMF_HConfigCreateAt(hconfig, keyString=COMPONENT_CHILDREN_SECTION, _RC) + is_map = ESMF_HConfigIsMap(children_cfg, _RC) + + _ASSERT(is_map, 'children spec must be mapping') + + iter_begin = ESMF_HCOnfigIterBegin(children_cfg, _RC) + iter_end = ESMF_HConfigIterEnd(children_cfg, _RC) + iter = iter_begin + do while (ESMF_HConfigIterLoop(iter, iter_begin, iter_end)) + child_name = ESMF_HConfigAsStringMapKey(iter, _RC) + child_cfg = ESMF_HConfigCreateAtMapVal(iter, _RC) + child_spec = parse_child(child_cfg, _RC) + call children%insert(child_name, child_spec) + call ESMF_HConfigDestroy(child_cfg, _RC) + end do + + call ESMF_HConfigDestroy(children_cfg, _RC) + + _RETURN(_SUCCESS) + end function parse_children + +end submodule parse_children_smod + diff --git a/generic3g/ComponentSpecParser/parse_component_spec.F90 b/generic3g/ComponentSpecParser/parse_component_spec.F90 new file mode 100644 index 000000000000..1a3f7880c0f8 --- /dev/null +++ b/generic3g/ComponentSpecParser/parse_component_spec.F90 @@ -0,0 +1,31 @@ +#include "MAPL_ErrLog.h" + +submodule (mapl3g_ComponentSpecParser) parse_component_spec_smod + +contains + + module function parse_component_spec(hconfig, rc) result(spec) + type(ComponentSpec) :: spec + type(ESMF_HConfig), target, intent(inout) :: hconfig + integer, optional, intent(out) :: rc + + integer :: status + logical :: has_mapl_section + type(ESMF_HConfig) :: mapl_cfg + + has_mapl_section = ESMF_HConfigIsDefined(hconfig, keyString=MAPL_SECTION, _RC) + _RETURN_UNLESS(has_mapl_section) + mapl_cfg = ESMF_HConfigCreateAt(hconfig, keyString=MAPL_SECTION, _RC) + + spec%geometry_spec = parse_geometry_spec(mapl_cfg, _RC) + spec%var_specs = parse_var_specs(mapl_cfg, _RC) + spec%connections = parse_connections(mapl_cfg, _RC) + spec%children = parse_children(mapl_cfg, _RC) + + call ESMF_HConfigDestroy(mapl_cfg, _RC) + + _RETURN(_SUCCESS) + end function parse_component_spec + +end submodule parse_component_spec_smod + diff --git a/generic3g/ComponentSpecParser/parse_connections.F90 b/generic3g/ComponentSpecParser/parse_connections.F90 new file mode 100644 index 000000000000..249049c8fc67 --- /dev/null +++ b/generic3g/ComponentSpecParser/parse_connections.F90 @@ -0,0 +1,143 @@ +#include "MAPL_ErrLog.h" + +submodule (mapl3g_ComponentSpecParser) parse_connections_smod + +contains + + module function parse_connections(hconfig, rc) result(connections) + type(ConnectionVector) :: connections + type(ESMF_HConfig), optional, intent(in) :: hconfig + integer, optional, intent(out) :: rc + + type(ESMF_HConfig) :: conn_specs, conn_spec + class(Connection), allocatable :: conn + integer :: status, i, num_specs + logical :: has_connections + + has_connections = ESMF_HConfigIsDefined(hconfig,keyString=COMPONENT_CONNECTIONS_SECTION,_RC) + _RETURN_UNLESS(has_connections) + + conn_specs = ESMF_HConfigCreateAt(hconfig, keyString=COMPONENT_CONNECTIONS_SECTION, _RC) + + num_specs = ESMF_HConfigGetSize(conn_specs, _RC) + do i = 1, num_specs + conn_spec = ESMF_HConfigCreateAt(conn_specs, index=i, _RC) + allocate(conn, source=parse_connection(conn_spec, rc=status)); _VERIFY(status) + call connections%push_back(conn) + deallocate(conn) + enddo + + _RETURN(_SUCCESS) + + contains + + function parse_connection(config, rc) result(conn) + class(Connection), allocatable :: conn + type(ESMF_HConfig), optional, intent(in) :: config + integer, optional, intent(out) :: rc + + integer :: status + character(:), allocatable :: src_name, dst_name + character(:), allocatable :: src_comp, dst_comp + character(:), allocatable :: src_intent, dst_intent + + call get_comps(config, src_comp, dst_comp, _RC) + + if (ESMF_HConfigIsDefined(config,keyString='all_unsatisfied')) then + conn = MatchConnection( & + ConnectionPt(src_comp, VirtualConnectionPt(state_intent='export', short_name='^.*$')), & + ConnectionPt(dst_comp, VirtualConnectionPt(state_intent='import', short_name='^.*$')) & + ) + _RETURN(_SUCCESS) + end if + + call get_names(config, src_name, dst_name, _RC) + call get_intents(config, src_intent, dst_intent, _RC) + + associate ( & + src_pt => VirtualConnectionPt(state_intent=src_intent, short_name=src_name), & + dst_pt => VirtualConnectionPt(state_intent=dst_intent, short_name=dst_name) ) + + if (dst_intent == 'export') then + conn = ReexportConnection( & + ConnectionPt(src_comp, src_pt), & + ConnectionPt(dst_comp, dst_pt)) + else + conn = SimpleConnection( & + ConnectionPt(src_comp, src_pt), & + ConnectionPt(dst_comp, dst_pt)) + end if + + end associate + + _RETURN(_SUCCESS) + end function parse_connection + + subroutine get_names(config, src_name, dst_name, rc) + type(ESMF_HConfig), intent(in) :: config + character(:), allocatable :: src_name + character(:), allocatable :: dst_name + integer, optional, intent(out) :: rc + + integer :: status + + associate (provides_names => & + ESMF_HConfigIsDefined(config,keyString='name') .or. & + (ESMF_HConfigIsDefined(config,keyString='src_name') .and. ESMF_HConfigIsDefined(config,keyString='dst_name')) & + ) + _ASSERT(provides_names, "Must specify 'name' or 'src_name' .and. 'dst_name' in connection.") + end associate + + if (ESMF_HConfigIsDefined(Config,keystring='name')) then ! replicate for src and dst + src_name = ESMF_HConfigAsString(config,keyString='name',_RC) + dst_name = src_name + _RETURN(_SUCCESS) + end if + + src_name = ESMF_HConfigAsString(config,keyString='src_name',_RC) + dst_name = ESMF_HConfigAsString(config,keyString='dst_name',_RC) + + _RETURN(_SUCCESS) + end subroutine get_names + + subroutine get_comps(config, src_comp, dst_comp, rc) + type(ESMF_HConfig), intent(in) :: config + character(:), allocatable :: src_comp + character(:), allocatable :: dst_comp + integer, optional, intent(out) :: rc + + integer :: status + + _ASSERT(ESMF_HConfigIsDefined(config,keyString='src_comp'), 'Connection must specify a src component') + _ASSERT(ESMF_HConfigIsDefined(config,keyString='dst_comp'), 'Connection must specify a dst component') + src_comp = ESMF_HConfigAsString(config,keyString='src_comp',_RC) + dst_comp = ESMF_HConfigAsString(config,keyString='dst_comp',_RC) + _RETURN(_SUCCESS) + end subroutine get_comps + + subroutine get_intents(config, src_intent, dst_intent, rc) + type(ESMF_HConfig), intent(in) :: config + character(:), allocatable :: src_intent + character(:), allocatable :: dst_intent + integer, optional, intent(out) :: rc + + integer :: status + + ! defaults + src_intent = 'export' + dst_intent = 'import' + + if (ESMF_HConfigIsDefined(config,keyString='src_intent')) then + src_intent = ESMF_HConfigAsString(config,keyString='src_intent',_RC) + end if + if (ESMF_HConfigIsDefined(config,keyString='dst_intent')) then + dst_intent = ESMF_HConfigAsString(config,keyString='dst_intent',_RC) + end if + + _RETURN(_SUCCESS) + end subroutine get_intents + + end function parse_connections + +end submodule parse_connections_smod + diff --git a/generic3g/ComponentSpecParser/parse_geometry_spec.F90 b/generic3g/ComponentSpecParser/parse_geometry_spec.F90 new file mode 100644 index 000000000000..2ea2371bfedd --- /dev/null +++ b/generic3g/ComponentSpecParser/parse_geometry_spec.F90 @@ -0,0 +1,85 @@ +#include "MAPL_ErrLog.h" + +submodule (mapl3g_ComponentSpecParser) parse_geometry_spec_smod + +contains + + ! Geom subcfg is passed raw to the GeomManager layer. So little + ! processing is needed here. + module function parse_geometry_spec(mapl_cfg, rc) result(geometry_spec) + type(GeometrySpec) :: geometry_spec + type(ESMF_HConfig), intent(in) :: mapl_cfg + integer, optional, intent(out) :: rc + + integer :: status + logical :: has_geometry_section + logical :: has_esmf_geom + logical :: has_geometry_kind + logical :: has_geometry_provider + character(:), allocatable :: geometry_kind_str + character(:), allocatable :: provider + integer :: geometry_kind + type(ESMF_HConfig) :: geometry_cfg + type(ESMF_HConfig) :: esmf_geom_cfg + type(GeomManager), pointer :: geom_mgr + class(GeomSpec), allocatable :: geom_spec + + has_geometry_section = ESMF_HConfigIsDefined(mapl_cfg,keyString=COMPONENT_GEOMETRY_SECTION, _RC) + _RETURN_UNLESS(has_geometry_section) + + geometry_cfg = ESMF_HConfigCreateAt(mapl_cfg, keyString=COMPONENT_GEOMETRY_SECTION, _RC) + + has_geometry_kind = ESMF_HConfigIsDefined(geometry_cfg, keyString='kind', _RC) + has_esmf_geom = ESMF_HConfigIsDefined(geometry_cfg, keyString=COMPONENT_ESMF_GEOM_SECTION, _RC) + + if (.not. (has_geometry_kind .or. has_esmf_geom)) then ! default + geometry_spec = GeometrySpec(GEOMETRY_FROM_PARENT) + call ESMF_HConfigDestroy(geometry_cfg, _RC) + _RETURN(_SUCCESS) + end if + + if (has_geometry_kind) then + geometry_kind_str = ESMF_HConfigAsString(geometry_cfg, keyString='kind', _RC) + end if + + if (has_esmf_geom) then + esmf_geom_cfg = ESMF_HConfigCreateAt(geometry_cfg, keyString=COMPONENT_ESMF_GEOM_SECTION, _RC) + end if + + if (has_geometry_kind .and. has_esmf_geom) then + _ASSERT(geometry_kind_str == 'provider', 'Geometry kind must be provider when using ESMF geom config.') + end if + + if (has_esmf_geom) then + geom_mgr => get_geom_manager() + allocate(geom_spec, source=geom_mgr%make_geom_spec(esmf_geom_cfg, rc=status)) + _VERIFY(status) + call ESMF_HConfigDestroy(geometry_cfg, _RC) + geometry_spec = GeometrySpec(geom_spec) + _RETURN(_SUCCESS) + end if + + if (has_geometry_kind) then + select case (ESMF_UtilStringLowerCase(geometry_kind_str)) + case ('none') + geometry_spec = GeometrySpec(GEOMETRY_NONE) + case ('provider') + geometry_spec = GeometrySpec(GEOMETRY_PROVIDER) + case ('from_parent') + geometry_spec = GeometrySpec(GEOMETRY_FROM_PARENT) + case ('from_child') + has_geometry_provider = ESMF_HConfigIsDefined(geometry_cfg, keystring='provider', _RC) + _ASSERT(has_geometry_provider, 'Must name provider when using GEOMETRY_FROM_CHILD') + provider = ESMF_HConfigAsString(geometry_cfg, keystring='provider', _RC) + geometry_spec = GeometrySpec(provider) + case default + _FAIL('Invalid geometry kind') + end select + call ESMF_HConfigDestroy(geometry_cfg, _RC) + end if + + _RETURN(_SUCCESS) + end function parse_geometry_spec + +end submodule parse_geometry_spec_smod + diff --git a/generic3g/ComponentSpecParser/parse_setservices.F90 b/generic3g/ComponentSpecParser/parse_setservices.F90 new file mode 100644 index 000000000000..44b89d182a66 --- /dev/null +++ b/generic3g/ComponentSpecParser/parse_setservices.F90 @@ -0,0 +1,31 @@ +#include "MAPL_ErrLog.h" + +submodule (mapl3g_ComponentSpecParser) parse_setservices_smod + +contains + + + module function parse_setservices(config, rc) result(user_ss) + type(DSOSetServices) :: user_ss + type(ESMF_HConfig), target, intent(in) :: config + integer, optional, intent(out) :: rc + + character(:), allocatable :: sharedObj, userRoutine + integer :: status + + sharedObj = ESMF_HConfigAsString(config,keyString='sharedObj',rc=status) + _ASSERT(status == 0, 'setServices spec does not specify sharedObj') + + if (ESMF_HConfigIsDefined(config,keyString='userRoutine')) then + userRoutine = ESMF_HConfigAsString(config,keyString='userRoutine',_RC) + else + userRoutine = 'setservices_' + end if + + user_ss = user_setservices(sharedObj, userRoutine) + + _RETURN(_SUCCESS) + end function parse_setservices + +end submodule parse_setservices_smod + diff --git a/generic3g/ComponentSpecParser/parse_var_specs.F90 b/generic3g/ComponentSpecParser/parse_var_specs.F90 new file mode 100644 index 000000000000..48bc94653eb0 --- /dev/null +++ b/generic3g/ComponentSpecParser/parse_var_specs.F90 @@ -0,0 +1,323 @@ +#include "MAPL_ErrLog.h" + +submodule (mapl3g_ComponentSpecParser) parse_var_specs_smod + +contains + + ! A component is not required to have var_specs. E.g, in theory GCM gridcomp will not + ! have var specs in MAPL3, as it does not really have a preferred geom on which to declare + ! imports and exports. + module function parse_var_specs(hconfig, rc) result(var_specs) + type(VariableSpecVector) :: var_specs + type(ESMF_HConfig), optional, intent(in) :: hconfig + integer, optional, intent(out) :: rc + + integer :: status + logical :: has_states_section + type(ESMF_HConfig) :: subcfg + + has_states_section = ESMF_HConfigIsDefined(hconfig,keyString=COMPONENT_STATES_SECTION, _RC) + _RETURN_UNLESS(has_states_section) + + subcfg = ESMF_HConfigCreateAt(hconfig,keyString=COMPONENT_STATES_SECTION, _RC) + + call parse_state_specs(var_specs, subcfg, COMPONENT_INTERNAL_STATE_SECTION, _RC) + call parse_state_specs(var_specs, subcfg, COMPONENT_EXPORT_STATE_SECTION, _RC) + call parse_state_specs(var_specs, subcfg, COMPONENT_IMPORT_STATE_SECTION, _RC) + + call ESMF_HConfigDestroy(subcfg, _RC) + + _RETURN(_SUCCESS) + contains + + subroutine parse_state_specs(var_specs, hconfig, state_intent, rc) + type(VariableSpecVector), intent(inout) :: var_specs + type(ESMF_HConfig), target, intent(in) :: hconfig + character(*), intent(in) :: state_intent + integer, optional, intent(out) :: rc + + type(VariableSpec) :: var_spec + type(ESMF_HConfigIter) :: iter,e,b + character(:), allocatable :: name + character(:), allocatable :: short_name + type(ESMF_HConfig) :: attributes + type(ESMF_TypeKind_Flag) :: typekind + real, allocatable :: default_value + type(VerticalDimSpec) :: vertical_dim_spec + type(UngriddedDims) :: ungridded_dims + character(:), allocatable :: standard_name + character(:), allocatable :: units + type(ESMF_StateItem_Flag), allocatable :: itemtype + type(ESMF_StateIntent_Flag) :: esmf_state_intent + + type(StringVector) :: service_items + integer :: status + logical :: has_state + logical :: has_standard_name + logical :: has_units + type(ESMF_HConfig) :: subcfg + type(StringVector) :: dependencies + + has_state = ESMF_HConfigIsDefined(hconfig,keyString=state_intent, _RC) + _RETURN_UNLESS(has_state) + + subcfg = ESMF_HConfigCreateAt(hconfig,keyString=state_intent, _RC) + + b = ESMF_HConfigIterBegin(subcfg, _RC) + e = ESMF_HConfigIterEnd(subcfg, _RC) + iter = b + do while (ESMF_HConfigIterLoop(iter,b,e)) + name = ESMF_HConfigAsStringMapKey(iter, _RC) + attributes = ESMF_HConfigCreateAtMapVal(iter,_RC) + + short_name = name + typekind = to_typekind(attributes, _RC) + call val_to_float(default_value, attributes, 'default_value', _RC) + vertical_dim_spec = to_VerticalDimSpec(attributes,_RC) + ungridded_dims = to_UngriddedDims(attributes, _RC) + + has_standard_name = ESMF_HConfigIsDefined(attributes,keyString='standard_name', _RC) + if (has_standard_name) then + standard_name = ESMF_HConfigAsString(attributes,keyString='standard_name', _RC) + end if + + has_units = ESMF_HConfigIsDefined(attributes,keyString='units', _RC) + if (has_units) then + units = ESMF_HConfigAsString(attributes,keyString='units', _RC) + end if + + call to_itemtype(itemtype, attributes, _RC) + call to_service_items(service_items, attributes, _RC) + + dependencies = to_dependencies(attributes, _RC) + + esmf_state_intent = to_esmf_state_intent(state_intent) + + var_spec = VariableSpec(esmf_state_intent, short_name=short_name, & + itemtype=itemtype, & + service_items=service_items, & + standard_name=standard_name, & + units=units, & + typekind=typekind, & + default_value=default_value, & + vertical_dim_spec=vertical_dim_spec, & + ungridded_dims=ungridded_dims, & + dependencies=dependencies & + ) + if (allocated(units)) deallocate(units) + if (allocated(standard_name)) deallocate(standard_name) + + call var_specs%push_back(var_spec) + + call ESMF_HConfigDestroy(attributes, _RC) + + end do + + call ESMF_HConfigDestroy(subcfg, _RC) + + _RETURN(_SUCCESS) + end subroutine parse_state_specs + + subroutine val_to_float(x, attributes, key, rc) + real, allocatable, intent(out) :: x + type(ESMF_HConfig), intent(in) :: attributes + character(*), intent(in) :: key + integer, optional, intent(out) :: rc + + integer :: status + logical :: has_default_value + + has_default_value = ESMF_HConfigIsDefined(attributes, keyString=KEY_DEFAULT_VALUE, _RC) + _RETURN_UNLESS(has_default_value) + + allocate(x) + x = ESMF_HConfigAsR4(attributes, keyString=KEY_DEFAULT_VALUE, _RC) + + _RETURN(_SUCCESS) + end subroutine val_to_float + + function to_typekind(attributes, rc) result(typekind) + use :: mapl3g_ESMF_Utilities, only: MAPL_TYPEKIND_MIRROR + type(ESMF_TypeKind_Flag) :: typekind + type(ESMF_HConfig), intent(in) :: attributes + integer, optional, intent(out) :: rc + + integer :: status + logical :: typekind_is_specified + character(:), allocatable :: typekind_str + + typekind = ESMF_TYPEKIND_R4 ! GEOS defaults + + typekind_is_specified = ESMF_HConfigIsDefined(attributes, keyString='typekind', _RC) + _RETURN_UNLESS(typekind_is_specified) + + typekind_str= ESMF_HConfigAsString(attributes,keyString='typekind',_RC) + select case (ESMF_UtilStringLowerCase(typekind_str)) + case ('r4') + typekind = ESMF_TYPEKIND_R4 + case ('r8') + typekind = ESMF_TYPEKIND_R8 + case ('i4') + typekind = ESMF_TYPEKIND_I4 + case ('i8') + typekind = ESMF_TYPEKIND_I8 + case ('mirror') + typekind = MAPL_TYPEKIND_MIRROR + case default + _FAIL('Unsupported typekind: <'//typekind_str//'>') + end select + + _RETURN(_SUCCESS) + end function to_typekind + + function to_VerticalDimSpec(attributes, rc) result(vertical_dim_spec) + type(VerticalDimSpec) :: vertical_dim_spec + type(ESMF_HConfig), intent(in) :: attributes + integer, optional, intent(out) :: rc + + integer :: status + character(:), allocatable :: vertical_str + logical :: has_dim_spec + + vertical_dim_spec = VERTICAL_DIM_UNKNOWN + has_dim_spec = ESMF_HConfigIsDefined(attributes,keyString=KEY_VERTICAL_DIM_SPEC, _RC) + _RETURN_UNLESS(has_dim_spec) + + vertical_str = ESMF_HConfigAsString(attributes,keyString=KEY_VERTICAL_DIM_SPEC,_RC) + + select case (ESMF_UtilStringLowerCase(vertical_str)) + case ('vertical_dim_none', 'n', 'none') + vertical_dim_spec = VERTICAL_DIM_NONE + case ('vertical_dim_center', 'c', 'center') + vertical_dim_spec = VERTICAL_DIM_CENTER + case ('vertical_dim_edge', 'e', 'edge') + vertical_dim_spec = VERTICAL_DIM_EDGE + case ('vertical_dim_mirror', 'm', 'mirror') + vertical_dim_spec = VERTICAL_DIM_MIRROR + case default + _FAIL('Unsupported vertical_dim_spec') + end select + + _RETURN(_SUCCESS) + end function to_VerticalDimSpec + + function to_UngriddedDims(attributes,rc) result(ungridded_dims) + type(UngriddedDims) :: ungridded_dims + type(ESMF_HConfig), intent(in) :: attributes + integer, optional, intent(out) :: rc + + integer :: status + type(ESMF_HConfig) :: dim_specs, dim_spec + character(len=:), allocatable :: dim_name + integer :: dim_size,i + type(UngriddedDim) :: temp_dim + + logical :: has_ungridded_dims + integer :: n_specs + + has_ungridded_dims = ESMF_HConfigIsDefined(attributes, keyString=KEY_UNGRIDDED_DIMS, _RC) + _RETURN_UNLESS(has_ungridded_dims) + + dim_specs = ESMF_HConfigCreateAt(attributes, keyString=KEY_UNGRIDDED_DIMS, _RC) + + n_specs = ESMF_HConfigGetSize(dim_specs, _RC) + do i = 1, n_specs + dim_spec = ESMF_HConfigCreateAt(dim_specs, index=i, _RC) + dim_name = ESMF_HConfigAsString(dim_spec, keyString=KEY_UNGRIDDED_DIM_NAME, _RC) + dim_size = ESMF_HConfigAsI4(dim_spec, keyString=KEY_UNGRIDDED_DIM_EXTENT, _RC) + temp_dim = UngriddedDim(dim_size) + call ungridded_dims%add_dim(temp_dim, _RC) + call ESMF_HConfigDestroy(dim_spec, _RC) + end do + + call ESMF_HConfigDestroy(dim_specs, _RC) + + _RETURN(_SUCCESS) + end function to_UngriddedDims + + + subroutine to_itemtype(itemtype, attributes, rc) + type(ESMF_StateItem_Flag), allocatable, intent(out) :: itemtype + type(ESMF_HConfig), target, intent(in) :: attributes + integer, optional, intent(out) :: rc + + integer :: status + character(:), allocatable :: subclass + logical :: has_itemtype + + has_itemtype = ESMF_HConfigIsDefined(attributes,keyString='class',_RC) + _RETURN_UNLESS(has_itemtype) + + subclass= ESMF_HConfigAsString(attributes, keyString='class',_RC) + + select case (ESMF_UtilStringLowerCase(subclass)) + case ('field') + itemtype = MAPL_STATEITEM_FIELD + case ('service') + itemtype = MAPL_STATEITEM_SERVICE + case ('wildcard') + itemtype = MAPL_STATEITEM_WILDCARD + case default + _FAIL('unknown subclass for state item: '//subclass) + end select + + _RETURN(_SUCCESS) + end subroutine to_itemtype + + subroutine to_service_items(service_items, attributes, rc) + type(StringVector), intent(out) :: service_items + type(ESMF_HConfig), target, intent(in) :: attributes + integer, optional, intent(out) :: rc + + integer :: status + type(ESMF_HConfig) :: seq + integer :: num_items, i + character(:), allocatable :: item_name + logical :: has_service_items + + has_service_items = ESMF_HConfigIsDefined(attributes,keyString='items',_RC) + _RETURN_UNLESS(has_service_items) + + seq = ESMF_HConfigCreateAt(attributes,keyString='items',_RC) + _ASSERT(ESMF_HConfigIsSequence(seq),"items must be a sequence") + num_items = ESMF_HConfigGetSize(seq,_RC) + do i = 1,num_items + item_name = ESMF_HConfigAsString(seq,index = i, _RC) + call service_items%push_back(item_name) + end do + + _RETURN(_SUCCESS) + end subroutine to_service_items + + function to_dependencies(attributes, rc) result(dependencies) + type(StringVector) :: dependencies + type(ESMF_HConfig), intent(in) :: attributes + integer, optional, intent(out) :: rc + + integer :: status + logical :: has_dependencies + type(ESMF_HConfig) :: dependencies_hconfig + integer :: i, n_dependencies + character(:), allocatable :: name + + dependencies = StringVector() + has_dependencies = ESMF_HConfigIsDefined(attributes, keyString='dependencies', _RC) + _RETURN_UNLESS(has_dependencies) + + dependencies_hconfig = ESMF_HConfigCreateAt(attributes, keyString='dependencies', _RC) + _ASSERT(ESMF_HConfigIsSequence(dependencies_hconfig), 'expected sequence for attribute ') + n_dependencies = ESMF_HConfigGetSize(dependencies_hconfig, _RC) + + do i = 1, n_dependencies + name = ESMF_HConfigAsString(dependencies_hconfig, index=i, _RC) + call dependencies%push_back(name) + end do + + _RETURN(_SUCCESS) + end function to_dependencies + + end function parse_var_specs + +end submodule parse_var_specs_smod + +