Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

System blocks are executed multiple times during input_output_response #933

Open
frapaoli opened this issue Oct 10, 2023 · 5 comments
Open

Comments

@frapaoli
Copy link

I'm using the library to build and simulate feedback control systems, which are made up of TransferFunction, StateSpace and NonlinearIOSystem blocks linked together.
During some simulations, I noticed a problem: for each single time sample of the simulation, each system block gets executed more than once. For example, assuming that my feedback control system (with sampling time T=1s) only has two blocks (the plant P and the controller C), at time t=0s I would expect that the code inside P and C gets executed just once, but in reality they get executed multiple times before the simulation goes on to the time sample t=1s. I also noticed that, during the "extra" blocks executions, the inputs of the blocks are all set to zero, no matter the inputs/outputs of the other blocks.

Is anyone experiencing the same problem? Am I possibly using the library in a wrong way? Does anyone have an example of feedback control system where it's verified that this problem doesn't occur?
Thank you

@sawyerbfuller
Copy link
Contributor

Hi @francesco-paoli-leonardi , here is an example that uses interconnect to connect DT and nonlinear systems (and I just now realized that for some reason it doesn’t appear in the official list of examples) https://github.com/python-control/python-control/blob/main/examples/simulating_discrete_nonlinear.ipynb

when creating nonlinear systems that will be used with the simulator, one to be aware of is that your P and C in general have two functions that get called: the update (dynamics) function and the output function. I have noticed that the output function may get called repeatedly but your update function should get called once per time increment. In other words, make sure your output function has no side-effects.

@murrayrm
Copy link
Member

Are you systems discrete time systems or continuous time systems (with a fix set of times passed as the time steps at which the input is defined)?

For continuous time systems, the integration algorithms (which come from SciPy) will often call a single block multiple times as the algorithm tries to sort out the right step size to use. So you can definitely get multiple calls to the right hand side of the differential equation (which then traces down to calls for the update function for each block). The function that controls all of this is scipy.integrate.solve_ivp.

For discrete time systems, the code that executes the simulation is pretty simple (from nlsys.input_output_response):

        for t in t_eval:
            # Store the current input, state, and output
            soln.y.append(x)
            u.append(ufun(t))
            y.append(sys._out(t, x, u[-1]))

            # Update the state for the next iteration
            x = sys._rhs(t, x, u[-1])

The call to sys._rhs goes through each block and calls the update function for those blocks.

As @sawyerbfuller notes above, the output functions can get called multiple times. In particular, to allow feedthrough terms to propagate correctly, there is a call to a function sys._compute_static_io that calls the output functions of all blocks multiple times to make sure that any direct feedthrough terms (e.g., the D matrix in a linear system) are properly accounted for.

In terms of the specific behavior that you note above, where there are calls with the inputs set to zero: is that for the update functions or for the output functions? If for the output functions, it may be coming from sys._compute_static_io.

Finally, is there an issue with simulation where you don't think it is carrying out the right calculation?

@frapaoli
Copy link
Author

Thank you both for your clarifications.

Replying to @sawyerbfuller : my problem is exactly what you pointed out, i.e., the output function of my P and C system blocks generally have side-effects when they are executed more than once per simulation time step and/or when they receive "unexpected" values in input. To avoid side-effects, I'm building all my feedback-control system blocks such that all "meaningful" dynamics is embedded in the update function, while the output function behaves just like an identity matrix (or something similar that has no strange behaviors). Still, I'm looking for a way (if is there any) to make the output function of each system block execute just once per simulation time step.

Replying to @murrayrm : as you pointed out, I forgot to mention that all blocks of my feedback-control system are discrete-time (all with same sampling time) and, in my case, the _rhs function executes a scipy.integrate.solve_ivp function that I personally set up in my code, used to update the system blocks at each discrete time step. Also, my system blocks receive all-zeros inputs only during the "extra" executions of the output function, and these all-zeros inputs usually create side-effects in my blocks' output function. Generally, a part from the "extra" executions, the simulation does its job are correctly computes the system evolution over time, but, if the blocks' output function is computationally heavy, their multiple executions per time step makes the simulation slower with respect to what it could potentially be.

@sawyerbfuller
Copy link
Contributor

@francesco-paoli-leonardi Interesting idea to wrap a solve_ivp inside your discrete-time dynamics to get accurate simulation. In the example I linked to above, I used a similar (less accurate but much faster) approach of discretizing the nonlinear plant using a fixed-step integrator. Someday python-control will get a way to use variable-step integrators with a fixed-step controller model, perhaps using a different integrator (e.g. LSODA method in solve_ivp?) but that is a future project. That example is meant to be an intermediary solution; perhaps if you get something working you might consider adding an example notebook.

Regarding how to get the output to be called only once. My current understanding is that the output function should in general never have any side effects (such as callling a numerical integrator), but I am curious to see what application you have in mind that might need that.

In the meantime, the other option is to perform the numerical integration yourself, it is only a few lines for a discrete-time system. You can use the dynamics and output methods (which are there for that exact purpose).

@frapaoli
Copy link
Author

@sawyerbfuller To answer your curiosity: the side-effects I have when executing the output function of a system block multiple times are maily:

  • when computing a discrete-time integral, the integration gets performed many more times than needed.
  • when implementing (for example) low-pass filters that make use of sliding-window buffers, the buffer window slides more than once per simulation iteration.

As I said in my previous message, I found a simple workaround for it:

I'm building all my feedback-control system blocks such that all "meaningful" dynamics is embedded in the update function, while the output function behaves just like an identity matrix (or something similar that has no strange behaviors)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants