Esempio n. 1
0
    def test_flat_model(self):
        m = ConcreteModel()
        m.T = ContinuousSet(bounds=(0, 1))
        m.x = Var()
        m.y = Var([1, 2])
        m.a = Var(m.T)
        m.b = Var(m.T, [1, 2])
        m.c = Var([3, 4], m.T)

        regular, time = flatten_dae_variables(m, m.T)
        regular_id = set(id(_) for _ in regular)
        self.assertEqual(len(regular), 3)
        self.assertIn(id(m.x), regular_id)
        self.assertIn(id(m.y[1]), regular_id)
        self.assertIn(id(m.y[2]), regular_id)
        # Output for debugging
        #for v in time:
        #    v.pprint()
        #    for _ in v.values():
        #        print"     -> ", _.name
        ref_data = {
            self._hashRef(Reference(m.a[:])),
            self._hashRef(Reference(m.b[:, 1])),
            self._hashRef(Reference(m.b[:, 2])),
            self._hashRef(Reference(m.c[3, :])),
            self._hashRef(Reference(m.c[4, :])),
        }
        self.assertEqual(len(time), len(ref_data))
        for ref in time:
            self.assertIn(self._hashRef(ref), ref_data)
Esempio n. 2
0
    def test_2level_model(self):
        m = ConcreteModel()
        m.T = ContinuousSet(bounds=(0,1))
        @m.Block([1,2],m.T)
        def B(b, i, t):
            @b.Block(list(range(2*i, 2*i+2)))
            def bb(bb, j):
                bb.y = Var([10,11])
            b.x = Var(list(range(2*i, 2*i+2)))

        regular, time = flatten_dae_variables(m, m.T)
        self.assertEqual(len(regular), 0)
        # Output for debugging
        #for v in time:
        #    v.pprint()
        #    for _ in v.values():
        #        print"     -> ", _.name
        ref_data = {
            self._hashRef(Reference(m.B[1,:].x[2])),
            self._hashRef(Reference(m.B[1,:].x[3])),
            self._hashRef(Reference(m.B[2,:].x[4])),
            self._hashRef(Reference(m.B[2,:].x[5])),
            self._hashRef(Reference(m.B[1,:].bb[2].y[10])),
            self._hashRef(Reference(m.B[1,:].bb[2].y[11])),
            self._hashRef(Reference(m.B[1,:].bb[3].y[10])),
            self._hashRef(Reference(m.B[1,:].bb[3].y[11])),
            self._hashRef(Reference(m.B[2,:].bb[4].y[10])),
            self._hashRef(Reference(m.B[2,:].bb[4].y[11])),
            self._hashRef(Reference(m.B[2,:].bb[5].y[10])),
            self._hashRef(Reference(m.B[2,:].bb[5].y[11])),
        }
        self.assertEqual(len(time), len(ref_data))
        for ref in time:
            self.assertIn(self._hashRef(ref), ref_data)
