Пример #1
0
def parse_jacobian_declarations(
    ode,
    args,
    args_doc,
    params,
):

    jacobian_declaration = ""
    if not params.functions.jacobian.generate:
        return jacobian_declaration

    # Flatten jacobian params
    if not params.code.array.flatten:
        debug("Generating jacobian C-code, forcing jacobian array "
              "to be flattened.")
        params.code.array.flatten = True

    jacobian_declaration = jacobian_template.format(
        num_states=ode.num_full_states,
        args=args,
        args_doc=args_doc,
        jac_name=params.functions.jacobian.result_name,
        jacobian_function_name=params.functions.jacobian.function_name,
    )
    return jacobian_declaration
    def __setattr__(self, name, value):
        """
        A magic function which will register expressions and simpler
        state expressions
        """

        from modelparameters.sympytools import symbols_from_expr

        # If we are registering a protected attribute or an attribute
        # during construction, just add it to the dict
        if name[0] == "_" or not self._allow_magic_attributes:
            self.__dict__[name] = value
            return

        # If no expression is registered
        if (not isinstance(value, scalars +
                           (sp.Number, ))) and not (isinstance(
                               value, sp.Basic) and symbols_from_expr(value)):
            debug(
                "Not registering: {0} as attribut. It does not contain "
                "any symbols or scalars.".format(name), )

            # FIXME: Should we raise an error?
            return

        # Check for special expressions
        expr, TYPE = special_expression(name, self.root)

        if TYPE == INTERMEDIATE:
            self.add_intermediate(name, value)

        elif TYPE == DERIVATIVE_EXPRESSION:

            # Try getting corresponding ODEObjects
            expr_name, var_name = expr.groups()
            expr_obj = self.root.present_ode_objects.get(expr_name)
            var_obj = self.root.present_ode_objects.get(var_name)

            # If the expr or variable is not declared in this ODE we
            # register an intermediate
            if expr_obj is None or var_obj is None:
                self.add_intermediate(name, value)

            else:
                self.add_derivative(expr_obj, var_obj, value)

        elif TYPE == STATE_SOLUTION_EXPRESSION:
            self.add_state_solution(expr, value)

        elif TYPE == ALGEBRAIC_EXPRESSION:

            # Try getting corresponding ODEObjects
            (var_name, ) = expr.groups()
            var_obj = self.root.present_ode_objects.get(var_name)

            if var_obj is None:
                self.add_intermediate(name, value)
            else:
                self.add_algebraic(var_obj, value)
Пример #3
0
def exec_ode(ode_str, name, **arguments):
    """
    Execute an ode given by a str

    Arguments
    ---------
    ode_str : str
        The ode as a str
    name : str
        The name of ODE
    arguments : dict (optional)
        Optional arguments which can control loading of model
    """
    # Dict to collect declared intermediates
    intermediate_dispatcher = IntermediateDispatcher()

    # Create an ODE which will be populated with data when ode file is loaded
    ode = ODE(name, intermediate_dispatcher)

    intermediate_dispatcher.ode = ode

    debug(f"Loading {ode.name}")

    # Create namespace which the ode file will be executed in
    _init_namespace(ode, arguments, intermediate_dispatcher)

    # Write str to file
    with open("_tmp_gotrand.ode", "w") as f:
        f.write(ode_str)

    # Execute file and collect
    with open("_tmp_gotrand.ode") as f:
        exec(compile(f.read(), "_tmp_gotrand.ode", "exec"), intermediate_dispatcher)
    os.unlink("_tmp_gotrand.ode")

    # Finalize ODE
    ode.finalize()

    # Check for completeness
    if not ode.is_complete:
        warning(f"ODE mode '{ode.name}' is not complete.")

    info(f"Loaded ODE model '{ode.name}' with:")
    for what in ["full_states", "parameters"]:
        num = getattr(ode, f"num_{what}")
        info(f"{('Num ' + what.replace('_', ' ')).rjust(20)}: {num}")
    return ode
    def __call__(self, name):
        """
        Return a child component, if the component does not excist, create
        and add one
        """
        check_arg(name, str)

        # If the requested component is the root component
        if self == self.root and name == self.root.name:
            comp = self.root
        else:
            comp = self.children.get(name)

        if comp is None:
            comp = self.add_component(name)
            debug(f"Adding '{name}' component to {self}")
        else:
            self.root._present_component = comp.name

        return comp
