Deltares / rtc-tools

The Deltares toolbox for control and optimization of environmental systems.
GNU Lesser General Public License v3.0
0 stars 2 forks source link

Draft: Enable adding custom equations to a simulation problem #1635

Closed SGeeversAtVortech closed 1 month ago

SGeeversAtVortech commented 1 month ago

In GitLab by @SGeeversAtVortech on Oct 10, 2023, 11:58

Enable users to add custom casadi equations to their simulation problem. This can be useful when certain equations are not supported by modelica or pymoca.

SGeeversAtVortech commented 1 month ago

In GitLab by @SGeeversAtVortech on Oct 10, 2023, 11:58

requested review from @k-horvath-deltares

SGeeversAtVortech commented 1 month ago

In GitLab by @SGeeversAtVortech on Oct 10, 2023, 14:19

requested review from @vreeken

SGeeversAtVortech commented 1 month ago

In GitLab by @SGeeversAtVortech on Oct 10, 2023, 14:19

requested review from @baayen

SGeeversAtVortech commented 1 month ago

In GitLab by @vreeken on Oct 10, 2023, 22:08

Hmm, I'm not sure the whole "CustomResidual" approach is needed. I think I have a preference for something a bit more similar to optimization, e.g. an API like:

# Not needed for this example, but just to show you can also easily add additional variables that don't appear in any equations in Modelica yet
# def extra_variables():
#   return [ca.MX.sym("y")]

def extra_equations():
    variables = self.get_variables()

    y = variables["y"]
    x = variables["x"]

    return [y - (-2 / 3600 * x)]  # To scale properly, divide by (y.nominal * x.nominal)**0.5

# Method already exists, just to show that you can/should scale these extra variables
def get_variable_nominal(self, v):
    if v == "y":
        return 10.0
    else:
        return super().get_variable_nominal()

and then in SimulationProblem:

class SimulationProblem:
    def __init__(self, *args):
        ...

        extra_vars = self.extra_variables()

        # TODO: Some checks on type, and size of the extra_vars

        self.__sym_list = (
            self.__mx["states"]
            + self.__mx["algebraics"]
            + self.__mx["derivatives"]
            + self.__mx["time"]
            + self.__mx["constant_inputs"]
            + self.__mx["parameters"]
            + extra_vars
        )

        ...

    def initialize(self, config_file=None):

        ...

        extra_equations = self.extra_equations()

        # TODO: checks

        equality_constraints = ca.vertcat(self.__dae_residual, self.__initial_residual, extra_equations)

        ...

That way, we can also properly scale extra variables with their nominals, and generally just have a common code path between Modelica variables and user-defined variables.

Disclaimer: haven't tested that this actually works Pushed it a little further (with a few hacks/todos remaining) in !463

SGeeversAtVortech commented 1 month ago

In GitLab by @k-horvath-deltares on Oct 17, 2023, 08:18

Commented on 1caf4323127f2ff83ebf9c0a0b083d517a757b54

Method 2 is still the standard RTC-Tools method that works very well for a lot of cases. Method 1 avoids instabilities with lookup tables, but it is very easy to create problems with it if you do not use it carefully. Therefore I would only encourage it for expert users and lookup tables (for which we might further develop a use case). I would write "Method 1 using custom explicit variable updates" and "Method 2 using custom residuals". Also write something: adding more equations with method 2 can lead to contradictions / non-solvable systems, be aware the consequenties.

SGeeversAtVortech commented 1 month ago

In GitLab by @SGeeversAtVortech on Oct 31, 2023, 08:45

marked this merge request as draft

SGeeversAtVortech commented 1 month ago

In GitLab by @SGeeversAtVortech on Nov 7, 2023, 15:31

This PR has been replaced by !463.