Search code examples
pythonarrayslistdifferential-equationsgekko

List handling in GEKKO python


i'm currently working on a model of a distillation flask for a university project, the phisical problem is described by a DAE system, and i'm trying to solve it using GEKKO.

I'm facing a problem with list handling: In this case i built a function that outputs the compressibility factor of a mixture, and it requires as inputs 3 gekko variables T1,x,y (x,y arrays) zv1 = m.Param(value=ZCALC(n,comps,R0,p,T1.value,x,y))

m = GEKKO()
y = m.Array(m.Var,n,value=0.)
x = m.Array(m.Var,n,value=0.)
for i in range(n):
    y[i].value = y0[i]
    x[i].value = x0[i]
T1 = m.Var(value=3.31513478e+02, lb=300, ub=900)

If i leave the 3 values as they are i recieve some errors like:

  File "F:\Codice_GEKKO\D86_GEKKO.py", line 113, in <module>
    zv1 = m.Param(value=ZCALC(n,comps,R0,p,T1.value,x,y))
  File "F:\Codice_GEKKO\compressibilityfactor.py", line 48, in ZCALC
    zv=np.max(np.roots([1,-1,(Av-Bv-Bv**2),-Av*Bv]))
  File "<__array_function__ internals>", line 6, in roots
  File "C:\Users\verci\AppData\Local\Programs\Python\Python36\lib\site-packages\numpy\lib\polynomial.py", line 222, in roots
    non_zero = NX.nonzero(NX.ravel(p))[0]
  File "<__array_function__ internals>", line 6, in nonzero
  File "C:\Users\verci\AppData\Local\Programs\Python\Python36\lib\site-packages\numpy\core\fromnumeric.py", line 1908, in nonzero
    return _wrapfunc(a, 'nonzero')
  File "C:\Users\verci\AppData\Local\Programs\Python\Python36\lib\site-packages\numpy\core\fromnumeric.py", line 67, in _wrapfunc
    return _wrapit(obj, method, *args, **kwds)
  File "C:\Users\verci\AppData\Local\Programs\Python\Python36\lib\site-packages\numpy\core\fromnumeric.py", line 44, in _wrapit
    result = getattr(asarray(obj), method)(*args, **kwds)
  File "C:\Users\verci\AppData\Local\Programs\Python\Python36\lib\site-packages\gekko\gk_operators.py", line 25, in __len__
    return len(self.value)
  File "C:\Users\verci\AppData\Local\Programs\Python\Python36\lib\site-packages\gekko\gk_operators.py", line 144, in __len__
    return len(self.value)
