Intermediate Variables

Intermediate variables are used to decrease the complexity of the model. These variables store temporary calculations with results that are not reported in the final solution reports. In many models, the temporary variables outnumber the regular variables. This model reduction often aides the solver in finding a solution by reducing the problem size.

Intermediate variables are declared in the Intermediates ... End Intermediates section of the model file or with m.Intermediates() in Python Gekko. The intermediate variables may be defined in one section or in multiple declarations throughout the model. Intermediate variables are parsed sequentially, from top to bottom. To avoid inadvertent overwrites, intermediate variable can be defined once. In the case of intermediate variables, the order of declaration is critical. If an intermediate is used before the definition, an error reports that there is an uninitialized value.

An example of intermediates is with the equations z=sqrt(x+y) and w=(x+y)2 that both share a common x+y term. In APMonitor the Intermediates keyword defines this special type of variable.

# APMonitor
Intermediates
   i = x+y
End Intermediates

Equations
   z=sqrt(i)
   w=i^2
End Equations

In Python Gekko, intermediates are defined with the Intermediate function.

# Python Gekko
i = m.Intermediate(x+y)
m.Equations([z==m.sqrt(i),
             w==i**2])

Explicit calculation

The intermediate variables are processed before the implicit equation residuals, every time the solver requests model information. As opposed to implicitly calculated variables, the intermediates are calculated repeatedly and substituted into other intermediate or implicit equations.

Clipping

When the intermediate variable is solely a function of parameters (not variables), the value may be clipped. This is accomplished by adding inequalities to the expression, separated by a comma. The inequalities may also be a function of other intermediate or regular variables.

Limitations

APMonitor and Python Gekko are designed to encourage the user to construct well-posed models for numerical solution. Excessive use of intermediate variables may lead to the violation of this limit. If this limit is encountered, the user can remediate this problem by converting an intermediate variable to a regular implicit variable.


Example

 ! Original model
 Model example
   Parameters
     p = 2
   End Parameters

   Variables
     x
     y
     z
   End Variables

   Equations
     exp(x*p)=y
     z = p*$x + x
     (y+2/x)^(x*z) * (log(tanh(sqrt(y-x+x^2))+3))^2 = &
       2+sinh(y)+acos(x+y)+asin(x/y)
   End Equations
 End Model


 ! Model simplified with use of intermediate variables
 Model example
   Parameters
     p = 2
   End Parameters

   Variables
     x
     y
     z
   End Variables

   Intermediates
     exp_result = exp(x*p)
     left = (y+2/x)^(x*z) * (log(tanh(sqrt(y-x+x^2))+3))^2
     right = 2+sinh(y)+acos(x+y)+asin(x/y)
   End Intermediates

   Equations
     exp_result=y
     z = p*$x + x
     left = right
   End Equations
 End Model
 ! Example with intermediate variable clipping
 Model example
   Parameters
     p = 1
   End Parameters

   Variables
     x
   End Variables

   Intermediates
     pi = p*3.1415, <1.5
   End Intermediates

   Equations
     x = 0.5 * pi
   End Equations
 End Model

Example Problem

  • Batch reactor with consecutive reactions A->B->C

$$\max_{T(t)} x_2 \left( t_f \right)$$ $$\mathrm{subject \; to}$$ $$\frac{dx_1}{dt}=-k_1 \, x_1^2$$ $$\frac{dx_2}{dt}=k_1 \, x_1^2 - k_2 \, x_2$$ $$k_1 = 4000 \, \exp{\left(-\frac{2500}{T}\right)}$$ $$k_2 = 6.2e5 \, \exp{\left(-\frac{5000}{T}\right)}$$ $$x(0) = [1 \; 0]^T$$ $$298 \le T \le 398$$ $$t_f=1$$

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

m = GEKKO()

nt = 101
m.time = np.linspace(0,1,nt)

# Parameters
T = m.MV(value=362,ub=398,lb=298)
T.STATUS = 1
T.DCOST = 0

# Variables
x1 = m.Var(value=1)
x2 = m.Var(value=0)

p = np.zeros(nt)
p[-1] = 1.0
final = m.Param(value=p)

# Intermediates
k1 = m.Intermediate(4000*m.exp(-2500/T))
k2 = m.Intermediate(6.2e5*m.exp(-5000/T))

# Equations
m.Equation(x1.dt()==-k1*x1**2)
m.Equation(x2.dt()==k1*x1**2 - k2*x2)

# Objective Function
m.Obj(-x2*final)

m.options.IMODE = 6
m.solve()

print('Objective: ' + str(x2[-1]))

plt.figure(1)

plt.subplot(2,1,1)
plt.plot(m.time,x1.value,'k:',LineWidth=2,label=r'$x_1$')
plt.plot(m.time,x2.value,'b-',LineWidth=2,label=r'$x_2$')
plt.ylabel('Value')
plt.legend(loc='best')

plt.subplot(2,1,2)
plt.plot(m.time,T.value,'r--',LineWidth=2,label=r'$T$')
plt.legend(loc='best')
plt.xlabel('Time')
plt.ylabel('Value')

plt.show()
💬