Пример #5
0
    def _recount(self, new_count=None, dependent=None):
        """
        Method called when an object need to get a new count
        """
        old_count = self._count

        if isinstance(new_count, (int, float)):
            check_arg(new_count, (int, float), ge=0, le=ODEObject.__count)
            self._count = new_count
        elif isinstance(dependent, ODEObject):
            ODEObject.__dependent_counts[dependent._count] += 1

            # FIXME: Do not hardcode the fractional increase
            self._count = (
                dependent._count +
                ODEObject.__dependent_counts[dependent._count] * 0.00001)
        else:
            self._count = ODEObject.__count
            ODEObject.__count += 1

        debug(f"Change count of {self.name} from {old_count} to {self._count}")
Пример #6
0
    def register_ode_object(self, obj, comp, dependent=None):
        """
        Register an ODE object in the root ODEComponent
        """

        from modelparameters.sympytools import symbols_from_expr

        if self._is_finalized_ode and isinstance(obj, StateExpression):
            error("Cannot register a StateExpression, the ODE is finalized")

        # Check for existing object in the ODE
        dup_obj = self.present_ode_objects.get(obj.name)

        # If object with same name is already registered in the ode we
        # need to figure out what to do
        if dup_obj:

            try:
                dup_comp = self.object_component[dup_obj]
            except KeyError:
                dup_comp = None

            # If a state is substituted by a state solution
            if isinstance(dup_obj, State) and isinstance(obj, StateSolution):
                debug(f"Reduce state '{dup_obj}' to {obj.expr}")

            # If duplicated object is an ODE Parameter and the added object is
            # either a State or a Parameter we replace the Parameter.
            elif (isinstance(dup_obj, Parameter) and dup_comp == self
                  and comp != self and isinstance(obj, (State, Parameter))):

                timer = Timer("Replace objects")  # noqa: F841

                # Remove the object
                self.ode_objects.remove(dup_obj)

                # FIXME: Do we need to recreate all expression the objects is used in?
                # Replace the object from the object_used_in dict and update
                # the correponding expressions
                subs = {dup_obj.sym: obj.sym}
                subs = {}

                # Recursively replace object dependencies
                self._replace_object(dup_obj, obj, subs)

                # for expr in self.object_used_in[dup_obj]:
                #    updated_expr = recreate_expression(expr, subs)
                #    self.object_used_in[obj].add(updated_expr)
                #
                #    # Exchange and update the dependencies
                #    self.expression_dependencies[expr].remove(dup_obj)
                #    self.expression_dependencies[expr].add(obj)
                #
                #    # FIXME: Do not remove the dependencies
                #    #self.expression_dependencies[updated_expr] = \
                #    #            self.expression_dependencies.pop(expr)
                #    self.expression_dependencies[updated_expr] = \
                #                self.expression_dependencies[expr]
                #
                #    # Find the index of old expression and exchange it with updated
                #    old_comp = self.object_component[expr]
                #    ind = old_comp.ode_objects.index(expr)
                #    old_comp.ode_objects[ind] = updated_expr
                #
                ## Remove information about the replaced objects
                # self.object_used_in.pop(dup_obj)

            # If duplicated object is an ODE Parameter and the added
            # object is an Intermediate we raise an error.
            elif (isinstance(dup_obj, Parameter) and dup_comp == self
                  and isinstance(obj, Expression)):
                error(
                    "Cannot replace an ODE parameter with an Expression, "
                    "only with Parameters and States.", )

            # If State, Parameter or DerivativeExpression we always raise an error
            elif any(
                    isinstance(
                        oo,
                        (
                            State,
                            Parameter,
                            Time,
                            Dt,
                            DerivativeExpression,
                            AlgebraicExpression,
                            StateSolution,
                        ),
                    ) for oo in [dup_obj, obj]):
                error(
                    "Cannot register {0}. A {1} with name '{2}' is "
                    "already registered in this ODE.".format(
                        type(obj).__name__,
                        type(dup_obj).__name__,
                        dup_obj.name,
                    ), )
            else:

                # Sanity check that both obj and dup_obj are Expressions
                assert all(
                    isinstance(oo, (Expression)) for oo in [dup_obj, obj])

                # Get list of duplicated objects or an empy list
                dup_objects = self.duplicated_expressions[obj.name]
                if len(dup_objects) == 0:
                    dup_objects.append(dup_obj)
                dup_objects.append(obj)

        # Update global information about ode object
        self.present_ode_objects[obj.name] = obj
        self.object_component[obj] = comp
        self.ns.update({obj.name: obj.sym})

        # If Expression
        if isinstance(obj, Expression):

            # Append the name to the list of all ordered components with
            # expressions. If the ODE is finalized we do not update components
            if not self._is_finalized_ode:
                self._handle_expr_component(comp, obj)

            # Expand and add any derivatives in the expressions
            expression_added = False
            replace_dict = {}
            derivative_expression_list = list(obj.expr.atoms(sp.Derivative))
            derivative_expression_list.sort(key=lambda e: e.sort_key())
            for der_expr in derivative_expression_list:
                expression_added |= self._expand_single_derivative(
                    comp,
                    obj,
                    der_expr,
                    replace_dict,
                    dependent,
                )

            # If expressions need to be re-created
            if replace_dict:
                obj.replace_expr(replace_dict)

            # If any expression was added we need to bump the count of the ODEObject
            if expression_added:
                obj._recount(dependent=dependent)

            # Add dependencies between the last registered comment and
            # expressions so they are carried over in Code components
            if comp._local_comments:
                self.object_used_in[comp._local_comments[-1]].add(obj)
                self.expression_dependencies[obj].add(comp._local_comments[-1])

            # Get expression dependencies
            for sym in symbols_from_expr(obj.expr, include_derivatives=True):

                dep_obj = self.present_ode_objects[sympycode(sym)]

                if dep_obj is None:
                    error(
                        "The symbol '{0}' is not declared within the '{1}' "
                        "ODE.".format(sym, self.name), )

                # Store object dependencies
                self.expression_dependencies[obj].add(dep_obj)
                self.object_used_in[dep_obj].add(obj)

            # If the expression is a StateSolution the state cannot have
            # been used previously
            if isinstance(obj, StateSolution) and self.object_used_in.get(
                    obj.state):
                used_in = self.object_used_in.get(obj.state)
                error(
                    "A state solution cannot have been used in "
                    "any previous expressions. {0} is used in: {1}".format(
                        obj.state,
                        used_in,
                    ), )
    def __init__(
        self,
        ode,
        function_name="forward_hybrid_generalized_rush_larsen",
        delta=1e-8,
        params=None,
        stiff_state_variables=None,
    ):
        """
        Create a HybridGeneralizedRushLarsen Solver component

        Arguments
        ---------
        ode : gotran.ODE
            The parent component of this ODEComponent
        function_name : str
            The name of the function which should be generated
        delta : float
            Value to safeguard the evaluation of the Rush-Larsen step.
        params : dict
            Parameters determining how the code should be generated

        """
        check_arg(ode, ODE)

        # Call base class using empty result_expressions
        descr = f"Compute a forward step using the FE / GRL1 scheme to the {ode} ODE"
        super(HybridGeneralizedRushLarsen, self).__init__(
            "HybridGeneralizedRushLarsen",
            ode,
            function_name,
            descr,
            params=params,
            additional_arguments=["dt"],
        )

        state_names = [s.name for s in self.root.full_states]
        if stiff_state_variables is None:
            stiff_state_variables = []
        elif type(stiff_state_variables) is str:
            stiff_state_variables = stiff_state_variables.split(",")

        for s in stiff_state_variables:
            if s == "":
                continue
            assert s in state_names, f"Unknown state '{s}'"

        # Recount the expressions if representation of states are "array" as
        # then the method is not full explcit
        recount = self._params.states.representation != "array"

        # Gather state expressions and states
        state_exprs = self.root.state_expressions
        states = self.root.full_states
        result_name = self._params.states.array_name

        self.shapes[result_name] = (len(states), )

        # Get time step and start creating the update algorithm
        if self._params.states.add_offset:
            offset_str = f"{result_name}_offset"
        else:
            offset_str = ""

        dt = self.root._dt.sym
        for i, expr in enumerate(state_exprs):
            state_is_stiff = state_names[i] in stiff_state_variables

            expr_diff = expr.expr.diff(expr.state.sym)
            dependent = expr if recount else None
            if not state_is_stiff or expr_diff.is_zero:
                # FE scheme
                self.add_indexed_expression(
                    result_name,
                    (i, ),
                    expr.state.sym + dt * expr.sym,
                    offset_str,
                    dependent=dependent,
                    enum=expr.state,
                )
                continue

            linearized_name = expr.name + "_linearized"
            linearized = self.add_intermediate(
                linearized_name,
                expr_diff,
                dependent=dependent,
            )

            need_zero_div_check = not fraction_numerator_is_nonzero(expr_diff)
            if not need_zero_div_check:
                debug(
                    f"{linearized_name} cannot be zero. Skipping zero division check"
                )

            RL_term = expr.sym / linearized * (sp.exp(linearized * dt) - 1)
            if need_zero_div_check:
                RL_term = Conditional(
                    abs(linearized) > delta,
                    RL_term,
                    dt * expr.sym,
                )

            # Solve "exact" using exp
            self.add_indexed_expression(
                result_name,
                (i, ),
                expr.state.sym + RL_term,
                offset_str,
                dependent=dependent,
                enum=expr.state,
            )

        # Call recreate body with the solver expressions as the result
        # expressions
        results = {result_name: self.indexed_objects(result_name)}
        results, body_expressions = self._body_from_results(**results)
        self.body_expressions = self._recreate_body(body_expressions,
                                                    **results)
    def __init__(
        self,
        ode,
        function_name="forward_rush_larsen",
        delta=1e-8,
        params=None,
    ):
        """
        Create a RushLarsen Solver component

        Arguments
        ---------
        ode : gotran.ODE
            The parent component of this ODEComponent
        function_name : str
            The name of the function which should be generated
        delta : float
            Value to safeguard the evaluation of the Rush-Larsen step.
        params : dict
            Parameters determining how the code should be generated
        """

        timer = Timer("Create RushLarsen expressions")
        check_arg(ode, ODE)

        if ode.is_dae:
            error("Cannot generate a Rush-Larsen forward step for a DAE.")

        # Call base class using empty result_expressions
        descr = f"Compute a forward step using the Rush-Larsen scheme to the {ode} ODE"
        super(RushLarsen, self).__init__(
            "RushLarsen",
            ode,
            function_name,
            descr,
            params=params,
            additional_arguments=["dt"],
        )

        # Recount the expressions if representation of states are "array" as
        # then the method is not full explcit
        recount = self._params.states.representation != "array"

        # Gather state expressions and states
        state_exprs = self.root.state_expressions
        states = self.root.full_states

        result_name = self._params.states.array_name
        self.shapes[result_name] = (len(states), )

        # Get time step and start creating the update algorithm
        if self._params.states.add_offset:
            offset_str = f"{result_name}_offset"
        else:
            offset_str = ""

        might_take_time = len(states) >= 10

        if might_take_time:
            info(
                f"Calculating derivatives of {ode.name}. Might take some time..."
            )
            sys.stdout.flush()

        dt = self.root._dt.sym
        for i, expr in enumerate(state_exprs):

            dependent = expr if recount else None

            # Diagonal jacobian value
            time_diff = Timer("Differentiate state_expressions for RushLarsen")
            expr_diff = expr.expr.diff(expr.state.sym)
            del time_diff

            # print expr.state.sym, expr_diff, expr_diff.args
            if expr_diff and expr.state.sym not in expr_diff.args:

                linearized_name = expr.name + "_linearized"
                linearized = self.add_intermediate(
                    linearized_name,
                    expr_diff,
                    dependent=dependent,
                )

                need_zero_div_check = not fraction_numerator_is_nonzero(
                    expr_diff)
                if not need_zero_div_check:
                    debug(
                        f"{linearized_name} cannot be zero. Skipping zero division check",
                    )

                RL_term = expr.sym / linearized * (sp.exp(linearized * dt) - 1)
                if need_zero_div_check:
                    RL_term = Conditional(
                        abs(linearized) > delta,
                        RL_term,
                        dt * expr.sym,
                    )

                # Solve "exact" using exp
                self.add_indexed_expression(
                    result_name,
                    (i, ),
                    expr.state.sym + RL_term,
                    offset_str,
                    dependent=dependent,
                    enum=expr.state,
                )

            else:

                # Explicit Euler step
                self.add_indexed_expression(
                    result_name,
                    (i, ),
                    expr.state.sym + dt * expr.sym,
                    offset_str,
                    dependent=dependent,
                    enum=expr.state,
                )

        if might_take_time:
            info(" done")

        # Call recreate body with the solver expressions as the result
        # expressions
        del timer
        results = {result_name: self.indexed_objects(result_name)}
        results, body_expressions = self._body_from_results(**results)
        self.body_expressions = self._recreate_body(body_expressions,
                                                    **results)