Esempio n. 3
0
def test_initialize_by_element_in_range():
    mod = make_model(horizon=2, ntfe=20)
    assert degrees_of_freedom(mod) == 0

    scalar_vars, dae_vars = flatten_dae_variables(mod.fs, mod.fs.time)
    diff_vars = [
        Reference(mod.fs.cstr.control_volume.energy_holdup[:, 'aq']),
        Reference(mod.fs.cstr.control_volume.material_holdup[:, 'aq', 'S']),
        Reference(mod.fs.cstr.control_volume.material_holdup[:, 'aq', 'E']),
        Reference(mod.fs.cstr.control_volume.material_holdup[:, 'aq', 'C']),
        Reference(mod.fs.cstr.control_volume.material_holdup[:, 'aq', 'P'])
    ]

    initialize_by_element_in_range(mod.fs,
                                   mod.fs.time,
                                   0,
                                   1,
                                   solver=solver,
                                   dae_vars=dae_vars,
                                   time_linking_variables=diff_vars,
                                   outlvl=idaeslog.DEBUG,
                                   solve_initial_conditions=True)

    assert degrees_of_freedom(mod.fs) == 0

    assert mod.fs.cstr.outlet.conc_mol[1, 'S'].value == approx(10.189,
                                                               abs=1e-3)
    assert mod.fs.cstr.outlet.conc_mol[1, 'C'].value == approx(0.4275,
                                                               abs=1e-4)
    assert mod.fs.cstr.outlet.conc_mol[1, 'E'].value == approx(0.0541,
                                                               abs=1e-4)
    assert mod.fs.cstr.outlet.conc_mol[1, 'P'].value == approx(0.3503,
                                                               abs=1e-4)

    initialize_by_element_in_range(mod.fs,
                                   mod.fs.time,
                                   1,
                                   2,
                                   solver=solver,
                                   dae_vars=dae_vars,
                                   outlvl=idaeslog.DEBUG)

    assert degrees_of_freedom(mod.fs) == 0
    for con in activated_equalities_generator(mod.fs):
        assert value(con.body) - value(con.upper) < 1e-5

    assert mod.fs.cstr.outlet.conc_mol[2, 'S'].value == approx(11.263,
                                                               abs=1e-3)
    assert mod.fs.cstr.outlet.conc_mol[2, 'C'].value == approx(0.4809,
                                                               abs=1e-4)
    assert mod.fs.cstr.outlet.conc_mol[2, 'E'].value == approx(0.0538,
                                                               abs=1e-4)
    assert mod.fs.cstr.outlet.conc_mol[2, 'P'].value == approx(0.4372,
                                                               abs=1e-4)
Esempio n. 4
0
def test_find_slices_in_model():
    # Define m1
    m1 = ConcreteModel()
    m1.time = Set(initialize=[1, 2, 3, 4, 5])

    m1.v1 = Var(m1.time, initialize=1)

    @m1.Block(m1.time)
    def blk(b, t):
        b.v2 = Var(initialize=1)

    # Define m2
    m2 = ConcreteModel()
    m2.time = Set(initialize=[1, 2, 3, 4, 5])

    m2.v1 = Var(m2.time, initialize=2)

    @m2.Block(m2.time)
    def blk(b, t):
        b.v2 = Var(initialize=2)

    ###

    scalar_vars_1, dae_vars_1 = flatten_dae_variables(m1, m1.time)
    scalar_vars_2, dae_vars_2 = flatten_dae_variables(m2, m2.time)

    t0_tgt = m1.time.first()
    group = NMPCVarGroup(dae_vars_1, m1.time)
    categ = VariableCategory.ALGEBRAIC
    locator = ComponentMap([(var[t0_tgt], NMPCVarLocator(categ, group, i))
                            for i, var in enumerate(dae_vars_1)])

    tgt_slices = find_slices_in_model(m1, m1.time, m2, m2.time, locator,
                                      dae_vars_2)

    dae_var_set_1 = ComponentSet(dae_vars_1)
    assert len(dae_var_set_1) == len(tgt_slices)
    assert len(tgt_slices) == len(dae_vars_2)
    for i, _slice in enumerate(tgt_slices):
        assert dae_vars_2[i].name == _slice.name
        assert _slice in dae_var_set_1
Esempio n. 5
0
def test_copy_values():
    # Define m1
    m1 = ConcreteModel()
    m1.time = Set(initialize=[1, 2, 3, 4, 5])

    m1.v1 = Var(m1.time, initialize=1)

    @m1.Block(m1.time)
    def blk(b, t):
        b.v2 = Var(initialize=1)

    # Define m2
    m2 = ConcreteModel()
    m2.time = Set(initialize=[1, 2, 3, 4, 5])

    m2.v1 = Var(m2.time, initialize=2)

    @m2.Block(m2.time)
    def blk(b, t):
        b.v2 = Var(initialize=2)

    ###

    scalar_vars_1, dae_vars_1 = flatten_dae_variables(m1, m1.time)
    scalar_vars_2, dae_vars_2 = flatten_dae_variables(m2, m2.time)

    m2.v1[2].set_value(5)
    m2.blk[2].v2.set_value(5)

    copy_values_at_time(dae_vars_1, dae_vars_2, 1, 2)

    for t in m1.time:
        if t != 1:
            assert m1.v1[t].value == 1
            assert m1.blk[t].v2.value == 1
        else:
            assert m1.v1[t].value == 5
            assert m1.blk[t].v2.value == 5
