# Buggy code
def expand_components(block):
    """
    Loop over block components and try expanding them. If expansion fails
    then save the component and try again later. This function has some
    built-in robustness for block-hierarchical models with circular
    references but will not work for all cases.
    """

    expansion_map = ComponentMap()
    redo_expansion = list()

    # Identify components that need to be expanded and try expanding them
    for c in block.component_objects(descend_into=True, sort=SortComponents.declOrder):
        try:
            update_contset_indexed_component(c, expansion_map)
        except AttributeError:
            redo_expansion.append(c)

    print("Completed first discretization pass")

    N = len(redo_expansion)
    print("Number of components to re-expand: ", N)
    while N:
        print(redo_expansion)
        for i in range(N):
            c = redo_expansion.pop()
            print("Re-expanding component ", str(c))
            expansion_map[c](c)
            # try:
            #     expansion_map[c](c)
            # except AttributeError:
            #     redo_expansion.append(c)
        print(redo_expansion)
        if len(redo_expansion) == N:
            raise DAE_Error(
                "Unable to fully discretize %s. Possible "
                "circular references detected between components "
                "%s. Reformulate your model to remove circular "
                "references or apply a discretization "
                "transformation before linking blocks together."
                % (block, str(redo_expansion))
            )
        N = len(redo_expansion)


# Traceback (AttributeError)
$ python -i temp.py
Traceback (most recent call last):
File "temp.py", line 26, in <module>
disc.apply_to(m, nfe=2)
File "/home/blnicho/Research/pyomo/pyomo/core/base/plugin.py", line 334, in apply_to
self._apply_to(model, **kwds)
File "/home/blnicho/Research/pyomo/pyomo/dae/plugins/finitedifference.py", line 187, in _apply_to
self._transformBlock(block, currentds)
File "/home/blnicho/Research/pyomo/pyomo/dae/plugins/finitedifference.py", line 218, in _transformBlock
update_contset_indexed_component(c)
File "/home/blnicho/Research/pyomo/pyomo/dae/misc.py", line 145, in update_contset_indexed_component
_update_constraint(comp)
File "/home/blnicho/Research/pyomo/pyomo/dae/misc.py", line 189, in _update_constraint
con.add(i, apply_indexed_rule(con, _rule, _parent, i))
File "/home/blnicho/Research/pyomo/pyomo/core/base/misc.py", line 61, in apply_indexed_rule
return rule(model, index)
File "temp.py", line 22, in _ratio_rule
return b.ratioP[t] <= b.holdup.properties_in[t].pressure - b.holdup.properties_out[t].pressure
File "/home/blnicho/Research/pyomo/pyomo/core/base/block.py", line 521, in __getattr__
% (self.__class__.__name__, val))
AttributeError: '_BlockData' object has no attribute 'pressure'


# Fixed code
def expand_components(block):
    """
    Loop over block components and try expanding them. If expansion fails
    then save the component and try again later. This function has some
    built-in robustness for block-hierarchical models with circular
    references but will not work for all cases.
    """

    # Used to map components to the functions used to expand them so that
    # the update_contset_indexed_component function logic only has to be
    # called once even in the case where we have to re-try expanding
    # components due to circular references
    expansion_map = ComponentMap()
    redo_expansion = list()

    # Record the missing BlockData before expanding components. This is for
    # the case where a ContinuousSet indexed Block is used in a Constraint.
    # If the Constraint is expanded before the Block then the missing
    # BlockData will be added to the indexed Block but will not be
    # constructed correctly.
    for blk in block.component_objects(Block, descend_into=True):
        missing_idx = set(blk._index) - set(iterkeys(blk._data))
        if missing_idx:
            blk._dae_missing_idx = missing_idx

    # Identify components that need to be expanded and try expanding them
    for c in block.component_objects(descend_into=True, sort=SortComponents.declOrder):
        try:
            update_contset_indexed_component(c, expansion_map)
        except AttributeError:
            redo_expansion.append(c)

    N = len(redo_expansion)
    while N:
        for i in range(N):
            c = redo_expansion.pop()
            try:
                expansion_map[c](c)
            except AttributeError:
                redo_expansion.append(c)
        if len(redo_expansion) == N:
            raise DAE_Error(
                "Unable to fully discretize %s. Possible "
                "circular references detected between components "
                "%s. Reformulate your model to remove circular "
                "references or apply a discretization "
                "transformation before linking blocks together."
                % (block, str(redo_expansion))
            )
        N = len(redo_expansion)