Search code examples
pythoncontrolsgekkooptimal

How to ensure a Step Function Control in GEKKO and confusion about MV_TYPE


I have a simple optimal control problem where I have to find a step function f(t) to maximize an objective functional (IMODE=6). Now f(t) is defined for t in [0, 10) such that f takes on 10 different equi-spaced values. (i.e. f(t)=f0 for 0 ≤ t < 1, etc.). Moreover f itself takes on only integer values in between 0 and 10 inclusive.

To start, I took m.time=np.linspace(0, 10, 11) to have [0, 1, 2, ..., 10] be the points in my time mesh. Then I took f=m.MV(lb=0, ub=10, integer=True) and solved with APOPT using m.options.SOLVER = 1 and just ignored the value of f(10).

This resolves fine and I am able to acquire an integer solution. The result of this, it appears, is a linear interpolant of f instead of the step-function interpolant of f that I wanted. After reading documentation, I thought that MV_TYPE was the flag that I needed. So I tried switching f to a step-function by setting m.options.MV_TYPE=0 but that gave me the same result as obtained by m.options.MV_TYPE=1. (Telling me that I completely misunderstand what MV_TYPE is supposed to do.) So for my first question: what even is the MV_TYPE flag doing? My read was that MV_TYPE=0 interpolates all MV variables with a step-function and MV_TYPE=1 interpolates all MV variables with a linear interpolant. However, two separate runs with both options for MV_TYPE yields identical solutions with identical objective functional values.

Secondly, is there some better way to do what I want? (Force my control to take the form of a discontinuous step function). I saw in the documentation (example #17 of 18) that the appropriate way to make a step function is basically to make the time mesh fine enough to cover the seam. Indeed, something like m.time=[0, 0.999, 1, 1.999, 2, 2.999, ..., 9, 9.999] would be a reasonable mesh for me. However, then my f=m.MV line would result in f having 20 independent values (instead of the 10 that I would like). Is there anyway to force the MV to take the same value at the time points t=0 and t=0.999 (and the same value at t=1 and t=1.999 etc.) in order to ensure that my function is a step-function?

I can't help but feel like I'm missing something obvious here!


Solution

  • Your understanding of MV_TYPE is correct. From the document for MV_TYPE:

    MV_TYPE specifies either a zero order hold (0) or a first order linear (1) interpolation between the MV endpoints. When the MV_STEP_HOR is two or greater, the MV_TYPE is applied only to each segment where adjustments are allowed. The MV segment is otherwise equal to the prior time segment. The MV_TYPE only influences the solution when the number of NODES is between 3 and 6. It is not important when NODES=2 because there are no interpolation nodes between the endpoints.

    It is likely giving the same answer because m.options.NODES=2 by default. Setting m.options.NODES>=3 will likely cause problems for integer type MVs with MV_TYPE=1 because the linear interpolating points also need to be integers. If you still want to use NODES=2 but need more simulation resolution then try something like:

    m.time = [0,0.5,0.9999,1,1.5,1.9999,2,2.5,2.9999,3]