Esempio n. 6
0
    def test_2dim_set(self):
        m = ConcreteModel()
        m.time = ContinuousSet(bounds=(0, 1))

        m.v = Var(m.time, [('a', 1), ('b', 2)])

        scalar, dae = flatten_dae_variables(m, m.time)
        self.assertEqual(len(scalar), 0)
        ref_data = {
            self._hashRef(Reference(m.v[:, 'a', 1])),
            self._hashRef(Reference(m.v[:, 'b', 2])),
        }
        self.assertEqual(len(dae), len(ref_data))
        for ref in dae:
            self.assertIn(self._hashRef(ref), ref_data)
Esempio n. 7
0
    def test_indexed_block(self):
        m = ConcreteModel()
        m.time = ContinuousSet(bounds=(0,1))
        m.comp = Set(initialize=['a', 'b'])

        def bb_rule(bb, t):
            bb.dae_var = Var()

        def b_rule(b, c):
            b.bb = Block(m.time, rule=bb_rule)

        m.b = Block(m.comp, rule=b_rule)

        scalar, dae = flatten_dae_variables(m, m.time)
        self.assertEqual(len(scalar), 0)
        ref_data = {
                self._hashRef(Reference(m.b['a'].bb[:].dae_var)),
                self._hashRef(Reference(m.b['b'].bb[:].dae_var)),
                }
        self.assertEqual(len(dae), len(ref_data))
        for ref in dae:
            self.assertIn(self._hashRef(ref), ref_data)