Пример #9
0
    def __setitem__(self, name, value):
        """
        This is only for expressions
        """

        from modelparameters.sympytools import symbols_from_expr

        timer = Timer("Namespace dispatcher")  # noqa: F841
        # Set the attr of the ODE
        # If a scalar or a sympy number or it is a sympy.Basic consisting of
        # sp.Symbols

        if (
            isinstance(value, scalars)
            or isinstance(value, sp.Number)
            or (isinstance(value, sp.Basic) and symbols_from_expr(value))
        ):

            # Get source which triggers the insertion to the global namespace
            frame = inspect.currentframe().f_back
            lines, lnum = inspect.findsource(frame)
            # Try getting the code
            try:
                code = lines[frame.f_lineno - 1].strip()

                # Concatenate lines with line continuation symbols
                prev = 2
                while (
                    frame.f_lineno - prev >= 0
                    and len(lines[frame.f_lineno - prev]) >= 2
                    and lines[frame.f_lineno - prev][-2:] == "\\\n"
                ):
                    code = lines[frame.f_lineno - prev][:-2].strip() + code
                    prev += 1
            except Exception:
                code = ""

            # Check if the line includes a for statement
            # Here we strip op potiential code comments after the main for
            # statement.
            if re.search(_for_template, code.split("#")[0].strip()) or re.search(
                _no_intermediate_template,
                code,
            ):

                debug(f"Not registering '{name}' as an intermediate.")

                # If so just add the value to the namespace without
                # registering the intermediate
                dict.__setitem__(self, name, value)

            else:

                del timer

                # Add obj to the present component
                setattr(self.ode.present_component, name, value)

        else:
            debug(f"Not registering '{name}' as an intermediate.")

            # If no ode attr was generated we just add the value to the
            # namespace
            dict.__setitem__(self, name, value)
