Group

dataRepository::Group serves as a base class for most objects in GEOSX. In GEOSX, the Group may be thought of as an analogy to the file folder in a hierachical filesystem-like structure. As such, a Group is used as a container class that holds a collection of other Groups, or sub-Groups, a pointer to the parent of the Group, and a collection of Wrappers. The Group also defines a general capability to create and traverse/access the objects in the hierarchy. The Wrappers contained in a Group may be of arbitrary type, but in the case of an LvArray object, a Group size and capacity may be translated down to array, thereby keeping a collection of wrapped Array objects that will resize in unison with the Group. Each group has a string “name” that defines its key in the parent Group. This key string must be unique in the scope of the parent Group.

Implementation Details

Some noteworthy implementation details inside the declaration of dataRepository::Group are:

/// The default key type for entries in the hierarchy.
using keyType = string;

/// The default index type for entries the hierarchy.
using indexType = localIndex;
  • In the GEOSX repository, the keyType is specified to be a string for all collection objects, while the indexType is specified to be a localIndex. The types are set in the common/DataTypes.hpp file, but are typically a std::string and a std::ptrdiff_t respectively.
  /// The template specialization of MappedVector to use for the collection of sub-Group objects.
  using subGroupMap = MappedVector< Group, Group *, keyType, indexType >;

  /// The template specialization of MappedVector to use for the collection wrappers objects.
  using wrapperMap = MappedVector< WrapperBase, WrapperBase *, keyType, indexType >;
  • The subGroupMap and wrapperMap aliases represent the type of container that the collection of sub-Group s and Wrapper s are stored in for each Group. These container types are template specializations of the MappedVector class, which store a pointer to a type, and provides functionality for a key or index based lookup. More details may be found in the documentation for MappedVector.
  /// The parent Group that contains "this" Group in its "sub-Group" collection.
  Group * m_parent = nullptr;

  /// Specification that this group will have the same m_size as m_parent.
  integer m_sizedFromParent;

  /// The container for the collection of all wrappers continued in "this" Group.
  wrapperMap m_wrappers;

  /// The container for the collection of all sub-groups contained in "this" Group.
  subGroupMap m_subGroups;

  /// The size/length of this Group...and all Wrapper<> that are are specified to have the same size as their
  /// owning group.
  indexType m_size;

  /// The capacity for wrappers in this group...and all Wrapper<> that are specified to have the same size as their
  /// owning group.
  indexType m_capacity;

  /// The name/key of this Group in its parent collection of sub-Groups.
  string m_name;

  /// Verbosity flag for group logs
  integer m_logLevel;
  • The m_parent member is a pointer to the Group that contains the current Group as part of its collection of sub-Group s.

    Warning

    The existence of the non-const m_parent gives the current Group access to alter the parent Group. Special care should be taken to avoid using this access whenever possible. Remember…with great power comes great responsibility.

  • The m_wrappers member is the collection of Wrappers contained in the current Group.

  • The m_subGroups member is the collection of Group s contained in the current Group.

  • The m_size and m_capacity members are used to set the size and capacity of any objects contained in the m_wrappers collection that have been specified to be set by their owning Group. This is typically only useful for Array types and is implemented within the WrapperBase object.

  • The m_name member is the key of this Group in the collection of m_parent->m_subGroups. This key is unique in the scope of m_parent, so some is required when constructing the hierarchy.

Interface Functions

The public interface for dataRepository::Group provides functionality for constructing a hierarchy, and traversing that hierarchy, as well as accessing the contents of objects stored in the Wrapper containers stored within a Group.

Adding New Groups

To add new sub-Group s there are several RegisterGroup functions that add a new Group under the calling Group scope. A listing of these functions is provided:

  /**
   * @name Sub-group registration interface
   */
  ///@{

  /**
   * @brief Register a new Group as a sub-group of current Group.
   *
   * @tparam T The type of the Group to add/register. This should be a type that derives from Group.
   * @param[in] name      The name of the group to use as a string key.
   * @param[in] newObject A unique_ptr to the object that is being registered.
   * @return              A pointer to the newly registered Group.
   *
   * Registers a Group or class derived from Group as a subgroup of this Group and takes ownership.
   */
  template< typename T = Group >
  T * RegisterGroup( std::string const & name, std::unique_ptr< T > newObject );

  /**
   * @brief @copybrief RegisterGroup(std::string const &,std::unique_ptr<T>)
   *
   * @tparam T The type of the Group to add/register. This should be a type that derives from Group.
   * @param[in] name          The name of the group to use as a string key.
   * @param[in] newObject     A unique_ptr to the object that is being registered.
   * @return                  A pointer to the newly registered Group.
   *
   * Registers a Group or class derived from Group as a subgroup of this Group but does not take ownership.
   */
  template< typename T = Group >
  T * RegisterGroup( std::string const & name,
                     T * newObject );

  /**
   * @brief @copybrief RegisterGroup(std::string const &,std::unique_ptr<T>)
   *
   * @tparam T The type of the Group to add/register. This should be a type that derives from Group.
   * @param[in] name The name of the group to use as a string key.
   * @return         A pointer to the newly registered Group.
   *
   * Creates and registers a Group or class derived from Group as a subgroup of this Group.
   */
  template< typename T = Group >
  T * RegisterGroup( std::string const & name )
  {
    return RegisterGroup< T >( name, std::move( std::make_unique< T >( name, this )) );
  }

  /**
   * @brief @copybrief RegisterGroup(std::string const &,std::unique_ptr<T>)
   *
   * @tparam T The type of the Group to add/register. This should be a type that derives from Group.
   * @param[in,out] keyIndex A KeyIndexT object that will be used to specify the name of
   *                         the new group. The index of the KeyIndex will also be set.
   * @return                 A pointer to the newly registered Group.
   *
   * Creates and registers a Group or class derived from Group as a subgroup of this Group.
   */
  template< typename T = Group >
  T * RegisterGroup( subGroupMap::KeyIndex const & keyIndex )
  {
    T * rval = RegisterGroup< T >( keyIndex.Key(), std::move( std::make_unique< T >( keyIndex.Key(), this )) );
    keyIndex.setIndex( this->m_subGroups.getIndex( keyIndex.Key()) );
    return rval;
  }

  /**
   * @brief @copybrief RegisterGroup(std::string const &,std::unique_ptr<T>)
   *
   * @tparam T The type of the Group to add/register. This should be a type that derives from Group.
   * @tparam TBASE The type whose type catalog will be used to look up the new sub-group type
   * @param[in] name        The name of the group to use as a string key.
   * @param[in] catalogName The catalog name of the new type.
   * @return                A pointer to the newly registered Group.
   *
   * Creates and registers a Group or class derived from Group as a subgroup of this Group.
   */
  template< typename T = Group, typename TBASE = Group >
  T * RegisterGroup( std::string const & name, std::string const & catalogName )
  {
    std::unique_ptr< TBASE > newGroup = TBASE::CatalogInterface::Factory( catalogName, name, this );
    return RegisterGroup< T >( name, std::move( newGroup ) );
  }

  /**
   * @brief Removes a child group from this group.
   * @param name the name of the child group to remove from this group.
   */
  void deregisterGroup( std::string const & name );

  /**
   * @brief Creates a new sub-Group using the ObjectCatalog functionality.
   * @param[in] childKey The name of the new object type's key in the
   *                     ObjectCatalog.
   * @param[in] childName The name of the new object in the collection of
   *                      sub-Groups.
   * @return A pointer to the new Group created by this function.
   */
  virtual Group * CreateChild( string const & childKey, string const & childName );

  ///@}

These functions all take in a name for the new Group, which will be used as the key when trying to access the Group in the future. Some variants create a new Group, while some variants take in an existing Group . The template argument is to specify the actaul type of the Group as it it is most likely a type that derives from Group that is we would like to create in the repository. Please see the doxygen documentation for a detailed description of each option.

Getting Groups

