3

I have an RTO problem that I want to solve for multiple simulated timesteps with some time-depended parameters. However, I'm struggling with the run-time and noticed that the total system time is relatively large compared to the actual solve time. I was therefore trying to reduce the total parse time, as all the equations remain the same - yet "only" the values of some parameters change with time. A simple example below:

#parameters from simulation
demand = 100 

#do RTO
from gekko import GEKKO
    
# first, create the model
m = GEKKO(remote=False)
    
# declare additional decision variables
m.u = m.Var(lb=5, ub=25)
m.v = m.Var(lb=0, ub=100)
m.w = m.Var(lb=0, ub=50)
m.b = m.Var(lb=0, ub=1, integer=True)
m.demand = m.Param(demand)
    
# now add the objective and the constraints
m.Minimize((1-0.8)*m.u*m.b+(1-0.9)*m.v+(1-0.7)*m.w)
m.Equation(m.u*m.b >= 10)
m.Equation(m.u*m.b + m.v + m.w == m.demand)
    
m.options.SOLVER=1
m.options.DIAGLEVEL = 1
    
m.solve() 

then I capture the results, execute them in the simulation and move on to the next timestep. Now I could just, execute all code above again - with updated parameters (let's say the demand is now 110). But this results in the before mentioned long run-time (the RTO problem needs to be build from scratch every time, while only some parameters change). So I thought the following could work:

m.demand.VALUE = 110
m.solve()

While this does work. It doesn't seem to improve the run-time (total parse time is still relatively long). Below are the display outputs of the actual problem.

First time solving the RTO problem.

 ----------------------------------------------------------------
 APMonitor, Version 1.0.0
 APMonitor Optimization Suite
 ----------------------------------------------------------------
 
 Called files( 55 )
 files: overrides.dbs does not exist
 
 Run id   : 2022y11m03d13h18m21.919s
 
 COMMAND LINE ARGUMENTS
 coldstart:  0
 imode    :  3
 dbs_read :  T
 dbs_write:  T
 specs    :  T
 
 rto selected
 Called files( 35 )
 READ info FILE FOR VARIABLE DEFINITION: gk_model6.info
 SS MODEL INIT  0
 Parsing model file gk_model6.apm
 Read model file (sec): 0.6602
 Initialize constants (sec): 0.
 Determine model size (sec): 0.4170999999999999
 Allocate memory (sec): 0.
 Parse and store model (sec): 0.45140000000000025
 
 --------- APM Model Size ------------
 Each time step contains
   Objects      :  247
   Constants    :  0
   Variables    :  752
   Intermediates:  249
   Connections  :  741
   Equations    :  745
   Residuals    :  496
 
 Error checking (sec): 0.17809999999999993
 Compile equations (sec): 1.9933000000000003
 Check for uninitialized intermediates (sec): 0.
 ------------------------------------------------------
 Total Parse Time (sec): 3.7062
 SS MODEL INIT  1
 SS MODEL INIT  2
 SS MODEL INIT  3
 SS MODEL INIT  4
 Called files( 31 )
 READ info FILE FOR PROBLEM DEFINITION: gk_model6.info
 Called files( 6 )
 Files(6): File Read rto.t0 F
 files: rto.t0 does not exist
 Called files( 51 )
 Read DBS File defaults.dbs
 files: defaults.dbs does not exist
 Called files( 51 )
 Read DBS File gk_model6.dbs
 files: gk_model6.dbs does not exist
 Called files( 51 )
 Read DBS File measurements.dbs
 Called files( 51 )
 Read DBS File overrides.dbs
 files: overrides.dbs does not exist
 Number of state variables:    1240
 Number of total equations: -  989
 Number of slack variables: -  0
 ---------------------------------------
 Degrees of freedom       :    251
 
 ----------------------------------------------
 Steady State Optimization with APOPT Solver
 ----------------------------------------------
Iter:     1 I:  0 Tm:      1.20 NLPi:   45 Dpth:    0 Lvs:    3 Obj:  9.86E-02 Gap:       NaN
--Integer Solution:   2.32E-01 Lowest Leaf:   9.86E-02 Gap:   1.34E-01
Iter:     2 I:  0 Tm:      0.06 NLPi:    4 Dpth:    1 Lvs:    2 Obj:  2.32E-01 Gap:  1.34E-01
Iter:     3 I:  0 Tm:      0.23 NLPi:    6 Dpth:    1 Lvs:    2 Obj:  2.16E-01 Gap:  1.34E-01
Iter:     4 I:  0 Tm:      0.44 NLPi:   12 Dpth:    1 Lvs:    3 Obj:  1.60E-01 Gap:  1.34E-01
--Integer Solution:   2.32E-01 Lowest Leaf:   1.60E-01 Gap:   7.21E-02
Iter:     5 I:  0 Tm:      0.20 NLPi:    6 Dpth:    2 Lvs:    2 Obj:  1.01E+00 Gap:  7.21E-02
--Integer Solution:   2.06E-01 Lowest Leaf:   2.06E-01 Gap:   0.00E+00
Iter:     6 I:  0 Tm:      0.20 NLPi:    5 Dpth:    2 Lvs:    2 Obj:  2.06E-01 Gap:  0.00E+00
 Successful solution
 
 ---------------------------------------------------
 Solver         :  APOPT (v1.0)
 Solution time  :  2.3522999999999996 sec
 Objective      :  0.20599966381706797
 Successful solution
 ---------------------------------------------------
 
 Called files( 2 )
 Called files( 52 )
 WRITE dbs FILE
 Called files( 56 )
 WRITE json FILE
Timer #     1       6.57/       1 =       6.57 Total system time
Timer #     2       2.35/       1 =       2.35 Total solve time
Timer #     3       0.01/     156 =       0.00 Objective Calc: apm_p
Timer #     4       0.01/      78 =       0.00 Objective Grad: apm_g
Timer #     5       0.01/     156 =       0.00 Constraint Calc: apm_c
Timer #     6       0.00/       0 =       0.00 Sparsity: apm_s
Timer #     7       0.00/       0 =       0.00 1st Deriv #1: apm_a1
Timer #     8       0.01/      78 =       0.00 1st Deriv #2: apm_a2
Timer #     9       0.42/       1 =       0.42 Custom Init: apm_custom_init
Timer #    10       0.00/       1 =       0.00 Mode: apm_node_res::case 0
Timer #    11       0.00/       1 =       0.00 Mode: apm_node_res::case 1
Timer #    12       0.02/       1 =       0.02 Mode: apm_node_res::case 2
Timer #    13       0.00/       1 =       0.00 Mode: apm_node_res::case 3
Timer #    14       0.17/     317 =       0.00 Mode: apm_node_res::case 4
Timer #    15       0.72/     156 =       0.00 Mode: apm_node_res::case 5
Timer #    16       0.00/       0 =       0.00 Mode: apm_node_res::case 6
Timer #    17       0.01/      78 =       0.00 Base 1st Deriv: apm_jacobian
Timer #    18       0.00/      78 =       0.00 Base 1st Deriv: apm_condensed_jacobian
Timer #    19       0.00/       1 =       0.00 Non-zeros: apm_nnz
Timer #    20       0.00/       0 =       0.00 Count: Division by zero
Timer #    21       0.00/       0 =       0.00 Count: Argument of LOG10 negative
Timer #    22       0.00/       0 =       0.00 Count: Argument of LOG negative
Timer #    23       0.00/       0 =       0.00 Count: Argument of SQRT negative
Timer #    24       0.00/       0 =       0.00 Count: Argument of ASIN illegal
Timer #    25       0.00/       0 =       0.00 Count: Argument of ACOS illegal
Timer #    26       0.00/       1 =       0.00 Extract sparsity: apm_sparsity
Timer #    27       0.00/      13 =       0.00 Variable ordering: apm_var_order
Timer #    28       0.00/       1 =       0.00 Condensed sparsity
Timer #    29       0.00/       0 =       0.00 Hessian Non-zeros
Timer #    30       0.00/       1 =       0.00 Differentials
Timer #    31       0.00/       0 =       0.00 Hessian Calculation
Timer #    32       0.00/       0 =       0.00 Extract Hessian
Timer #    33       0.00/       1 =       0.00 Base 1st Deriv: apm_jac_order
Timer #    34       0.01/       1 =       0.01 Solver Setup
Timer #    35       1.39/       1 =       1.39 Solver Solution
Timer #    36       0.00/     167 =       0.00 Number of Variables
Timer #    37       0.01/      84 =       0.00 Number of Equations
Timer #    38       0.01/      14 =       0.00 File Read/Write
Timer #    39       0.00/       0 =       0.00 Dynamic Init A
Timer #    40       0.00/       0 =       0.00 Dynamic Init B
Timer #    41       0.00/       0 =       0.00 Dynamic Init C
Timer #    42       0.66/       1 =       0.66 Init: Read APM File
Timer #    43       0.00/       1 =       0.00 Init: Parse Constants
Timer #    44       0.42/       1 =       0.42 Init: Model Sizing
Timer #    45       0.00/       1 =       0.00 Init: Allocate Memory
Timer #    46       0.45/       1 =       0.45 Init: Parse Model
Timer #    47       0.18/       1 =       0.18 Init: Check for Duplicates
Timer #    48       1.99/       1 =       1.99 Init: Compile Equations
Timer #    49       0.00/       1 =       0.00 Init: Check Uninitialized
Timer #    50       0.01/    1257 =       0.00 Evaluate Expression Once
Timer #    51       0.00/       0 =       0.00 Sensitivity Analysis: LU Factorization
Timer #    52       0.00/       0 =       0.00 Sensitivity Analysis: Gauss Elimination
Timer #    53       0.00/       0 =       0.00 Sensitivity Analysis: Total Time

Updating one parameter and only calling m.solve() again - is shown in the simple problem above.

 ----------------------------------------------------------------
 APMonitor, Version 1.0.0
 APMonitor Optimization Suite
 ----------------------------------------------------------------
 
 Called files( 55 )
 Called files( 55 )
 files: overrides.dbs does not exist
 
 Run id   : 2022y11m03d13h18m28.729s
 
 COMMAND LINE ARGUMENTS
 coldstart:  0
 imode    :  3
 dbs_read :  T
 dbs_write:  T
 specs    :  T
 
 rto selected
 Called files( 35 )
 READ info FILE FOR VARIABLE DEFINITION: gk_model6.info
 SS MODEL INIT  0
 Parsing model file gk_model6.apm
 Read model file (sec): 0.6901
 Initialize constants (sec): 0.
 Determine model size (sec): 0.4546999999999999
 Allocate memory (sec): 0.
 Parse and store model (sec): 0.2824000000000002
 
 --------- APM Model Size ------------
 Each time step contains
   Objects      :  247
   Constants    :  0
   Variables    :  752
   Intermediates:  249
   Connections  :  741
   Equations    :  745
   Residuals    :  496
 
 Error checking (sec): 0.16720000000000002
 Compile equations (sec): 2.0142999999999995
 Check for uninitialized intermediates (sec): 0.
 ------------------------------------------------------
 Total Parse Time (sec): 3.6097
 SS MODEL INIT  1
 SS MODEL INIT  2
 SS MODEL INIT  3
 SS MODEL INIT  4
 Called files( 31 )
 READ info FILE FOR PROBLEM DEFINITION: gk_model6.info
 Called files( 6 )
 Files(6): File Read rto.t0 T
 Called files( 51 )
 Read DBS File defaults.dbs
 files: defaults.dbs does not exist
 Called files( 51 )
 Read DBS File gk_model6.dbs
 Called files( 51 )
 Read DBS File measurements.dbs
 Called files( 51 )
 Read DBS File overrides.dbs
 files: overrides.dbs does not exist
 Number of state variables:    1240
 Number of total equations: -  989
 Number of slack variables: -  0
 ---------------------------------------
 Degrees of freedom       :    251
 
 ----------------------------------------------
 Steady State Optimization with APOPT Solver
 ----------------------------------------------
Iter:     1 I:  0 Tm:      0.26 NLPi:    7 Dpth:    0 Lvs:    3 Obj:  9.35E-02 Gap:       NaN
--Integer Solution:   1.21E-01 Lowest Leaf:   9.35E-02 Gap:   2.71E-02
Iter:     2 I:  0 Tm:      0.22 NLPi:    5 Dpth:    1 Lvs:    2 Obj:  1.21E-01 Gap:  2.71E-02
--Integer Solution:   1.21E-01 Lowest Leaf:   9.35E-02 Gap:   2.71E-02
Iter:     3 I:  0 Tm:      0.32 NLPi:   10 Dpth:    1 Lvs:    1 Obj:  1.03E+00 Gap:  2.71E-02
Iter:     4 I:  0 Tm:      0.25 NLPi:    8 Dpth:    1 Lvs:    1 Obj:  1.20E-01 Gap:  2.71E-02
--Integer Solution:   1.21E-01 Lowest Leaf:   1.86E-01 Gap:  -6.58E-02
Iter:     5 I:  0 Tm:      0.37 NLPi:   15 Dpth:    2 Lvs:    1 Obj:  1.86E-01 Gap: -6.58E-02
 Successful solution
 
 ---------------------------------------------------
 Solver         :  APOPT (v1.0)
 Solution time  :  1.4365000000000006 sec
 Objective      :  0.12065435497282542
 Successful solution
 ---------------------------------------------------
 
 Called files( 2 )
 Called files( 52 )
 WRITE dbs FILE
 Called files( 56 )
 WRITE json FILE
Timer #     1       5.64/       1 =       5.64 Total system time
Timer #     2       1.44/       1 =       1.44 Total solve time
Timer #     3       0.00/      91 =       0.00 Objective Calc: apm_p
Timer #     4       0.00/      45 =       0.00 Objective Grad: apm_g
Timer #     5       0.01/      91 =       0.00 Constraint Calc: apm_c
Timer #     6       0.00/       0 =       0.00 Sparsity: apm_s
Timer #     7       0.00/       0 =       0.00 1st Deriv #1: apm_a1
Timer #     8       0.00/      45 =       0.00 1st Deriv #2: apm_a2
Timer #     9       0.44/       1 =       0.44 Custom Init: apm_custom_init
Timer #    10       0.00/       1 =       0.00 Mode: apm_node_res::case 0
Timer #    11       0.00/       1 =       0.00 Mode: apm_node_res::case 1
Timer #    12       0.01/       1 =       0.01 Mode: apm_node_res::case 2
Timer #    13       0.00/       1 =       0.00 Mode: apm_node_res::case 3
Timer #    14       0.10/     187 =       0.00 Mode: apm_node_res::case 4
Timer #    15       0.46/      90 =       0.01 Mode: apm_node_res::case 5
Timer #    16       0.00/       0 =       0.00 Mode: apm_node_res::case 6
Timer #    17       0.00/      45 =       0.00 Base 1st Deriv: apm_jacobian
Timer #    18       0.00/      45 =       0.00 Base 1st Deriv: apm_condensed_jacobian
Timer #    19       0.00/       1 =       0.00 Non-zeros: apm_nnz
Timer #    20       0.00/       0 =       0.00 Count: Division by zero
Timer #    21       0.00/       0 =       0.00 Count: Argument of LOG10 negative
Timer #    22       0.00/       0 =       0.00 Count: Argument of LOG negative
Timer #    23       0.00/       0 =       0.00 Count: Argument of SQRT negative
Timer #    24       0.00/       0 =       0.00 Count: Argument of ASIN illegal
Timer #    25       0.00/       0 =       0.00 Count: Argument of ACOS illegal
Timer #    26       0.00/       1 =       0.00 Extract sparsity: apm_sparsity
Timer #    27       0.00/      13 =       0.00 Variable ordering: apm_var_order
Timer #    28       0.00/       1 =       0.00 Condensed sparsity
Timer #    29       0.00/       0 =       0.00 Hessian Non-zeros
Timer #    30       0.01/       1 =       0.01 Differentials
Timer #    31       0.00/       0 =       0.00 Hessian Calculation
Timer #    32       0.00/       0 =       0.00 Extract Hessian
Timer #    33       0.00/       1 =       0.00 Base 1st Deriv: apm_jac_order
Timer #    34       0.01/       1 =       0.01 Solver Setup
Timer #    35       0.84/       1 =       0.84 Solver Solution
Timer #    36       0.00/     102 =       0.00 Number of Variables
Timer #    37       0.00/      51 =       0.00 Number of Equations
Timer #    38       0.12/      14 =       0.01 File Read/Write
Timer #    39       0.00/       0 =       0.00 Dynamic Init A
Timer #    40       0.00/       0 =       0.00 Dynamic Init B
Timer #    41       0.00/       0 =       0.00 Dynamic Init C
Timer #    42       0.69/       1 =       0.69 Init: Read APM File
Timer #    43       0.00/       1 =       0.00 Init: Parse Constants
Timer #    44       0.45/       1 =       0.45 Init: Model Sizing
Timer #    45       0.00/       1 =       0.00 Init: Allocate Memory
Timer #    46       0.28/       1 =       0.28 Init: Parse Model
Timer #    47       0.17/       1 =       0.17 Init: Check for Duplicates
Timer #    48       2.01/       1 =       2.01 Init: Compile Equations
Timer #    49       0.00/       1 =       0.00 Init: Check Uninitialized
Timer #    50       0.00/     505 =       0.00 Evaluate Expression Once
Timer #    51       0.00/       0 =       0.00 Sensitivity Analysis: LU Factorization
Timer #    52       0.00/       0 =       0.00 Sensitivity Analysis: Gauss Elimination
Timer #    53       0.00/       0 =       0.00 Sensitivity Analysis: Total Time

Many thanks in advance for your ideas.

John Hedengren
  • 12,068
  • 1
  • 21
  • 25
Glenn C.
  • 119
  • 7

1 Answers1

2

Here are a few ideas to improve the compile time speed:

  • If the problem is a simulation, it can be run with IMODE=7 to compile the model once and run through all the timesteps sequentially. This is often the fastest option for simulation.
  • If the problem has degrees of freedom (find optimal parameter to minimize objective) then it is also possible to set up the problem as IMODE=6 to compile the model once and it solves all time steps simultaneously.
  • The model re-compiles every time m.solve() is called. Keeping a compiled version of the model is built into the REPLAY parameter, but it has little documentation and is built for exactly reenacting a sequence from historical data. If you'd like the option to keep a compiled version of the model for the next run, please add it as a feature request in GitHub.

Using the simple model shows that the model re-compile time doesn't change significantly between runs with different values of demand.

Timing results

from gekko import GEKKO
from numpy import random
import numpy as np
import matplotlib.pyplot as plt
import time

# first, create the model
m = GEKKO(remote=False)

# declare additional decision variables
m.u = m.Var(lb=5, ub=25)
m.v = m.Var(lb=0, ub=100)
m.w = m.Var(lb=0, ub=50)
m.b = m.Var(lb=0, ub=1, integer=True)
m.demand = m.Param()

# now add the objective and the constraints
m.Minimize((1-0.8)*m.u*m.b+(1-0.9)*m.v+(1-0.7)*m.w)
m.Equation(m.u*m.b >= 10)
m.Equation(m.u*m.b + m.v + m.w == m.demand)

m.options.SOLVER=1
m.options.DIAGLEVEL = 0

tt = [] # total time
ts = [] # solve time

for i in range(101):
    start = time.time()
    m.demand.value = 50+random.rand()*50
    m.solve(disp=False)
    tt.append(time.time()-start)
    ts.append(m.options.SOLVETIME)

plt.figure(figsize=(8,4))    
plt.plot(tt,label='Total Time')
plt.plot(ts,label='Solve Time')
plt.plot(np.array(tt)-np.array(ts),label='Compile Time')
plt.ylim([0,0.03]); plt.grid(); plt.ylabel('Time (sec)')
plt.savefig('timing.png',dpi=300)
plt.legend()
plt.show()

The compile and solve times are very fast for this simple problem. If you can post the full RTO problem, we can give more specific suggestions to improve solve time.

John Hedengren
  • 12,068
  • 1
  • 21
  • 25
  • 1
    Many thanks for your extensive reply. The simulations itself is not done with GEKKO. Solving over all (future) timesteps is also not possible (as they are unknown a piori). It really is an RTO problem, where the problem is indeed not the solve time with various parameter values (demand in the simple example) - but rather that the total system time is relatively large compared to the solve time (in the actual RTO problem). Reducing the need to recompile every time would already help (+- 2 sec out of 6 sec total system time, with 2 sec solve time) - I'll make a feature request on Github. – Glenn C. Nov 04 '22 at 08:28
  • 1
    Thanks for submitting the feature request: https://github.com/BYU-PRISM/GEKKO/issues/153 What's the cycle time on the RTO application? Large RTO applications typically run every hour or when the process reaches steady state. MPC applications typically run every minute for chemical processing although higher-speed applications can run in the 10-100 Hz range. – John Hedengren Nov 04 '22 at 13:17
  • 1
    The real (and thus also the simulated) cycle time would be 15 minutes. But I'm planning to simulate 500.000 timesteps and repeat this 5 times. I got the total system time down to about 2 sec per timestep (only for the GEKKO part), but of those 2 secs - only have 0.3-0.4 sec solve time. – Glenn C. Nov 04 '22 at 15:30
  • 1
    One way to speed up the time is to multi-thread the application for parallelization: https://stackoverflow.com/questions/58887835/how-to-do-parallel-python-gekko This also allows the jobs to be distributed over multiple servers (local or publicly available). Here are instructions for installing a local server: https://apmonitor.com/wiki/index.php/Main/APMonitorServerLinux or just use `remote=False` to compute locally on the computer. – John Hedengren Nov 04 '22 at 16:07