Esempio n. 8
0
def initialize_by_element_in_range(model, time, t_start, t_end, 
        time_linking_vars=[],
        dae_vars=[],
        max_linking_range=0,
        **kwargs):
    """Function for solving a square model, time element-by-time element,
    between specified start and end times.

    Args:
        model : Flowsheet model to solve
        t_start : Beginning of timespan over which to solve
        t_end : End of timespan over which to solve

    Kwargs:
        solver : Solver option used to solve portions of the square model
        outlvl : idaes.logger output level
    """
    # TODO: How to handle config arguments here? Should this function
    # be moved to be a method of NMPC? Have a module-level config?
    # CONFIG, KWARGS: handle these kwargs through config

    solver = kwargs.pop('solver', SolverFactory('ipopt'))
    outlvl = kwargs.pop('outlvl', idaeslog.NOTSET)
    init_log = idaeslog.getInitLogger('nmpc', outlvl)
    solver_log = idaeslog.getSolveLogger('nmpc', outlvl)
    solve_initial_conditions = kwargs.pop('solve_initial_conditions', False)

    #TODO: Move to docstring
    # Variables that will be fixed for time points outside the finite element
    # when constraints for a finite element are activated.
    # For a "normal" process, these should just be differential variables
    # (and maybe derivative variables). For a process with a (PID) controller,
    # these should also include variables used by the controller.
    # If these variables are not specified, 

    # Timespan over which these variables will be fixed, counting backwards
    # from the first time point in the finite element (which will always be
    # fixed)
    # Should I specify max_linking_range as an integer number of finite
    # elements, an integer number of time points, or a float in actual time
    # units? Go with latter for now.

    # TODO: Should I fix scalar vars? Intuition is that they should already
    # be fixed.

    #time = model.time
    assert t_start in time.get_finite_elements()
    assert t_end in time.get_finite_elements()
    assert degrees_of_freedom(model) == 0

    #dae_vars = kwargs.pop('dae_vars', [])
    if not dae_vars:
        scalar_vars, dae_vars = flatten_dae_variables(model, time)
        for var in scalar_vars:
            var.fix()
        deactivate_constraints_unindexed_by(model, time)

    ncp = time.get_discretization_info()['ncp']

    fe_in_range = [i for i, fe in enumerate(time.get_finite_elements())
                            if fe >= t_start and fe <= t_end]
    t_in_range = [t for t in time if t >= t_start and t <= t_end]

    fe_in_range.pop(0)
    n_fe_in_range = len(fe_in_range)

    was_originally_active = get_activity_dict(model)
    was_originally_fixed = get_fixed_dict(model)

    # Deactivate model
    if not solve_initial_conditions:
        time_list = [t for t in time]
        deactivated = deactivate_model_at(model, time, time_list,
                outlvl=idaeslog.ERROR)
    else:
        time_list = [t for t in time if t != time.first()]
        deactivated = deactivate_model_at(model, time, time_list,
                outlvl=idaeslog.ERROR)

        assert degrees_of_freedom(model) == 0
        with idaeslog.solver_log(solver_log, level=idaeslog.DEBUG) as slc:
            results = solver.solve(model, tee=slc.tee)
        if results.solver.termination_condition == TerminationCondition.optimal:
            pass
        else:
            raise ValueError

        deactivated[time.first()] = deactivate_model_at(model, time, 
                time.first(),
                outlvl=idaeslog.ERROR)[time.first()]

    # "Integration" loop
    for i in fe_in_range:
        t_prev = time[(i-1)*ncp+1]

        fe = [time[k] for k in range((i-1)*ncp+2, i*ncp+2)]

        con_list = []
        for t in fe:
            # These will be fixed vars in constraints at t
            # Probably not necessary to record at what t
            # they occur
            for comp in deactivated[t]:
                if was_originally_active[id(comp)]:
                   comp.activate()
                   if not time_linking_vars:
                       if isinstance(comp, _ConstraintData):
                           con_list.append(comp)
                       elif isinstance(comp, _BlockData):
                           # Active here should be independent of whether block
                           # was active
                           con_list.extend(
                               list(comp.component_data_objects(Constraint,
                                                                 active=True)))

        if not time_linking_vars:
            fixed_vars = []
            for con in con_list:
                for var in identify_variables(con.expr,
                                              include_fixed=False):
                    # use var_locator/ComponentMap to get index somehow
                    t_idx = get_implicit_index_of_set(var, time)
                    if t_idx is None:
                        assert not is_in_block_indexed_by(var, time)
                        continue
                    if t_idx <= t_prev:
                        fixed_vars.append(var)
                        var.fix()
        else:
            fixed_vars = []
            time_range = [t for t in time 
                          if t_prev - t <= max_linking_range
                          and t <= t_prev]
            time_range = [t_prev]
            for _slice in time_linking_vars:
                for t in time_range:
                    #if not _slice[t].fixed:
                    _slice[t].fix()
                    fixed_vars.append(_slice[t])

        # Here I assume that the only variables that can appear in 
        # constraints at a different (later) time index are derivatives
        # and differential variables (they do so in the discretization
        # equations) and that they only participate at t_prev.
        #
        # This is not the case for, say, PID controllers, in which case
        # I should pass in a list of "complicating variables," then fix
        # them at all time points outside the finite element.
        #
        # Alternative solution is to identify_variables in each constraint
        # that is activated and fix those belonging to a previous finite
        # element. (Should not encounter variables belonging to a future
        # finite element.)
        # ^ This option is easier, less efficient
        #
        # In either case need to record whether variable was previously fixed
        # so I know if I should unfix it or not.

        for t in fe:
            for _slice in dae_vars:
                if not _slice[t].fixed:
                    # Fixed DAE variables are time-dependent disturbances,
                    # whose values should not be altered by this function.
                    _slice[t].set_value(_slice[t_prev].value)

        assert degrees_of_freedom(model) == 0

        with idaeslog.solver_log(solver_log, level=idaeslog.DEBUG) as slc:
            results = solver.solve(model, tee=slc.tee)
        if results.solver.termination_condition == TerminationCondition.optimal:
            pass
        else:
            raise ValueError

        for t in fe:
            for comp in deactivated[t]:
                comp.deactivate()

        for var in fixed_vars:
            if not was_originally_fixed[id(var)]:
                var.unfix()

    for t in time:
        for comp in deactivated[t]:
            if was_originally_active[id(comp)]:
                comp.activate()

    assert degrees_of_freedom(model) == 0