The collection of functions to retrieve a Group and their descriptions are taken from source and shown here:

  /**
   * @name Sub-group retrieval methods.
   *
   * This collection of functions are used to get a sub-Group from the current group. Various methods
   * for performing the lookup are provided (localIndex, string, KeyIndex), and each have their
   * advantages and costs. The lowest cost lookup is the "localIndex" lookup. The KeyIndex lookup
   * will add a cost for checking to make sure the index stored in KeyIndex is valid (a string
   * compare, and a hash if it is incorrect). The string lookup is the full cost hash lookup every
   * time that it is called.
   *
   * The template parameter specifies the "type" that the caller expects to lookup, and thus attempts
   * to cast the pointer that is stored in m_subGroups to a pointer of the desired type. If this
   * cast fails, then a @p nullptr is returned. If no template parameter is specified then a default
   * type of Group is assumed.
   */
  ///@{

  /**
   * @brief Retrieve a sub-group from the current Group using an index.
   * @tparam T type of subgroup
   * @param[in] index the integral index of the group to retrieve.
   * @return A pointer to @p T that refers to the sub-group
   */
  template< typename T = Group >
  T * GetGroup( localIndex index )
  {
    return group_cast< T * >( m_subGroups[index] );
  }

  /**
   * @copydoc GetGroup(localIndex)
   */
  template< typename T = Group >
  T const * GetGroup( localIndex index ) const
  {
    return group_cast< T const * >( m_subGroups[index] );
  }

  /**
   * @brief Retrieve a sub-group from the current Group using a string.
   * @tparam T type of subgroup
   * @param[in] name the name/key of the group lookup and retrieve.
   * @return A pointer to @p T that refers to the sub-group
   */
  template< typename T = Group >
  T * GetGroup( string const & name )
  {
    return group_cast< T * >( m_subGroups[name] );
  }

  /**
   * @copydoc GetGroup(string const &)
   */
  template< typename T = Group >
  T const * GetGroup( string const & name ) const
  { return group_cast< T const * >( m_subGroups[name] ); }

  /**
   * @brief @return Return a reference to the Group @p name.
   * @tparam The type to return.
   * @param key The name of the group to retrieve.
   * @note Will abort if the group doesn't exist.
   */
  template< typename T = Group >
  T & getGroupReference( string const & key )
  { return dynamicCast< T & >( *m_subGroups[ key ] ); }

  /**
   * @copydoc getGroupReference( string const & )
   */
  template< typename T = Group >
  T const & getGroupReference( string const & key ) const
  { return dynamicCast< T const & >( *m_subGroups[ key ] ); }

  /**
   * @copydoc getGroupReference( string const & )
   */
  template< typename T = Group >
  T & GetGroupReference( subGroupMap::KeyIndex const & key )
  { return dynamicCast< T & >( *m_subGroups[key] ); }

  /**
   * @copydoc getGroupReference( string const & )
   */
  template< typename T = Group >
  T const & GetGroupReference( subGroupMap::KeyIndex const & key ) const
  { return dynamicCast< T const & >( *m_subGroups[key] ); }

  /**
   * @brief Retrieve a sub-group from the current Group using a KeyIndexT.
   * @tparam T type of subgroup
   * @param[in] key the KeyIndex to use for the lookup
   * @return A pointer to @p T that refers to the sub-group
   */
  template< typename T = Group >
  T * GetGroup( subGroupMap::KeyIndex const & key )
  {
    return group_cast< T * >( m_subGroups[key] );
  }

  /**
   * @copydoc GetGroup(subGroupMap::KeyIndex const & key)
   */
  template< typename T = Group >
  T const * GetGroup( subGroupMap::KeyIndex const & key ) const
  {
    return group_cast< T const * >( m_subGroups[key] );
  }

  /**
   * @brief Retrieve a group from the hierarchy using a path.
   * @tparam T type of subgroup
   * @param[in] path a unix-style string (absolute, relative paths valid)
   *                 to lookup the Group to return. Absolute paths search
   *                 from the tree root, while relative - from current group.
   * @return A pointer to @p T that refers to the sub-group
   */
  template< typename T = Group >
  T * GetGroupByPath( string const & path )
  {
    return const_cast< T * >(const_cast< Group const * >(this)->GetGroupByPath< T >( path ));
  }

  /**
   * @copydoc GetGroupByPath(string const &)
   */
  template< typename T = Group >
  T const * GetGroupByPath( string const & path ) const;