TypeError: object of type 'int' has no len()```

Traceback (most recent call last):
  File "F:\Codice_GEKKO\D86_GEKKO.py", line 113, in <module>
    zv1 = m.Param(value=ZCALC(n,comps,R0,p,T1,x,y))
  File "F:\Codice_GEKKO\compressibilityfactor.py", line 27, in ZCALC
    (1-np.sqrt(t/tc[ii])))**2
TypeError: loop of ufunc does not support argument 0 of type GK_Operators which has no callable sqrt method

The first error is biven by the fact that x and y are not list, but they are GEKKO arrays and the second error is due to T1 not being a float (t=T1)

I found out that by using T1.value i can avoid the second error but still i have the first error

I have read the gekko documentation but i haven't been able to find a method to obtain a "standard" python list from a GEKKO array

Thank you in advance for your help


Solution

  • There are two different methods for obtained the value of zv.

    Option 1: Initialization Calculation

    The first method is to use floating point numbers to obtain a single calculation that can be used for initialization of a parameter. This first method allows any type of functions such as np.roots() or np.sqrt(). The function ZCALC() returns a floating point number. Even though Gekko variables are used as an input, the floating point number is accessed from a scalar variable with T1.value or from an array variable with x[i].value.

    def ZCALC(n,comps,R0,p,T1,x,y):
        # using the initialized values
        t = T1.value
        i = 0 # select values from x,y arrays
        x1 = x[i].value
        y1 = y[i].value
        print('t,x[0],y[0] initialized values')
        print(t,x1,y1)
    
        # include equations for compressibility factor
        w = (1-np.sqrt(t/300))**2
        z = np.max(np.roots([1,-1,x1**2,x1*y1]))
        # original equations from question
        #(1-np.sqrt(t/tc[ii])))**2
        #zv=np.max(np.roots([1,-1,(Av-Bv-Bv**2),-Av*Bv]))
        return z
    zv1 = m.Param(value=ZCALC(n,comps,R0,p,T1,x,y))
    

    Option 2: Implicit Calculation

    If the compressibility factor needs to change as T1 and x,y change then use Gekko variables so that the model is compiled with that dependency. The functions are only called during problem initialization. Gekko needs the equations with specific Gekko functions to enable automatic differentiation to provide gradients to the solvers.

    def ZCALC2(n,comps,R0,p,T1,x,y):
        # using gekko variables
        t = T1
        i = 0
        x1 = x[i] # use index to x array
        y1 = y[i] # use index to y array
    
        # use Gekko equations, not Numpy
        w = (x1/y1)*(1-m.sqrt(t/300))**2
        # set lower bound to get the maximum root
        zv = m.Var(value=ZCALC(n,comps,R0,p,T1,x,y),lb=10)
        # solve for roots of eq with gekko, not with np.roots
        eq = 1-zv+x1**2*zv+x1*y1*zv**3
        m.Equation(eq==0)
        return zv
    zv2 = ZCALC2(n,comps,R0,p,T1,x,y)
    

    Here is a script that shows the two methods:

    import numpy as np
    m=GEKKO(remote=False)
    
    def ZCALC(n,comps,R0,p,T1,x,y):
        # using the initialized values
        t = T1.value
        i = 0 # select values from x,y arrays
        x1 = x[i].value
        y1 = y[i].value
        print('t,x[0],y[0] initialized values')
        print(t,x1,y1)
    
        # include equations for compressibility factor
        w = (1-np.sqrt(t/300))**2
        z = np.max(np.roots([1,-1,x1**2,x1*y1]))
        # original equations from question
        #(1-np.sqrt(t/tc[ii])))**2
        #zv=np.max(np.roots([1,-1,(Av-Bv-Bv**2),-Av*Bv]))
        return z
    
    def ZCALC2(n,comps,R0,p,T1,x,y):
        # using gekko variables
        t = T1
        i = 0
        x1 = x[i] # use index to x array
        y1 = y[i] # use index to y array
    
        # use Gekko equations, not Numpy
        w = (x1/y1)*(1-m.sqrt(t/300))**2
        # set lower bound to get the maximum root
        zv = m.Var(value=ZCALC(n,comps,R0,p,T1,x,y),lb=10)
        # solve for roots of eq with gekko, not with np.roots
        eq = 1-zv+x1**2*zv+x1*y1*zv**3
        m.Equation(eq==0)
        return zv
    
    n = 3
    y = m.Array(m.Var,n)
    x = m.Array(m.Var,n)
    x0 = [0.1,0.2,0.3]
    y0 = [0.15,0.25,0.35]
    for i in range(n):
        y[i].value = y0[i]
        x[i].value = x0[i]
    T1 = m.Var(value=331, lb=300, ub=900)
    
    comps = ['C2=','C3=','C2H8']; R0 = 8.314; p=10
    
    # define Zv from initialized values (fixed parameter)
    zv1 = m.Param(value=ZCALC(n,comps,R0,p,T1,x,y))
    
    # define Zv from Gekko variables (updates with T1,x,y changes)
    zv2 = ZCALC2(n,comps,R0,p,T1,x,y)
    
    # initialized value of zv1 does not update with changes in T1,x,y
    # initialized value of zv2 does update with changes in T1,x,y
    print('initialized value of zv1, zv2')
    print(zv1.value,zv2.value)
    

    If the compressibility factor correlations can't be expressed as Gekko equations then try the cspline for 1D or bspline for 2D functions to create an approximation. You may be able to use the bspline function if compressibility can depend on just 2 variables T and x (replace y with an explicit calculation of x).