Esempio n. 9
0
def test_add_noise_at_time():
    mod = make_model(horizon=2, ntfe=20)
    time = mod.fs.time
    t0 = time.first()
    assert degrees_of_freedom(mod) == 0

    scalar_vars, dae_vars = flatten_dae_variables(mod.fs, time)
    diff_vars = [
        Reference(mod.fs.cstr.control_volume.energy_holdup[:, 'aq']),
        Reference(mod.fs.cstr.control_volume.material_holdup[:, 'aq', 'S']),
        Reference(mod.fs.cstr.control_volume.material_holdup[:, 'aq', 'E']),
        Reference(mod.fs.cstr.control_volume.material_holdup[:, 'aq', 'C']),
        Reference(mod.fs.cstr.control_volume.material_holdup[:, 'aq', 'P'])
    ]

    for t in time:
        diff_vars[0][t].setlb(290)
        diff_vars[0][t].setub(310)
        for i in range(1, 5):
            diff_vars[i][t].setlb(0)
            diff_vars[i][t].setub(1)
            # Pretend this is mole fraction...

    assert diff_vars[0][0].value == 300
    for i in range(1, 5):
        assert diff_vars[i][0].value == 0.001

    copy_values_at_time(diff_vars, diff_vars, [t for t in time if t != t0], t0)

    for seed in [4, 8, 15, 16, 23, 42]:
        random.seed(seed)
        weights = [10, 0.001, 0.001, 0.001, 0.001]
        nom_vals = add_noise_at_time(diff_vars, 0, weights=weights)

        assert nom_vals[0][0] == 300
        assert diff_vars[0][0].value != 300
        assert diff_vars[0][0].value == approx(300, abs=2)
        for i in range(1, 5):
            assert nom_vals[0][i] == 0.001
            # ^ nom_vals indexed by time, then var-index. This is confusing,
            # might need to change (or only accept one time point at a time)
            assert diff_vars[i][0].value != 0.001
            assert diff_vars[i][0].value == approx(0.001, abs=2e-4)
            # Within four standard deviations should be a safe check

        for i in range(0, 5):
            diff_vars[i][0].set_value(nom_vals[0][i])
        # Reset and try again with new seed

    # Try providing function for uniform random
    rand_fcn = random.uniform

    random_arg_dict = {
        'range_list': [(295, 305), (0.001, 0.01), (0.001, 0.01), (0.001, 0.01),
                       (0.001, 0.01)]
    }

    def args_fcn(i, val, **kwargs):
        # args_fcn expects arguments like this
        range_list = kwargs.pop('range_list', None)
        return range_list[i]

    nom_vals = add_noise_at_time(diff_vars,
                                 0.5,
                                 random_function=rand_fcn,
                                 args_function=args_fcn,
                                 random_arg_dict=random_arg_dict)

    assert nom_vals[0.5][0] == 300
    assert diff_vars[0][0.5].value != 300
    assert 295 <= diff_vars[0][0.5].value <= 305
    for i in range(1, 5):
        assert nom_vals[0.5][i] == 0.001
        assert diff_vars[i][0.5].value != 0.001
        assert 0.001 <= diff_vars[i][0.5].value <= 0.01

    # Try to get some bound violations
    random_arg_dict = {
        'range_list': [(295, 305), (1, 2), (1, 2), (1, 2), (1, 2)]
    }

    nom_vals = add_noise_at_time(diff_vars,
                                 1,
                                 random_function=rand_fcn,
                                 args_function=args_fcn,
                                 random_arg_dict=random_arg_dict,
                                 bound_strategy='push',
                                 bound_push=0.01)

    for i in range(1, 5):
        assert diff_vars[i][1].value == 0.99

    random.seed(123)
    with pytest.raises(ValueError) as exc_test:
        # Large weights - one of these lower bounds should fail...
        nom_vals = add_noise_at_time(diff_vars,
                                     1.5,
                                     bound_strategy='discard',
                                     discard_limit=0,
                                     weights=[1, 1, 1, 1, 1],
                                     sig_0=0.05)

    @pytest.mark.unit
    def test_get_violated_bounds_at_time():
        m = ConcreteModel()
        m.time = Set(initialize=[1, 2, 3])
        m.v = Var(m.time, ['a', 'b', 'c'], initialize=5)

        varlist = [
            Reference(m.v[:, 'a']),
            Reference(m.v[:, 'b']),
            Reference(m.v[:, 'c'])
        ]
        group = NMPCVarGroup(varlist, m.time)
        group.set_lb(0, 0)
        group.set_lb(1, 6)
        group.set_lb(2, 0)
        group.set_ub(0, 4)
        group.set_ub(1, 10)
        group.set_ub(2, 10)
        violated = get_violated_bounds_at_time(group, [1, 2, 3],
                                               tolerance=1e-8)
        violated_set = ComponentSet(violated)
        for t in m.time:
            assert m.v[t, 'a'] in violated_set
            assert m.v[t, 'b'] in violated_set

        violated = get_violated_bounds_at_time(group, 2, tolerance=1e-8)
        violated_set = ComponentSet(violated)
        assert m.v[2, 'a'] in violated_set
        assert m.v[2, 'b'] in violated_set