Register Wrappers

  /**
   * @name Wrapper registration interface
   */
  ///@{

  /**
   * @brief Create and register a Wrapper around a new object.
   * @tparam T The type of the object allocated.
   * @tparam TBASE The type of the object that the Wrapper holds.
   * @param[in]  name the name of the wrapper to use as a string key
   * @param[out] rkey a pointer to a index type that will be filled with the new
   *             Wrapper index in this Group
   * @return     a pointer to the newly registered/created Wrapper
   */
  template< typename T, typename TBASE=T >
  Wrapper< TBASE > * registerWrapper( std::string const & name,
                                      wrapperMap::KeyIndex::index_type * const rkey = nullptr );

  /**
   * @copybrief registerWrapper(std::string const &,wrapperMap::KeyIndex::index_type * const)
   * @tparam     T the type of the wrapped object
   * @tparam TBASE the base type to cast the returned wrapper to
   * @param[in] viewKey The KeyIndex that contains the name of the new Wrapper.
   * @return            a pointer to the newly registered/created Wrapper
   */
  template< typename T, typename TBASE=T >
  Wrapper< TBASE > * registerWrapper( Group::wrapperMap::KeyIndex const & viewKey );

  /**
   * @brief Register a Wrapper around a given object and take ownership.
   * @tparam T the type of the wrapped object
   * @param[in] name      the name of the wrapper to use as a string key
   * @param[in] newObject an owning pointer to the object that is being registered
   * @return              a pointer to the newly registered/created Wrapper
   */
  template< typename T >
  Wrapper< T > * registerWrapper( std::string const & name,
                                  std::unique_ptr< T > newObject );

  /**
   * @brief Register a Wrapper around an existing object, does not take ownership of the object.
   * @tparam T the type of the wrapped object
   * @param[in] name          the name of the wrapper to use as a string key
   * @param[in] newObject     a pointer to the object that is being registered
   * @return                  a pointer to the newly registered/created Wrapper
   */
  template< typename T >
  Wrapper< T > * registerWrapper( std::string const & name,
                                  T * newObject );

  /**
   * @brief Register and take ownership of an existing Wrapper.
   * @param name    the name of the wrapper to use as a string key
   * @param wrapper a pointer to the an existing wrapper.
   * @return        an un-typed pointer to the newly registered/created wrapper
   */
  WrapperBase * registerWrapper( string const & name,
                                 std::unique_ptr< WrapperBase > wrapper );

  /**
   * @brief Removes a Wrapper from this group.
   * @param name the name of the Wrapper to remove from this group.
   */
  void deregisterWrapper( string const & name );

  ///@}

