I'm using GEKKO and CoolProp for thermal systems simulation. When trying to use CoolProp's functions inside the model equations (as shown below for an isentropic expansion) I'm getting an error message regarding the variable type:
"must be real number, not GKVariable".
Could someone please help me with this issue?
from gekko import GEKKO
import CoolProp.CoolProp as CP
#
p1 = 2e5
T1 = 300.0 + 273.15
p2 = 1e5
eta = 0.80
fluid = 'H2O'
#
h1 = CP.PropsSI('H','T',T1,'P',p1,fluid)
s1 = CP.PropsSI('S','T',T1,'P',p1,fluid)
#
m = GEKKO()
h2 = m.Var()
h2s = m.Var()
T2 = m.Var()
#
m.Equation(eta * (h1 - h2) - (h1 - h2s) == 0)
m.Equation(h2s - CP.PropsSI('H','S',s1,'P',p2,fluid) == 0)
m.Equation(h2 - CP.PropsSI('H','T',T2,'P',p2,fluid) == 0)
#
m.options.IMODE = 1 #Steady state
m.options.SOLVER = 3 # solver (IPOPT)
m.solve(disp=False)
#
print(T2.value[0])
Thanks in advance.
Gekko needs to have the CoolProp equations to perform automatic differentiation. If it doesn't have the equation then you could use a cspline (cubic spline documentation).
from gekko import GEKKO
import CoolProp.CoolProp as CP
import numpy as np
#
p1 = 2e5
T1 = 300.0 + 273.15
p2 = 1e5
eta = 0.80
fluid = 'H2O'
# constants
h1 = CP.PropsSI('H','T',T1,'P',p1,fluid)
s1 = CP.PropsSI('S','T',T1,'P',p1,fluid)
c2 = CP.PropsSI('H','S',s1,'P',p2,fluid)
# gekko model
m = GEKKO(remote=False)
h2 = m.Var()
h2s = m.Var()
T2 = m.Var()
# build cubic spline
n = 100
T = np.linspace(373,1000,100)
h = [CP.PropsSI('H','T',Ti,'P',p1,fluid) for Ti in T]
m.cspline(T2,h2,T,h)
#
m.Equation(eta * (h1 - h2) - (h1 - h2s) == 0)
m.Equation(h2s - c2 == 0)
#
m.options.IMODE = 1 #Steady state
m.options.SOLVER = 3 # solver (IPOPT)
m.solve(disp=False)
#
print(T2.value[0])
This produces the solution:
T2 = 468.52459939
Related
I would like to solve the above formulation in Scipy and solve it using milp(). For a given graph (V, E), f_ij and x_ij are the decision variables. f_ij is the flow from i to j (it can be continuous). x_ij is the number of vehicles from i to j. p is the price. X is the available number vehicles in a region. c is the capacity.
I have difficulty in translating the formulation to Scipy milp code. I would appreciate it if anyone could give me some pointers.
What I have done:
The code for equation (1):
f_obj = [p[i] for i in Edge]
x_obj = [0]*len(Edge)
obj = f_obj + v_obj
Integrality:
f_cont = [0 for i in Edge] # continous
x_int = [1]*len(Edge) # integer
integrality = f_cont + x_int
Equation (2):
def constraints(self):
b = []
A = []
const = [0]*len(Edge) # for f_ij
for i in v: # for x_ij
for e in Edge:
if e[0] == i:
const.append(1)
else:
const.append(0)
A.append(const)
b.append(self.accInit[i])
const = [0]*len(Edge) # for f_ij
return A, b
Equation (4):
[(0, demand[e]) for e in Edge]
I'm going to do some wild guessing, given how much you've left open to interpretation. Let's assume that
this is a maximisation problem, since the minimisation problem is trivial
Expression (1) is actually the maximisation objective function, though you failed to write it as such
p and d are floating-point vectors
X is an integer vector
c is a floating-point scalar
the graph edges, since you haven't described them at all, do not matter for problem setup
The variable names are not well-chosen and hide what they actually contain. I demonstrate potential replacements.
import numpy as np
from numpy.random._generator import Generator
from scipy.optimize import milp, Bounds, LinearConstraint
import scipy.sparse
from numpy.random import default_rng
rand: Generator = default_rng(seed=0)
N = 20
price = rand.uniform(low=0, high=10, size=N) # p
demand = rand.uniform(low=0, high=10, size=N) # d
availability = rand.integers(low=0, high=10, size=N) # X aka. accInit
capacity = rand.uniform(low=0, high=10) # c
c = np.zeros(2*N) # f and x
c[:N] = -price # (1) f maximized with coefficients of 'p'
# x not optimized
CONTINUOUS = 0
INTEGER = 1
integrality = np.empty_like(c, dtype=int)
integrality[:N] = CONTINUOUS # f
integrality[N:] = INTEGER # x
upper = np.empty_like(c)
upper[:N] = demand # (4) f
upper[N:] = availability # (2) x
eye_N = scipy.sparse.eye(N)
A = scipy.sparse.hstack((-eye_N, capacity*eye_N)) # (3) 0 <= -f + cx
result = milp(
c=c, integrality=integrality,
bounds=Bounds(lb=np.zeros_like(c), ub=upper),
constraints=LinearConstraint(lb=np.zeros(N), A=A),
)
print(result.message)
flow = result.x[:N]
vehicles = result.x[N:].astype(int)
I am tring to solve the equation of motion of charged particle in planetary magnetic field to see the path of the particle using Forward Euler's and RK5 method in python (as an excercise in learning Numerical methods) I encounter two problems:
The 'for loop' in the RK4 method does not update the new values. It give the values of the first iteration for all iteration.
With the change of the sing of 'β = charge/mass' the path of particle which is expected does not change. It seems the path is unaffected by the nature(sign) of the particle. What does this mean physically or mathematically?
The codes are adapted from :
python two coupled second order ODEs Runge Kutta 4th order
and
Applying Forward Euler Method to a Three-Box Model System of ODEs
I would be immensely grateful if anyone explain to me what is wrong in the code.
thank you.
The Code are as under:
import numpy as np
import matplotlib.pyplot as plt
from math import sin, cos
from scipy.integrate import odeint
scales = np.array([1e7, 0.1, 1, 1e-5, 10, 1e-5])
def LzForce(t,p):
# assigning each ODE to a vector element
r,x,θ,y,ϕ,z = p*scales
# constants
R = 60268e3 # metre
g_20 = 1583e-9
Ω = 9.74e-3 # degree/second
B_θ = (R/r)**4*g_20*cos(θ)*sin(θ)
B_r = 2*(R/r)**4*g_20*(0.5*(3*cos(θ)**2-1))
β = +9.36e10
# defining the ODEs
drdt = x
dxdt = r*(y**2 +(z+Ω)**2*sin(θ)**2-β*z*sin(θ)*B_θ)
dθdt = y
dydt = (-2*x*y +r*(z+Ω)**2*sin(θ)*cos(θ)+β*r*z*sin(θ)*B_r)/r
dϕdt = z
dzdt = (-2*x*(z+Ω)*sin(θ)-2*r*y*(z+Ω)*cos(θ)+β*(x*B_θ-r*y*B_r))/(r*sin(θ))
return np.array([drdt,dxdt,dθdt,dydt,dϕdt,dzdt])/scales
def ForwardEuler(fun,t0,p0,tf,dt):
r0 = 6.6e+07
x0 = 0.
θ0 = 88.
y0 = 0.
ϕ0 = 0.
z0 = 22e-3
p0 = np.array([r0,x0,θ0,y0,ϕ0,z0])
t = np.arange(t0,tf+dt,dt)
p = np.zeros([len(t), len(p0)])
p[0] = p0
for i in range(len(t)-1):
p[i+1,:] = p[i,:] + fun(t[i],p[i,:]) * dt
return t, p
def rk4(fun,t0,p0,tf,dt):
# initial conditions
r0 = 6.6e+07
x0 = 0.
θ0 = 88.
y0 = 0.
ϕ0 = 0.
z0 = 22e-3
p0 = np.array([r0,x0,θ0,y0,ϕ0,z0])
t = np.arange(t0,tf+dt,dt)
p = np.zeros([len(t), len(p0)])
p[0] = p0
for i in range(len(t)-1):
k1 = dt * fun(t[i], p[i])
k2 = dt * fun(t[i] + 0.5*dt, p[i] + 0.5 * k1)
k3 = dt * fun(t[i] + 0.5*dt, p[i] + 0.5 * k2)
k4 = dt * fun(t[i] + dt, p[i] + k3)
p[i+1] = p[i] + (k1 + 2*(k2 + k3) + k4)/6
return t,p
dt = 0.5
tf = 1000
p0 = [6.6e+07,0.0,88.0,0.0,0.0,22e-3]
t0 = 0
#Solution with Forward Euler
t,p_Euler = ForwardEuler(LzForce,t0,p0,tf,dt)
#Solution with RK4
t ,p_RK4 = rk4(LzForce,t0, p0 ,tf,dt)
print(t,p_Euler)
print(t,p_RK4)
# Plot Solutions
r,x,θ,y,ϕ,z = p_Euler.T
fig,ax=plt.subplots(2,3,figsize=(8,4))
plt.xlabel('time in sec')
plt.ylabel('parameters')
for a,s in zip(ax.flatten(),[r,x,θ,y,ϕ,z]):
a.plot(t,s); a.grid()
plt.title("Forward Euler", loc='left')
plt.tight_layout(); plt.show()
r,x,θ,y,ϕ,z = p_RK4.T
fig,ax=plt.subplots(2,3,figsize=(8,4))
plt.xlabel('time in sec')
plt.ylabel('parameters')
for a,q in zip(ax.flatten(),[r,x,θ,y,ϕ,z]):
a.plot(t,q); a.grid()
plt.title("RK4", loc='left')
plt.tight_layout(); plt.show()
[RK4 solution plot][1]
[Euler's solution methods][2]
''''RK4 does not give iterated values.
The path is unaffected by the change of sign which is expected as it is under Lorentz force''''
[1]: https://i.stack.imgur.com/bZdIw.png
[2]: https://i.stack.imgur.com/tuNDp.png
You are not iterating more than once inside the for loop in rk4 because it returns after the first iteration.
for i in range(len(t)-1):
k1 = dt * fun(t[i], p[i])
k2 = dt * fun(t[i] + 0.5*dt, p[i] + 0.5 * k1)
k3 = dt * fun(t[i] + 0.5*dt, p[i] + 0.5 * k2)
k4 = dt * fun(t[i] + dt, p[i] + k3)
p[i+1] = p[i] + (k1 + 2*(k2 + k3) + k4)/6
# This is the problem line, the return was tabbed in, to be inside the for block, so the block executed once and returned.
return t,p
For physics questions please try a different forum.
I am trying to solve the problem as follows with Gekko in python.
I_s is an indicator variable in the problem whose value is 1 if theta is positive and 0 if theta is zero.
I wrote the problem in a code using Gekko, python.
In contrast to my previous posts, I add some constraints with respect to I, which is an indicator variable.
If I set N=10, the solution, theta is all zero, which is the result that I want.
But if I set N=100 or 200, the solution cannot be found. I cannot understand why this happens.
I want to check if theta is also zero in larger N (200).
Is there any way to solve this issue?
My code is as belows.
# Import package
from gekko import GEKKO
import numpy as np
# Define parameters
P_CO = 600 # $/tonCO
beta_CO2 = 1 # no unit
P_CO2 = 80 # $/tonCO2eq
E_ref = 3.1022616 # tonCO2eq/tonCO
E_dir = -1.600570692 # tonCO2eq/tonCO
E_indir_others = 0.3339226804 # tonCO2eq/tonCO
E_indir_elec_cons = 18.46607256 # GJ/tonCO
C1_CAPEX = 285695 # no unit
C2_CAPEX = 188.42 # no unit
C1_FOX = 82282 # no unit
C2_FOX = 24.094 # no unit
C1_ROX = 4471.5 # no unit
C2_ROX = 96.034 # no unit
C1_UOX = 7934.9 # no unit
C2_UOX = 986.9 # no unit
r = 0.08 # discount rate
N = 10 # number of scenarios
T = 30 # total time period
GWP_init = 0.338723235 # 2020 Electricity GWP in EU 27 countries
theta_max = 1600000 # Max capacity
# Function to make GWP_EU matrix (TxN matrix)
def Electricity_GWP(GWP_init, n_years, num_episodes):
GWP_mean = 0.36258224*np.exp(-0.16395611*np.arange(1, n_years+2)) + 0.03091272
GWP_mean = GWP_mean.reshape(-1,1)
GWP_Yearly = np.tile(GWP_mean, num_episodes)
noise = np.zeros((n_years+1, num_episodes))
stdev2050 = GWP_mean[-1] * 0.25
stdev = np.arange(0, stdev2050 * (1 + 1/n_years), stdev2050/n_years)
for i in range(n_years+1):
noise[i,:] = np.random.normal(0, stdev[i], num_episodes)
GWP_forecast = GWP_Yearly + noise
return GWP_forecast
GWP_EU = Electricity_GWP(GWP_init, T, N) # (T+1)*N matrix
GWP_EU = GWP_EU[1:,:] # T*N matrix
print(np.shape(GWP_EU))
# Build Gekko model
m = GEKKO(remote=False)
theta = m.Array(m.Var, N, lb=0, ub=theta_max)
I = m.Array(m.Var, N, lb=0, ub=1, integer=True)
demand = np.ones((T,1))
demand[0] = 8031887.589
for k in range(1,11):
demand[k] = demand[k-1] * 1.026
for k in range(11,21):
demand[k] = demand[k-1] * 1.016
for k in range(21,T):
demand[k] = demand[k-1] * 1.011
demand = 0.12 * demand
demand = np.tile(demand, N) # T*N matrix
print(np.shape(demand))
m3 = [[m.min3(demand[t,s],theta[s]) for t in range(T)] for s in range(N)]
obj = m.sum([sum([((1/(1+r))**(t+1))*((P_CO*m3[s][t]) \
+ (beta_CO2*P_CO2*m3[s][t]*(E_ref-E_dir-E_indir_others-E_indir_elec_cons*GWP_EU[t,s])) \
- (C1_CAPEX*I[s]+C2_CAPEX*theta[s]+C1_FOX*I[s]+C2_FOX*theta[s])\
- (C1_ROX*I[s]+C2_ROX*m3[s][t]+C1_UOX*I[s]+C2_UOX*m3[s][t])) for t in range(T)]) for s in range(N)])
for i in range(N):
m.Equation(theta[i]<=1000000*I[i])
m.Equation(-theta[i]<1000000*(1-I[i]))
# obj = m.sum([m.sum([((1/(1+r))**(t+1))*((P_CO*m.min3(demand[t,s], theta[s])) \
# + (beta_CO2*P_CO2*m.min3(demand[t,s], theta[s])*(E_ref-E_dir-E_indir_others-E_indir_elec_cons*GWP_EU[t,s])) \
# - (C1_CAPEX+C2_CAPEX*theta[s]+C1_FOX+C2_FOX*theta[s])-(C1_ROX+C2_ROX*m.min3(demand[t,s], theta[s])+C1_UOX+C2_UOX*m.min3(demand[t,s], theta[s]))) for t in range(T)]) for s in range(N)])
m.Maximize(obj/N)
m.solve(disp=True)
# s = m.sum(m.sum(((1/(1+r))**(t+1))*((P_CO*m.min3(demand[t,s], theta[s])) \
# + beta_CO2*P_CO2*m.min3(demand[t,s], theta[s])*(E_ref-E_dir-E_indir_others-E_indir_elec_cons*GWP_EU[t,s]) \
# - (C1_CAPEX + C2_CAPEX*theta[s]) - (C1_FOX + C2_FOX*theta[s]) - (C1_ROX + C2_ROX*m.min3(demand[t,s], theta[s])) - (C1_UOX + C2_UOX*m.min3(demand[t,s], theta[s])))
# for s in range(N)) for t in range(T))/N
print(theta)
I solved this issue by increasing the big M in the constraint for an indicator variable I, 1000000 to 10000000.
for i in range(N):
m.Equation(theta[i]<=10000000*I[i])
m.Equation(-theta[i]<10000000*(1-I[i]))
I didn't understand why this worked, but the result gave me the solution of 200*1 array with all zero.
I use Q1 and Q2 to control T1, so as to realize the simulation scenario of multi-controls. I want to adjust the parameters to achieve which MV has more action, as shown in the figure. I found that I couldn’t achieve the effect I wanted by adjusting the cost of the MV, can anyone give me some suggestions? thanks!
Here is my code:
import tclab
from tclab import labtime
from tclab import TCLabModel
import numpy as np
import time
import matplotlib.pyplot as plt
from gekko import GEKKO
import json
class tclab_heaterpipe():
delay_q1_step = 10
delay_q2_step = 10
q1_buffer = [0] * delay_q1_step
q2_buffer = [0] * delay_q2_step
m = TCLabModel()
def __init__(self, d1, d2, model):
if d1 >= 1 and d2 >= 1:
self.delay_q1_step = int(d1)
self.delay_q2_step = int(d2)
self.q1_buffer = [0] * self.delay_q1_step
self.q2_buffer = [0] * self.delay_q2_step
self.m = model
else:
self.delay_q1_step = 0
self.delay_q2_step = 0
def Q1_delay(self, q1):
if self.delay_q1_step == 0:
self.m.Q1(q1)
self.q1_buffer.insert(0, q1)
self.m.Q1(self.q1_buffer.pop())
def Q2_delay(self, q2):
if self.delay_q2_step == 0:
self.m.Q1(q2)
self.q2_buffer.insert(0, q2)
self.m.Q2(self.q2_buffer.pop())
# Connect to Arduino
connected = False
theta = 1
theta2 = 1
T = tclab.setup(connected)
a = T()
tclab_delay = tclab_heaterpipe(theta, theta2, a)
# Turn LED on
print('LED On')
a.LED(100)
# Run time in minutes
run_time = 80.0
# Number of cycles
loops = int(60.0 * run_time)
#########################################################
# Initialize Model
#########################################################
# use remote=True for MacOS
m = GEKKO(name='tclab-mpc', remote=False)
m.time = np.linspace(0, 400, 41)
step = 10
# Temperature (K)
t1sp = 45.0
T1 = np.ones(int(loops / step) + 1) * a.T1 # temperature (degC)
Tsp1 = np.ones(int(loops / step) + 1) * t1sp # set point (degC)
# heater values
Q1s = np.ones(int(loops / step) + 1) * 0.0
Q2s = np.ones(int(loops / step) + 1) * 0.0
# Parameters
Q1_ss = m.Param(value=0)
TC1_ss = m.Param(value=a.T1)
Q2_ss = m.Param(value=0)
Kp1 = m.Param(value=0.8)
tau1 = m.Param(value=160.0)
Kp2 = m.Param(value=0.1)
tau2 = m.Param(value=160.0)
# Manipulated variable
Q1 = m.MV(value=0, name='q1')
Q1.STATUS = 1 # use to control temperature
Q1.FSTATUS = 0 # no feedback measurement
Q1.LOWER = 0.0
Q1.UPPER = 100.0
Q1.DMAX = 50.0
Q1.DCOST = 1.0
# Q1.COST = 0.1
Q2 = m.MV(value=0, name='q2')
Q2.STATUS = 1 # use to control temperature
Q2.FSTATUS = 0 # no feedback measurement
Q2.LOWER = 0.0
Q2.UPPER = 100.0
Q2.DCOST = 1.0
# Q2.COST = 1.0
# Controlled variable
TC1 = m.CV(value=a.T1, name='tc1')
TC1.STATUS = 1 # minimize error with setpoint range
TC1.FSTATUS = 1 # receive measurement
TC1.TR_INIT = 2 # reference trajectory
TC1.WSPHI = 20
TC1.WSPLO = 20
TC1.TAU = 40 # time constant for response
# TC1.COST = 1
Q1d = m.Var()
m.delay(Q1, Q1d, theta)
Q2d = m.Var()
m.delay(Q2, Q2d, theta2)
# Equation
m.Equation(tau1 * TC1.dt() + (TC1 - TC1_ss) == Kp1 * (Q1d - Q1_ss))
m.Equation(tau2 * TC1.dt() + (TC1 - TC1_ss) == Kp2 * (Q2d - Q2_ss))
# Global Options
m.options.IMODE = 6 # MPC
m.options.CV_TYPE = 3 # Objective type
m.options.NODES = 2 # Collocation nodes
m.options.MAX_TIME = 10
m.options.SOLVER = 1 # 1=APOPT, 3=IPOPT
##################################################################
# Create plot
plt.figure()
plt.ion()
plt.show()
# Main Loop
a.Q1(0)
a.Q2(0)
Q2s[0:] = 0
start_time = time.time()
tm = np.zeros(int(loops / step) + 1)
j = 0
try:
time_start = time.time()
labtime_start = labtime.time()
if (not connected):
labtime.set_rate(10)
for i in tclab.clock(loops, adaptive=False):
i = int(i)
if (i == 0):
continue
print("-----------------------")
t_real = time.time() - time_start
t_lab = labtime.time() - labtime_start
print("real time = {0:4.1f} lab time = {1:4.1f} m.time = {1:4.1f}".format(t_real, t_lab, m.time))
if (i % step != 0):
continue
j = i / step
j = int(j)
print(j)
T1[j] = a.T1
tm[j] = i
###############################
### MPC CONTROLLER ###
###############################
TC1.MEAS = T1[j]
print("T1 meas:{0:4.1f} ".format(a.T1))
# input setpoint with deadband +/- DT
DT = 0.5
TC1.SPHI = Tsp1[j] + DT
TC1.SPLO = Tsp1[j] - DT
try:
# stop model time to solve MPC in cast the solver takes too much time
if (not connected):
labtime.stop()
m.solve(disp=False)
# start model time
if (not connected):
labtime.start()
except Exception as e:
if (not connected):
if (not labtime.running):
labtime.start()
print("sovle's exception:")
print(e)
if (j != 0):
Q1s[j] = Q1s[j - 1]
Q2s[j] = Q2s[j - 1]
continue
# test for successful solution
if (m.options.APPSTATUS == 1):
# retrieve the first Q value
tclab_delay.Q1_delay(Q1.NEWVAL)
tclab_delay.Q2_delay(Q2.NEWVAL)
Q1s[j:] = np.ones(len(Q1s) - j) * Q1.NEWVAL
Q2s[j:] = np.ones(len(Q2s) - j) * Q2.NEWVAL
# a.Q1(Q1.NEWVAL)
# a.Q2(Q2.NEWVAL)
print("Q1 applied with delay: {0:4.1f} ".format(Q1.NEWVAL))
print("Q2 applied with delay: {0:4.1f} ".format(Q2.NEWVAL))
with open(m.path + '//results.json') as f:
results = json.load(f)
else:
# not successful, set heater to zero
Q1s[j] = Q1s[j - 1]
Q2s[j] = Q2s[j - 1]
print("APPSTATUS is not 1,set Q to 0")
if (not connected):
labtime.stop()
# Plot
try:
plt.clf()
ax = plt.subplot(2, 1, 1)
ax.grid()
plt.plot(tm[0:j], T1[0:j], 'ro', markersize=3, label=r'$T_1$')
plt.plot(tm[0:j], Tsp1[0:j], 'r-', markersize=3, label=r'$T_1 Setpoint$')
plt.plot(tm[j] + m.time, results['tc1.bcv'], 'r-.', markersize=1, \
label=r'$T_1$ predicted', linewidth=1)
plt.ylabel('Temperature (degC)')
plt.legend(loc='best')
ax = plt.subplot(2, 1, 2)
ax.grid()
plt.plot(tm[0:j], Q1s[0:j], 'r-', linewidth=3, label=r'$Q_1$')
plt.plot(tm[0:j], Q2s[0:j], 'b-', linewidth=3, label=r'$Q_2$')
plt.plot(tm[j] + m.time, Q1.value, 'r-.', \
label=r'$Q_1$ plan', linewidth=1)
plt.plot(tm[j] + m.time, Q2.value, 'b-.', \
label=r'$Q_2$ plan', linewidth=1)
plt.ylabel('Heaters')
plt.xlabel('Time (sec)')
plt.legend(loc='best')
plt.draw()
plt.pause(0.05)
except Exception as e:
print(e)
pass
if (not connected):
labtime.start()
# Turn off heaters
a.Q1(0)
a.Q2(0)
print('Shutting down')
input("Press Enter to continue...")
a.close()
# Allow user to end loop with Ctrl-C
except KeyboardInterrupt:
# Disconnect from Arduino
a.Q1(0)
a.Q2(0)
print('Shutting down')
a.close()
# Make sure serial connection still closes when there's an error
except:
# Disconnect from Arduino
a.Q1(0)
a.Q2(0)
print('Error: Shutting down')
a.close()
raise
There are a many ways to preferentially select one MV over another with additional resources on the course website with Control Objectives and Control Tuning.
COST (minimize or maximize use of MV)
DCOST (change penalty)
DMAX (move change constraint)
TIER (for strong decoupling)
For this particular problem, it appears that the two MVs (Manipulated Variables) Q1 and Q2 values should be a ratio and there is only one CV (Controlled Variable). An easy way to accomplish this is to add another equation such as:
m.Equation(Q2==1.29*Q1)
This consumes a degree a freedom with the equation so that there is effectively only one MV. If the STATUS is off then this could lead to a solver error (Infeasible solution) so you may want to change one of the MVs to a m.Var() type (such as Q2) if this is a problem.
Python code:
I have used the Python code as below. Here, machine is trained by using Logistic Regression algorithm and wine dataset. Here, problem is that weights are not getting updated. I don't understand where is the problem.
from sklearn import datasets
import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split
dataset = datasets.load_wine()
x = dataset.data
y = dataset.target
y = y.reshape(178,1)
x_train,x_test,y_train,y_test = train_test_split(x,y,test_size=0.15,shuffle=True)
print(x_train.shape)
class log_reg():
def __init__(self):
pass
def sigmoid(self,x):
return 1 / (1 + np.exp(-x))
def train(self,x,y,w1,w2,alpha,iterations):
cost_history = [0] * iterations
Y_train = np.zeros([y.shape[0],3])
for i in range(Y_train.shape[0]):
for j in range(Y_train.shape[1]):
if(y[i] == j):
Y_train[i,j] = 1
for iteration in range(iterations):
z1 = x.dot(w1)
a1 = self.sigmoid(z1)
z2 = a1.dot(w2)
a2 = self.sigmoid(z2)
sig_sum = np.sum(np.exp(a2),axis=1)
sig_sum = sig_sum.reshape(a2.shape[0],1)
op = np.exp(a2) / sig_sum
loss = (Y_train * np.log(op))
dl = (op-Y_train)
dz1 = ((dl*(self.sigmoid(z2))*(1-self.sigmoid(z2))).dot(w2.T))*(self.sigmoid(z1))*(1-self.sigmoid(z1))
dz2 = (dl * (self.sigmoid(z2))*(1-self.sigmoid(z2)))
dw1 = x.T.dot(dz1)
dw2 = a1.T.dot(dz2)
w1 += alpha * dw1
w2 += alpha * dw2
cost_history[iteration] = (np.sum(loss)/len(loss))
return w1,w2,cost_history
def predict(self,x,y,w1,w2):
z1 = x.dot(w1)
a1 = self.sigmoid(z1)
z2 = a1.dot(w2)
a2 = self.sigmoid(z2)
sig_sum = np.sum(np.exp(a2),axis=1)
sig_sum = sig_sum.reshape(a2.shape[0],1)
op = np.exp(a2) / sig_sum
y_preds = np.argmax(op,axis=1)
acc = self.accuracy(y_preds,y)
return y_preds,acc
def accuracy(self,y_preds,y):
y_preds = y_preds.reshape(len(y_preds),1)
correct = (y_preds == y)
accuracy = (np.sum(correct) / len(y)) * 100
return (accuracy)
if __name__ == "__main__":
network = log_reg()
w1 = np.random.randn(14,4) * 0.01
w2 = np.random.randn(4,3) * 0.01
X_train = np.ones([x_train.shape[0],x_train.shape[1]+1])
X_train[:,:-1] = x_train
X_test = np.ones([x_test.shape[0],x_test.shape[1]+1])
X_test[:,:-1] = x_test
new_w1,new_w2,cost = network.train(X_train,y_train,w1,w2,0.0045,10000)
y_preds,accuracy = network.predict(X_test,y_test,new_w1,new_w2)
print(y_preds,accuracy)
In the above code, parameters are mentioned as below
x--training set,
y--target(output),
w1--weights for first layer,
w2--weights for second layer,
I used logistic regression with 2 hidden layers.
I am trying to train dataset wine from sklearn.I don't know where the problem is, but weights are not updating. Any help would be appreciated.
Your weights are updating , but I think you cant see them changing because you are printing them after execution. Python has a object reference method for numpy arrays so when you passed w1 , its values change values too so new_w1 and w1 become the same .
Take this example
import numpy as np
x=np.array([1,2,3,4])
def change(x):
x+=3
return x
print(x)
change(x)
print(x)
if you see the output it comes out as
[1 2 3 4]
[4 5 6 7]
I recommend that you add a bias and fix your accuracy function as I get my accuracy as 1000.
My execution when i run the code
the w1 and w2 values are indeed changing .
the only thing i changed was the main code and enabled the original data set , please do the same and tell if your weights are still not updating
if __name__ == "__main__":
network = log_reg()
w1 = np.random.randn(13,4) * 0.01
w2 = np.random.randn(4,3) * 0.01
print(w1)
print(" ")
print(w2)
print(" ")
new_w1,new_w2,cost = network.train(x_train,y_train,w1,w2,0.0045,10000)
print(w1)
print(" ")
print(w2)
print(" ")
y_preds,accuracy = network.predict(x_test,y_test,new_w1,new_w2)
print(y_preds,accuracy)