Esempio n. 10
0
    def categorize_variables(model, initial_inputs):
        """Creates lists of time-only-slices of the different types of variables
        in a model, given knowledge of which are inputs. These lists are added 
        as attributes to the model's namespace.

        Possible variable categories are:

            - INPUT --- Those specified by the user to be inputs
            - DERIVATIVE --- Those declared as Pyomo DerivativeVars, whose 
                             "state variable" is not fixed, except possibly as an
                             initial condition
            - DIFFERENTIAL --- Those referenced as the "state variable" by an
                               unfixed (except possibly as an initial condition)
                               DerivativeVar
            - FIXED --- Those that are fixed at non-initial time points. These
                        are typically disturbances, design variables, or 
                        uncertain parameters.
            - ALGEBRAIC --- Unfixed, time-indexed variables that are neither
                            inputs nor referenced by an unfixed derivative.
            - SCALAR --- Variables unindexed by time. These could be variables
                         that refer to a specific point in time (initial or
                         final conditions), averages over time, or truly 
                         time-independent variables like diameter.

        Args:
            model : Model whose variables will be flattened and categorized
            initial_inputs : List of VarData objects that are input variables
                             at the initial time point

        """
        namespace = getattr(model,
                DynamicBase.get_namespace_name())
        time = namespace.get_time()
        t0 = time.first()
        t1 = time.get_finite_elements()[1]
        deriv_vars = []
        diff_vars = []
        input_vars = []
        alg_vars = []
        fixed_vars = []

        ic_vars = []

        # Create list of time-only-slices of time indexed variables
        # (And list of VarData objects for scalar variables)
        scalar_vars, dae_vars = flatten_dae_variables(model, time)

        dae_map = ComponentMap([(v[t0], v) for v in dae_vars])
        t0_vardata = list(dae_map.keys())
        namespace.dae_vars = list(dae_map.values())
        namespace.scalar_vars = \
            NMPCVarGroup(
                list(ComponentMap([(v, v) for v in scalar_vars]).values()),
                index_set=None, is_scalar=True)
        namespace.n_scalar_vars = \
                namespace.scalar_vars.n_vars
        input_set = ComponentSet(initial_inputs)
        updated_input_set = ComponentSet(initial_inputs)

        # Iterate over initial vardata, popping from dae map when an input,
        # derivative, or differential var is found.
        for var0 in t0_vardata:
            if var0 in updated_input_set:
                input_set.remove(var0)
                time_slice = dae_map.pop(var0)
                input_vars.append(time_slice)

            parent = var0.parent_component()
            if not isinstance(parent, DerivativeVar):
                continue
            if not time in ComponentSet(parent.get_continuousset_list()):
                continue
            index0 = var0.index()
            var1 = dae_map[var0][t1]
            index1 = var1.index()
            state = parent.get_state_var()

            if state[index1].fixed:
                # Assume state var is fixed everywhere, so derivative
                # 'isn't really' a derivative.
                # Should be safe to remove state from dae_map here
                state_slice = dae_map.pop(state[index0])
                fixed_vars.append(state_slice)
                continue
            if state[index0] in input_set:
                # If differential variable is an input, then this DerivativeVar
                # is 'not really a derivative'
                continue

            deriv_slice = dae_map.pop(var0)

            if var1.fixed:
                # Assume derivative has been fixed everywhere.
                # Add to list of fixed variables, and don't remove its state variable.
                fixed_vars.append(deriv_slice)
            elif var0.fixed:
                # In this case the derivative has been used as an initial condition. 
                # Still want to include it in the list of derivatives.
                ic_vars.append(deriv_slice)
                state_slice = dae_map.pop(state[index0])
                if state[index0].fixed:
                    ic_vars.append(state_slice)
                deriv_vars.append(deriv_slice)
                diff_vars.append(state_slice)
            else:
                # Neither is fixed. This should be the most common case.
                state_slice = dae_map.pop(state[index0])
                if state[index0].fixed:
                    ic_vars.append(state_slice)
                deriv_vars.append(deriv_slice)
                diff_vars.append(state_slice)

        if not updated_input_set:
            raise RuntimeError('Not all inputs could be found')
        assert len(deriv_vars) == len(diff_vars)

        for var0, time_slice in dae_map.items():
            var1 = time_slice[t1]
            # If the variable is still in the list of time-indexed vars,
            # it must either be fixed (not a var) or be an algebraic var
            if var1.fixed:
                fixed_vars.append(time_slice)
            else:
                if var0.fixed:
                    ic_vars.append(time_slice)
                alg_vars.append(time_slice)

        namespace.deriv_vars = NMPCVarGroup(deriv_vars, time)
        namespace.diff_vars = NMPCVarGroup(diff_vars, time)
        namespace.n_diff_vars = len(diff_vars)
        namespace.n_deriv_vars = len(deriv_vars)
        assert (namespace.n_diff_vars == 
                namespace.n_deriv_vars)
                
        # ic_vars will not be stored as a NMPCVarGroup - don't want to store
        # all the info twice
        namespace.ic_vars = ic_vars
        namespace.n_ic_vars = len(ic_vars)
        #assert model.n_dv == len(ic_vars)
        # Would like this to be true, but accurately detecting differential
        # variables that are not implicitly fixed (by fixing some input)
        # is difficult
        # Also, a categorization can have no input vars and still be
        # valid for MHE

        namespace.input_vars = NMPCVarGroup(input_vars, time)
        namespace.n_input_vars = len(input_vars)

        namespace.alg_vars = NMPCVarGroup(alg_vars, time)
        namespace.n_alg_vars = len(alg_vars)

        namespace.fixed_vars = NMPCVarGroup(fixed_vars, time)
        namespace.n_fixed_vars = len(fixed_vars)

        namespace.variables_categorized = True