Getting Wrappers/Wrapped Objects

  /**
   * @name Untyped wrapper retrieval methods
   *
   * These functions query the collection of Wrapper objects for the given
   * index/name/KeyIndex and returns a WrapperBase pointer to the object if
   * it exists. If it is not found, nullptr is returned.
   */
  ///@{

  /**
   * @brief Retrieve a WrapperBase stored in this group.
   * @param[in] index an integral lookup value used to search the collection of wrappers.
   * @return          a pointer to the WrapperBase that resulted from the lookup.
   */
  WrapperBase const * getWrapperBase( indexType const index ) const
  { return m_wrappers[index]; }

  /**
   * @copydoc getWrapperBase(indexType const) const
   */
  WrapperBase * getWrapperBase( indexType const index )
  { return m_wrappers[index]; }

  /**
   * @brief Retrieve a WrapperBase stored in this group.
   * @param[in] name a string lookup value used to search the collection of wrappers.
   * @return         a pointer to the WrapperBase that resulted from the lookup.
   */
  WrapperBase const * getWrapperBase( std::string const & name ) const
  { return m_wrappers[name]; }

  /**
   * @copydoc getWrapperBase(std::string const &) const
   */
  WrapperBase * getWrapperBase( std::string const & name )
  { return m_wrappers[name]; }

  /**
   * @brief Retrieve a WrapperBase stored in this group.
   * @param[in] keyIndex a KeyIndex lookup value used to search the collection of wrappers.
   * @return             a pointer to the WrapperBase that resulted from the lookup.
   */
  WrapperBase const * getWrapperBase( wrapperMap::KeyIndex const & keyIndex ) const
  { return m_wrappers[keyIndex]; }

  /**
   * @copydoc getWrapperBase(wrapperMap::KeyIndex const &) const
   */
  WrapperBase * getWrapperBase( wrapperMap::KeyIndex const & keyIndex )
  { return m_wrappers[keyIndex]; }

  /**
   * @brief
   * @param name
   * @return
   */
  indexType getWrapperIndex( std::string const & name ) const
  {
    return m_wrappers.getIndex( name );
  }

  /**
   * @brief Get access to the internal wrapper storage.
   * @return a reference to wrapper map
   */
  wrapperMap const & wrappers() const
  {
    return m_wrappers;
  }

  /**
   * @copydoc wrappers() const
   */
  wrapperMap & wrappers()
  {
    return m_wrappers;
  }

  ///@}

  /**
   * @name Typed wrapper retrieval methods
   *
   * These functions query the collection of Wrapper objects for the given
   * index/key and returns a Wrapper<T> pointer to the object if
   * it exists. The template parameter @p T is used to perform a cast
   * on the WrapperBase pointer that is returned by the lookup, into
   * a Wrapper<T> pointer. If the wrapper is not found, or the
   * WrapperBase pointer cannot be cast to a Wrapper<T> pointer, then nullptr
   * is returned.
   */
  ///@{

  /**
   * @brief Check if a wrapper exists
   * @tparam LOOKUP_TYPE the type of key used to perform the lookup.
   * @param[in] lookup a lookup value used to search the collection of wrappers
   * @return @p true if wrapper exists (regardless of type), @p false otherwise
   */
  template< typename LOOKUP_TYPE >
  bool hasWrapper( LOOKUP_TYPE const & lookup ) const
  {
    return (m_wrappers[lookup] != nullptr);
  }

  /**
   * @brief Retrieve a Wrapper stored in this group.
   * @tparam T           the object type contained in the Wrapper
   * @tparam LOOKUP_TYPE the type of key used to perform the lookup
   * @param[in] index    a lookup value used to search the collection of wrappers
   * @return             A pointer to the Wrapper<T> that resulted from the lookup
   */
  template< typename T, typename LOOKUP_TYPE >
  Wrapper< T > const * getWrapper( LOOKUP_TYPE const & index ) const
  {
    return dynamicCast< Wrapper< T > const * >( m_wrappers[index] );
  }

  /**
   * @copydoc getWrapper(LOOKUP_TYPE const &) const
   */
  template< typename T, typename LOOKUP_TYPE >
  Wrapper< T > * getWrapper( LOOKUP_TYPE const & index )
  { return const_cast< Wrapper< T > * >( const_cast< Group const * >(this)->getWrapper< T >( index ) ); }

  /**
   * @brief Retrieve a Wrapper stored in this group.
   * @tparam T      the object type contained in the Wrapper
   * @param[in] key a string lookup value used to search the collection of wrappers
   * @return        a pointer to the Wrapper<T> that resulted from the lookup
   */
  template< typename T >
  Wrapper< T > const * getWrapper( char const * const key ) const
  { return getWrapper< T >( string( key ) ); }

  /**
   * @copydoc getWrapper(char const * const) const
   */
  template< typename T >
  Wrapper< T > * getWrapper( char const * const key )
  { return getWrapper< T >( string( key ) ); }

  ///@}

  /**
   * @name Wrapper data access methods.
   *
   * These functions can be used to get referece/pointer access to the data
   * stored by wrappers in this group. They are essentially just shortcuts for
   * @p Group::getWrapper() and @p Wrapper<T>::getReference().
   * An additional template parameter can be provided to cast the return pointer
   * or reference to a base class pointer or reference (e.g. Array to ArrayView).
   */
  ///@{

  /**
   * @brief Look up a wrapper and get reference to wrapped object.
   * @tparam T           return value type
   * @tparam WRAPPEDTYPE wrapped value type (by default, same as return)
   * @tparam LOOKUP_TYPE type of value used for wrapper lookup
   * @param lookup       value for wrapper lookup
   * @return             reference to @p T
   *
   * @note An error will be raised if wrapper does not exist or type cast is invalid.
   */
  template< typename T, typename LOOKUP_TYPE >
  GEOSX_DECLTYPE_AUTO_RETURN
  getReference( LOOKUP_TYPE const & lookup ) const
  {
    Wrapper< T > const * const wrapper = getWrapper< T >( lookup );
    if( wrapper == nullptr )
    {
      if( hasWrapper( lookup ) )
      {
        GEOSX_ERROR( "call to getWrapper results in nullptr but a view exists. Most likely given the incorrect type. lookup : " << lookup );
      }
      GEOSX_ERROR( "call to getWrapper results in nullptr and a view does not exist. lookup : " << lookup );
    }

    return wrapper->reference();
  }

  /**
   * @copydoc getReference(LOOKUP_TYPE const &) const
   */
  template< typename T, typename LOOKUP_TYPE >
  T &
  getReference( LOOKUP_TYPE const & lookup )
  {
    Wrapper< T > * const wrapper = getWrapper< T >( lookup );
    if( wrapper == nullptr )
    {
      if( hasWrapper( lookup ) )
      {
        GEOSX_ERROR( "call to getWrapper results in nullptr but a view exists. Most likely given the incorrect type. lookup : " << lookup );
      }
      GEOSX_ERROR( "call to getWrapper results in nullptr and a view does not exist. lookup : " << lookup );
    }

    return wrapper->reference();
  }

  /**
   * @copybrief getReference(LOOKUP_TYPE const &) const
   * @tparam T           return value type
   * @tparam WRAPPEDTYPE wrapped value type (by default, same as return)
   * @param name         name of the wrapper
   * @return             reference to @p T
   *
   * @note An error will be raised if wrapper does not exist or type cast is invalid.
   */
  template< typename T >
  GEOSX_DECLTYPE_AUTO_RETURN
  getReference( char const * const name ) const
  { return getReference< T >( string( name ) ); }

  /**
   * @copydoc getReference(char const * const) const
   */
  template< typename T >
  T & getReference( char const * const name )
  { return getReference< T >( string( name ) ); }