Пример #10
0
def _load(filename, name, **arguments):
    """
    Load an ODE or Cell from file and return the instance

    The method looks for a file with .ode extension.

    Arguments
    ---------
    filename : str
        Name of the ode file to load
    name : str (optional)
        Set the name of ODE (defaults to filename)
    arguments : dict (optional)
        Optional arguments which can control loading of model
    """
    timer = Timer("Load ODE")  # noqa: F841
    filename = Path(filename).with_suffix(".ode").absolute()
    # Extract name from filename
    name = filename.stem

    # Execute the file
    if not filename.is_file():
        error(f"Could not find '{filename}'")

    # Copy file temporary to current directory
    basename = Path(filename.name).absolute()
    copyfile = False
    if basename != filename:
        shutil.copy(filename, basename)
        filename = basename
        name = filename.stem
        copyfile = True

    # If a Param is provided turn it into its value
    for key, value in list(arguments.items()):
        if isinstance(value, Param):
            arguments[key] = value.getvalue()

    # Dict to collect namespace
    intermediate_dispatcher = IntermediateDispatcher()

    # Create an ODE which will be populated with data when ode file is loaded

    ode = ODE(name, intermediate_dispatcher)

    intermediate_dispatcher.ode = ode

    debug(f"Loading {ode.name}")

    # Create namespace which the ode file will be executed in
    _init_namespace(ode, arguments, intermediate_dispatcher)

    # Execute file and collect
    with open(filename, "r") as f:
        exec(compile(f.read(), filename, "exec"), intermediate_dispatcher)

    # Finalize ODE
    ode.finalize()

    # Check for completeness
    if not ode.is_complete:
        warning(f"ODE model '{ode.name}' is not complete.")

    info(f"Loaded ODE model '{ode.name}' with:")
    for what in ["full_states", "parameters"]:
        num = getattr(ode, f"num_{what}")
        info(f"{('Num ' + what.replace('_', ' ')).rjust(20)}: {num}")
    if copyfile:
        os.unlink(filename)

    return ode
