Search code examples
pythonattributesmvgekko

What GEKKO model construct(s) or modelling strategy can I use to fix the UPPER value or even status attribute of MV at a specific time


I plan to use GEKKO to solve a dynamic production scheduling problem involving a supply, process, consume process flow with hold-up (storage) between some steps. I want to maximise some objective over the horizon. However, at some timesteps there may be pre-defined limitations on processing capacities of some unit operations. While I can use the fix(var,val,pos) function to fix the variable entirely at pos, constraining it on the UPPER side better represent what I want to accomplish and will probably yield a different solution in some scenarios.
Here are some toy problem code, that does not contain the dynamics yet):

"""
Simple toy problem to test flexibillity of limiting MV's at certain time points in the
horizon without fixing them specifically, i.e. leave one bound unconstrained.
"""

from gekko import GEKKO

m=GEKKO(remote=False)
m.time=[0,1,2,3,4]  #Use 5 discrete points
m.options.CV_TYPE = 1

supply2=m.Param(5.5)  #Supply of stream2 available to a separation unit op
recovery=m.Const(value=0.92)  #Extraction efficiency of unit op
feed1=m.MV(20,lb=15,ub=40)  #define feed 1 as an independent variable
feed2=m.MV(5,lb=0,ub=10) #define feed 2 as an independent variable

feed1.COST=1  #cost of feed stream 1
feed2.COST=1.5 #cost of feed stream 2

feed1.STATUS=1  #use feed1 in optimisation
feed2.STATUS=1  #use feed2 in optimisation

ovhds=m.CV(30) #define ovhds of unit op as dependent variable
ovhds.STATUS=1 #use in Objective function
ovhds.SPLO=40  #low limit for dependent variable
ovhds.SPHI=50  #high limit for dependenent variable
ovhds.COST=-2  # negative cost (aka profit) from extracted stream
feed1.UPPER=48 #set overall upper limit of 48 for feed1 MV
m.fix(feed1,47,2)  #fix feed 1 at a point pos=2 in the horizon
#TODO: add dynamics e.g. differential equations to model inventory volumes.

supply2_flared=m.Intermediate(feed2-supply2)  #another independent variable
total_feed=m.Intermediate(feed1+feed2)  #the total intake of feed

m.Equation(ovhds==total_feed*recovery)  #define relationship between dependent and independent variable

m.options.IMODE=6 #dynamic control, dynamics and dynamic constraints to be added as Equations later.
m.solve()

print("Feed1",feed1.value)
print("Feed2", feed2.value)
print("Product", ovhds.value)


Solution

  • Define a time-varying parameter such as feed1_ub and include an inequality constraint with m.Equation().

    feed1_ub = m.Param([48,48,30,48,48])
    m.Equation(feed1<=feed1_ub)
    

    Here is a complete script:

    from gekko import GEKKO
    
    m=GEKKO(remote=False)
    m.time=[0,1,2,3,4]  #Use 5 discrete points
    m.options.CV_TYPE = 1
    
    supply2=m.Param(5.5)  #Supply of stream2 available to a separation unit op
    recovery=m.Const(value=0.92)  #Extraction efficiency of unit op
    feed1=m.MV(20,lb=15,ub=40)  #define feed 1 as an independent variable
    feed2=m.MV(5,lb=0,ub=10) #define feed 2 as an independent variable
    
    feed1.COST=1  #cost of feed stream 1
    feed2.COST=1.5 #cost of feed stream 2
    
    feed1.STATUS=1  #use feed1 in optimisation
    feed2.STATUS=1  #use feed2 in optimisation
    
    ovhds=m.CV(30) #define ovhds of unit op as dependent variable
    ovhds.STATUS=1 #use in Objective function
    ovhds.SPLO=40  #low limit for dependent variable
    ovhds.SPHI=50  #high limit for dependenent variable
    ovhds.COST=-2  # negative cost (aka profit) from extracted stream
    feed1.UPPER=48 #set overall upper limit of 48 for feed1 MV
    
    supply2_flared=m.Intermediate(feed2-supply2)  #another independent variable
    total_feed=m.Intermediate(feed1+feed2)  #the total intake of feed
    
    m.Equation(ovhds==total_feed*recovery)  #define relationship between dependent and independent variable
    
    feed1_ub = m.Param([48,48,30,48,48])
    m.Equation(feed1<=feed1_ub)
    
    m.options.IMODE=6 #dynamic control, dynamics and dynamic constraints to be added as Equations later.
    m.solve()
    
    print("Feed1",feed1.value)
    print("Feed2", feed2.value)
    print("Product", ovhds.value)
    

    Solution

    EXIT: Optimal Solution Found.
    
     The solution was found.
    
     The final value of the objective function is  -92.03399437705355
     
     ---------------------------------------------------
     Solver         :  IPOPT (v3.12)
     Solution time  :  0.043 sec
     Objective      :  -92.03399324684949
     Successful solution
     ---------------------------------------------------
     
    
    Feed1 [20.0, 48.0, 30.000000009, 48.0, 48.0]
    Feed2 [5.0, 6.3478260396, 10.0, 6.3478260399, 6.3478260394]
    Product [30.0, 49.999999965, 36.8000001, 49.999999965, 49.999999965]