Looping Interface

  /**
   * @name Functor-based subgroup iteration
   *
   * These functions loop over sub-groups and executes a functor that uses the sub-group as an
   * argument. The functor is only executed if the group can be cast to a certain type specified
   * by the @p ROUPTYPE/S pack. The variadic list consisting of @p GROUPTYPE/S will be used recursively
   * to check if the group is able to be cast to the one of these types. The first type in the
   * @p GROUPTYPE/S list will be used to execute the functor, and the next sub-group will be processed.
   */
  ///@{

  /**
   * @brief Apply the given functor to subgroups that can be casted to one of specified types.
   * @tparam GROUPTYPE  the first type that will be used in the attempted casting of group.
   * @tparam GROUPTYPES a variadic list of types that will be used in the attempted casting of group.
   * @tparam LAMBDA     the type of functor to call
   * @param[in] lambda  the functor to call on subgroups
   */
  template< typename GROUPTYPE = Group, typename ... GROUPTYPES, typename LAMBDA >
  void forSubGroups( LAMBDA lambda )
  {
    for( auto & subGroupIter : m_subGroups )
    {
      applyLambdaToContainer< GROUPTYPE, GROUPTYPES... >( *subGroupIter.second, [&]( auto & castedSubGroup )
      {
        lambda( castedSubGroup );
      } );
    }
  }

  /**
   * @copydoc forSubGroups(LAMBDA)
   */
  template< typename GROUPTYPE = Group, typename ... GROUPTYPES, typename LAMBDA >
  void forSubGroups( LAMBDA lambda ) const
  {
    for( auto const & subGroupIter : m_subGroups )
    {
      applyLambdaToContainer< GROUPTYPE, GROUPTYPES... >( *subGroupIter.second, [&]( auto const & castedSubGroup )
      {
        lambda( castedSubGroup );
      } );
    }
  }

  /**
   * @copybrief forSubGroups(LAMBDA)
   * @tparam GROUPTYPE        the first type that will be used in the attempted casting of group.
   * @tparam GROUPTYPES       a variadic list of types that will be used in the attempted casting of group.
   * @tparam LOOKUP_CONTAINER type of container of subgroup lookup keys (names or indices), must support range-based for
   * loop
   * @tparam LAMBDA           type of functor callable with an index in lookup container and a reference to casted
   * subgroup
   * @param[in] subGroupKeys  container with subgroup lookup keys (e.g. names or indices) to apply the functor to
   * @param[in] lambda        the functor to call
   */
  template< typename GROUPTYPE = Group, typename ... GROUPTYPES, typename LOOKUP_CONTAINER, typename LAMBDA >
  void forSubGroups( LOOKUP_CONTAINER const & subGroupKeys, LAMBDA lambda )
  {
    localIndex counter = 0;
    for( auto const & subgroup : subGroupKeys )
    {
      applyLambdaToContainer< GROUPTYPE, GROUPTYPES... >( *GetGroup( subgroup ), [&]( auto & castedSubGroup )
      {
        lambda( counter, castedSubGroup );
      } );
      ++counter;
    }
  }

  /**
   * @copybrief forSubGroups(LAMBDA)
   * @tparam GROUPTYPE        the first type that will be used in the attempted casting of group.
   * @tparam GROUPTYPES       a variadic list of types that will be used in the attempted casting of group.
   * @tparam LOOKUP_CONTAINER type of container of subgroup lookup keys (names or indices), must support range-based for
   * loop
   * @tparam LAMBDA           type of functor callable with an index in lookup container and a reference to casted
   * subgroup
   * @param[in] subGroupKeys  container with subgroup lookup keys (e.g. names or indices) to apply the functor to
   * @param[in] lambda        the functor to call
   */
  template< typename GROUPTYPE = Group, typename ... GROUPTYPES, typename LOOKUP_CONTAINER, typename LAMBDA >
  void forSubGroups( LOOKUP_CONTAINER const & subGroupKeys, LAMBDA lambda ) const
  {
    localIndex counter = 0;
    for( auto const & subgroup : subGroupKeys )
    {
      applyLambdaToContainer< GROUPTYPE, GROUPTYPES... >( *GetGroup( subgroup ), [&]( auto const & castedSubGroup )
      {
        lambda( counter, castedSubGroup );
      } );
      ++counter;
    }
  }
  ///@}

  /**
   * @name Functor-based wrapper iteration
   *
   * These functions loop over the wrappers contained in this group, and executes a functor that
   * uses the Wrapper as an argument. The functor is only executed if the Wrapper can be casted to
   * a certain type specified by the @p TYPE/S pack. The variadic list consisting of
   * @p TYPE/S will be used recursively to check if the Wrapper is able to be casted to the
   * one of these types. The first type in the @p WRAPPERTYPE/S list will be used to execute the
   * functor, and the next Wrapper will be processed.
   */
  ///@{

  /**
   * @brief Apply the given functor to wrappers.
   * @tparam LAMBDA the type of functor to call
   * @param[in] lambda  the functor to call
   */
  template< typename LAMBDA >
  void forWrappers( LAMBDA lambda )
  {
    for( auto & wrapperIter : m_wrappers )
    {
      lambda( *wrapperIter.second );
    }
  }

  /**
   * @copydoc forWrappers(LAMBDA)
   */
  template< typename LAMBDA >
  void forWrappers( LAMBDA lambda ) const
  {
    for( auto const & wrapperIter : m_wrappers )
    {
      lambda( *wrapperIter.second );
    }
  }

  /**
   * @brief Apply the given functor to wrappers that can be cast to one of specified types.
   * @tparam TYPE   the first type that will be used in the attempted casting of Wrapper
   * @tparam TYPES  a variadic list of types that will be used in the attempted casting of Wrapper
   * @tparam LAMBDA the type of functor to call
   * @param[in] lambda  the functor to call
   */
  template< typename TYPE, typename ... TYPES, typename LAMBDA >
  void forWrappers( LAMBDA lambda )
  {
    for( auto & wrapperIter : m_wrappers )
    {
      applyLambdaToContainer< Wrapper< TYPE >, Wrapper< TYPES >... >( *wrapperIter.second,
                                                                      std::forward< LAMBDA >( lambda ));
    }
  }

  /**
   * @brief Apply the given functor to wrappers that can be cast to one of specified types.
   * @tparam TYPE   the first type that will be used in the attempted casting of Wrapper
   * @tparam TYPES  a variadic list of types that will be used in the attempted casting of Wrapper
   * @tparam LAMBDA the type of functor to call
   * @param[in] lambda  the functor to call
   */
  template< typename TYPE, typename ... TYPES, typename LAMBDA >
  void forWrappers( LAMBDA lambda ) const
  {
    for( auto const & wrapperIter : m_wrappers )
    {
      applyLambdaToContainer< Wrapper< TYPE >, Wrapper< TYPES >... >( *wrapperIter.second,
                                                                      std::forward< LAMBDA >( lambda ));
    }
  }

  ///@}