Пример #11
0
    def _body_from_cse(self, **results):

        timer = Timer(f"Compute common sub expressions for {self.name}")  # noqa: F841

        (
            orig_result_expressions,
            result_names,
            expanded_result_exprs,
        ) = self._expanded_result_expressions(**results)

        state_offset = self._params["states"]["add_offset"]

        # Collect results and body_expressions
        body_expressions = []
        new_results = defaultdict(list)

        might_take_time = len(orig_result_expressions) >= 40

        if might_take_time:
            info(
                "Computing common sub expressions for {0}. Might take "
                "some time...".format(self.name),
            )
            sys.stdout.flush()

        # Call sympy common sub expression reduction
        cse_exprs, cse_result_exprs = cse(
            expanded_result_exprs,
            symbols=sp.numbered_symbols("cse_"),
            optimizations=[],
        )

        # Map the cse_expr to an OrderedDict
        cse_exprs = OrderedDict(cse_expr for cse_expr in cse_exprs)

        # Extract the symbols into a set for fast comparison
        cse_syms = set((sym for sym in cse_exprs))

        # Create maps between cse_expr and result expressions trying
        # to optimized the code by weaving in the result expressions
        # in between the cse_expr

        # A map between result expr and name and indices so we can
        # construct IndexedExpressions
        result_expr_map = defaultdict(list)

        # A map between last cse_expr used in a particular result expr
        # so that we can put the result expression right after the
        # last cse_expr it uses.
        last_cse_expr_used_in_result_expr = defaultdict(list)

        # Result expressions that does not contain any cse_sym
        result_expr_without_cse_syms = []

        # A map between cse_sym and its substitutes
        cse_subs = {}

        for ind, (orig_result_expr, result_expr) in enumerate(
            zip(orig_result_expressions, cse_result_exprs),
        ):

            # Collect information so that we can recreate the result
            # expression from
            result_expr_map[result_expr].append(
                (
                    result_names[orig_result_expr],
                    orig_result_expr.indices
                    if isinstance(orig_result_expr, IndexedExpression)
                    else ind,
                ),
            )

            # If result_expr does not contain any cse_sym
            if not any(cse_sym in cse_syms for cse_sym in result_expr.atoms()):
                result_expr_without_cse_syms.append(result_expr)

            else:

                # Get last cse_sym used in result expression
                last_cse_sym = sorted(
                    (cse_sym for cse_sym in result_expr.atoms() if cse_sym in cse_syms),
                    key=cmp_to_key(lambda a, b: cmp(int(a.name[4:]), int(b.name[4:]))),
                )[-1]

                if result_expr not in last_cse_expr_used_in_result_expr[last_cse_sym]:
                    last_cse_expr_used_in_result_expr[last_cse_sym].append(result_expr)

        debug(
            "Found {0} result expressions without any cse_syms.".format(
                len(result_expr_without_cse_syms),
            ),
        )

        # print ""
        # print "LAST cse_syms:", last_cse_expr_used_in_result_expr.keys()

        cse_cnt = 0
        atoms = [state.sym for state in self.root.full_states]
        atoms.extend(param.sym for param in self.root.parameters)

        # Collecte what states and parameters has been used
        used_states = set()
        used_parameters = set()

        self.add_comment(
            "Common sub expressions for the body and the " "result expressions",
        )
        body_expressions.append(self.ode_objects[-1])

        # Register the common sub expressions as Intermediates
        for cse_sym, expr in list(cse_exprs.items()):

            # print cse_sym, expr

            # If the expression is just one of the atoms of the ODE we
            # skip the cse expressions but add a subs for the atom We
            # also skip Relationals and Piecewise as the type checking
            # in Piecewise otherwise kicks in and destroys things for
            # us.
            if expr in atoms or isinstance(
                expr,
                (sp.Piecewise, sp.relational.Relational, sp.relational.Boolean),
            ):
                cse_subs[cse_sym] = expr.xreplace(cse_subs)
            else:
                # Add body expression as an intermediate expression
                sym = self.add_intermediate(f"cse_{cse_cnt}", expr.xreplace(cse_subs))
                obj = self.ode_objects.get(sympycode(sym))
                for dep in self.root.expression_dependencies[obj]:
                    if isinstance(dep, State):
                        used_states.add(dep)
                    elif isinstance(dep, Parameter):
                        used_parameters.add(dep)
                cse_subs[cse_sym] = sym
                cse_cnt += 1
                body_expressions.append(obj)

            # Check if we should add a result expressions
            if last_cse_expr_used_in_result_expr[cse_sym]:

                # Iterate over all registered result expr for this cse_sym
                for result_expr in last_cse_expr_used_in_result_expr.pop(cse_sym):

                    for result_name, indices in result_expr_map[result_expr]:

                        # Replace pure state and param expressions
                        # print cse_subs, result_expr
                        exp_expr = result_expr.xreplace(cse_subs)

                        sym = self.add_indexed_expression(
                            result_name,
                            indices,
                            exp_expr,
                            add_offset=state_offset,
                        )

                        expr = self.ode_objects.get(sympycode(sym))

                        for dep in self.root.expression_dependencies[expr]:
                            if isinstance(dep, State):
                                used_states.add(dep)
                            elif isinstance(dep, Parameter):
                                used_parameters.add(dep)

                        # Register the new result expression
                        new_results[result_name].append(expr)
                        body_expressions.append(expr)

        if might_take_time:
            info(" done")

        # Sort used state, parameters and expr
        self.used_states = sorted(used_states)
        self.used_parameters = sorted(used_parameters)

        return new_results, body_expressions