In [ ]:
 

More optimization trials

Previously, I tried throwing a bunch of optimizers at my model. I liked the results from the scipy.optimize routines COBYLA and BFGS. Let's try adjusting how we handle the constraints, and see if we get better results.

In [10]:
%load_ext autoreload
%autoreload 2
In [11]:
import matplotlib
import numpy
from IPython.display import HTML, SVG
In [12]:
#matplotlib.use('svg')
import matplotlib.pyplot as plt
plt.rcParams['svg.fonttype'] = 'none'
plt.rcParams['font.sans-serif'] = 'Arial'
In [13]:
from io import BytesIO
def pltsvg():
    imgdata = BytesIO()
    plt.savefig(imgdata)
    imgdata.seek(0)
    display(SVG(data=imgdata.read()))
In [14]:
import ammonia1
import system_aqua1
import scipy.optimize
In [19]:
T_heat_reject = 305.
UAgoal = 100
xB = [400, 1, T_heat_reject, 3, T_heat_reject, 5, 285, 4, T_heat_reject, 0.15]
bdry = system_aqua1.makeBoundary(xB)

Trial 2.1

In general, here is how we compute the modified objective function. GenOpt manual suggests adding functions to the objective, namely separate barrier (B) and penalty (P) functions. The objective function is, with step number as $k$,

$$ Objective = -Q_{cooling}(x) + \mu_1 B(x) + \mu_2 P(x) $$$$ \mu_1 = k^{-2}$$$$ B(x) = \left(\sum_{j=1}^{N_B} g_j(x) \right) ^ {-1} $$$$\mu_2 = k^{2}$$$$ P(x) = \sum_{j=N_B+1}^{N_B + N_P} g_j(x)$$

GenOpt implements a mechanism to pass in the step number. However, using the scipy.optimize routines, passing in the step number is a challenge, so the Problem object should probably use its own counter of function calls. This could be quirky, since then the objective function is changing with each call. Some routines allow for termination after a number of steps, so we could run a while, increment step number, then continue.

But we need to make some decisions.

  • Which constraints should be handled by barriers, and which by penalties?
    • Use barriers for hard constraints, eg. required for feasibility of the chiller model
    • Use penalties for soft constraints, eg. heat exchange feasibility that can be violated now and satisfied later
  • With scipy.optimize, we can directly apply constraints on the input variables (such as $T_1 > T_2$)
In [15]:
def saturate(x, bottom=-numpy.inf, top=0):
    a_bottom = numpy.empty_like(x)
    a_top = numpy.empty_like(x)
    a_bottom.fill(bottom)
    a_top.fill(top)
    return numpy.minimum(a_top,
                         numpy.maximum(a_bottom,
                                       x))

def barrier1(c, length_scale):
    """The domain for B is the feasible set only.
    We want B large near boundary, but small elsewhere.
    Feasible means c > 0 and g < 0. Please input c.
    """
    g = numpy.negative(c)
    b = numpy.exp(g / length_scale)
    return numpy.sum(b)

def decay1(step_number, initial_value = 1., rate = 1.):
    """A decaying function to scale barrier functions.
    step_number: as this increases, I decay.
    initial_value: value when step_number = 0.
    decay_rate: how fast to decay.
    """
    # Magnitude tends to zero, slowly
    mu_B = initial_value * numpy.exp(-rate * step_number)
    return mu_B        

def penalty1(c, length_scale):
    """We want P = 0 for feasible, P > 0 elsewhere
    Feasible means c > 0 and g < 0.
    """
    g = numpy.negative(c)
    g = saturate(g,bottom=0,top=numpy.inf)
    p = (g / length_scale)**2
    return numpy.sum(p)

def grow1(step_number, initial_value = 1., rate = 1.):
    """A growing function to scale penalty functions."""
    # Magnitude tends to infinite, bit more quickly
    mu_P = initial_value * numpy.exp(rate * step_number)
    return mu_P
In [41]:
# Demo barriers
xx = numpy.linspace(0,1)
yy = numpy.linspace(0,1)
X, Y = numpy.meshgrid(xx,yy)
B = numpy.zeros_like(X)
mu_B = 10
for i, (xi,yi) in enumerate(zip(X.flat,Y.flat)):
    # The domain for B is the feasible set only.
    # We want B large near boundary, but small elsewhere.
    # Feasible means c > 0 and g < 0.
    c = numpy.array([xi, yi, (1-xi), (1-yi)])
    # The length scale for the barrier
    dx = 0.05
    B.flat[i] = mu_B * barrier1(c,dx)

plt.close('all')
fig = plt.figure()
plt.plot(X[25],B[25],'r')
plt.title("barrier1()")
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.plot_surface(X,Y,B,cmap=cm.coolwarm)
ax.plot(X[25],Y[25],B[25],'r')
#pltsvg()
plt.show()

xx = numpy.linspace(-1,11)
yy = numpy.linspace(-1,11)
X, Y = numpy.meshgrid(xx,yy)
P = numpy.zeros_like(X)

mu_P = 10
for i, (xi,yi) in enumerate(zip(X.flat,Y.flat)):
    # We want P = 0 for feasible, P > 0 elsewhere
    # Feasible means c > 0 and g < 0.
    c = numpy.array([xi, yi, (10-xi), (10-yi)])
    # The length scale for the penalty
    dx = 1
    P.flat[i] = mu_P * penalty1(c, dx)

plt.close('all')
fig = plt.figure()
plt.plot(X[25],P[25])
plt.title("penalty1")
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.plot_surface(X,Y,P,cmap=cm.coolwarm)
ax.plot(X[25],Y[25],P[25],'b')
#pltsvg()
plt.show()
In [44]:
step_number = numpy.arange(50)
mu_B = decay1(step_number,initial_value=1000,rate=0.2)
mu_P = grow1(step_number,initial_value=1,rate=0.2)
plt.close('all')
plt.figure()
plt.semilogy(step_number,mu_B,'r',label='barrier')
plt.semilogy(step_number,mu_P,'b',label='penalty')
plt.xlabel('Optimization step number')
plt.ylabel('Scaling for constraint function')
plt.legend()
plt.show()
In [21]:
step_number = 39
mu_B = 1 * numpy.exp(0.1 * step_number)
mu_B
Out[21]:
49.402449105530188
In [181]:
class Problem_2_1:
    def __init__(self, bdry, UAgoal, mu=0.1):
        self.bdry = bdry
        self.UAgoal = UAgoal
        self.mu = mu
        self.Ncons = 7
        self.n_calls = 0
        # Soft constraints mode: this is sent to minimizer
        self.constraints = [{'type': 'ineq',
                             'fun': self.constraint,
                             'args': (i,)
                            } for i in range(self.Ncons)]
            
    def objective(self, xC):
        step_number = numpy.floor(self.n_calls / 7)
        self.n_calls += 1
        print(xC,flush=True)
        Q,B,P = 0.,0.,0.
        try:
            ch = system_aqua1.makeChiller(xC)
            sys = system_aqua1.System(self.bdry, ch)
            
            # Barriers
            # Magnitude tends to zero, slowly
            mu_B = 1000. * numpy.exp(-0.1 * step_number)
            length_scale_b = 1
            # Or ... magnitude fixed, but shape changes
            mu_B = 1000
            length_scale_b = 1 * numpy.exp(-0.1 * step_number)
            
            # These are zero at the boundary ...
            barriers = [ch.check_rectifier_delta_T] \
                        + [deltaT
                           for name, deltaT, epsilon, UA, Qhx in sys.data]
            B = mu_B * barrier1(barriers,length_scale_b)
            
            # Penalties
            # Magnitude tends to infinite
            mu_P = 1 * numpy.exp(0.1 * step_number)
            penalties = [(1.0 - sys.totalUA/self.UAgoal)]
            P = mu_P * penalty1(penalties,1)
            
            Q = sys.chiller.Q_evap
        except:
            Q = numpy.inf
        
        print(self.n_calls, step_number, Q, B, P, "\n", flush=True)
        return -Q + B + P
    
    def constraint(self, x, *args):
        cons = [x[0] - 0.1,
                1. - x[0],
                x[2] - x[1] - 1.0,
                x[3] - x[2] - 0.1,
                x[4] - x[1] - 10.0,
                x[5] - x[3] - 1.0,
                x[5] - x[4] - 1.0]
        if len(args) > 0:
            i, = args
            return cons[i]
        else:
            return cons

    def callback(self, x):
        print("Did an iteration at ", x)
In [ ]:
P = Problem_2_1(bdry, UAgoal)
In [183]:
rT = T_heat_reject
x = numpy.array([0.05, 278.45, rT+7, rT+8, rT+5, 395.15])
In [125]:
x = numpy.array([0.51284472, 277.97717012, 312.16427764, 313.6952877,
               310.24856734, 374.14020482])
In [205]:
P.n_calls = 7 * numpy.floor(P.n_calls / 7)
opt = scipy.optimize.minimize(P.objective, x, method="COBYLA",
                              constraints=P.constraints, callback=P.callback,
                              options={'disp':True,'maxiter':1000,'rhobeg':0.05})
opt
[  3.35723183e-01   2.78159709e+02   3.12221943e+02   3.13043890e+02
   3.10475563e+02   3.95211794e+02]
C:\Users\user1\Miniconda3\envs\openachp\lib\site-packages\scipy\optimize\_minimize.py:403: RuntimeWarning: Method COBYLA does not support callback.
  warn('Method %s does not support callback.' % method, RuntimeWarning)
C:\Users\user1\Miniconda3\envs\openachp\lib\site-packages\scipy\interpolate\_cubic.py:147: RuntimeWarning: divide by zero encountered in true_divide
  mk = (y[1:] - y[:-1]) / hk
C:\Users\user1\Miniconda3\envs\openachp\lib\site-packages\scipy\interpolate\_cubic.py:165: RuntimeWarning: invalid value encountered in true_divide
  whmean = (w1/mk[:-1] + w2/mk[1:]) / (w1 + w2)
Note: Absorber inlet is subcooled
267.0 38.0 118.815227129 1.83964553605e-19 5.87765955217 

[  3.85723183e-01   2.78159709e+02   3.12221943e+02   3.13043890e+02
   3.10475563e+02   3.95211794e+02]
Note: Absorber inlet is subcooled
268.0 38.0 136.510643025 7.56828855898e+81 inf 

[  3.35723183e-01   2.78209709e+02   3.12221943e+02   3.13043890e+02
   3.10475563e+02   3.95211794e+02]
Note: Absorber inlet is subcooled
269.0 38.0 119.02522898 2.98340116676e-18 6.27134717824 

[  3.35723183e-01   2.78159709e+02   3.12271943e+02   3.13043890e+02
   3.10475563e+02   3.95211794e+02]
Note: Absorber inlet is subcooled
270.0 38.0 118.690582757 1.31818327891e-19 5.63438826919 

[  3.35723183e-01   2.78159709e+02   3.12271943e+02   3.13093890e+02
   3.10475563e+02   3.95211794e+02]
Note: Absorber inlet is subcooled
271.0 38.0 118.690582757 1.31818327891e-19 5.63395309103 

[  3.35723183e-01   2.78159709e+02   3.12271943e+02   3.13093890e+02
   3.10525563e+02   3.95211794e+02]
Note: Absorber inlet is subcooled
272.0 38.0 118.503217405 7.98684249975e-20 5.32600200197 

[  3.35723183e-01   2.78159709e+02   3.12271943e+02   3.13093890e+02
   3.10525563e+02   3.95261794e+02]
Note: Absorber inlet is subcooled
273.0 38.0 118.586824733 9.98790131717e-20 5.48392007157 

[ nan  nan  nan  nan  nan  nan]
274.0 39.0 inf 0.0 0.0 

[ nan  nan  nan  nan  nan  nan]
275.0 39.0 inf 0.0 0.0 

[  3.48223183e-01   2.78159709e+02   3.12271943e+02   3.13093890e+02
   3.10525563e+02   3.95211794e+02]
Note: Absorber inlet is subcooled
276.0 39.0 122.915454304 1.74958276029e-16 16.3194891161 

[  3.10724408e-01   2.78159535e+02   3.12272056e+02   3.13093891e+02
   3.10525678e+02   3.95211723e+02]
Note: Absorber inlet is subcooled
277.0 39.0 109.677731689 9.91744117312e-33 0.364376001282 

[  3.35810402e-01   2.78147210e+02   3.12271943e+02   3.13093890e+02
   3.10525563e+02   3.95211794e+02]
Note: Absorber inlet is subcooled
278.0 39.0 118.481524866 1.92129913319e-22 5.83044350722 

[  3.59792178e-01   2.78166444e+02   3.12272314e+02   3.13093892e+02
   3.10525940e+02   3.95211562e+02]
Note: Absorber inlet is subcooled
279.0 39.0 127.026445961 1.5142716904e+15 inf 

[  3.35779508e-01   2.78159710e+02   3.12284443e+02   3.13093890e+02
   3.10525563e+02   3.95211794e+02]
Note: Absorber inlet is subcooled
280.0 39.0 118.491907997 3.66302800781e-22 5.84954527756 

[  3.58950380e-01   2.78166217e+02   3.12265388e+02   3.13093891e+02
   3.10525927e+02   3.95211570e+02]
Note: Absorber inlet is subcooled
281.0 40.0 126.74676114 5.65485308044e+13 inf 

[  3.35723390e-01   2.78159709e+02   3.12271943e+02   3.13106390e+02
   3.10525563e+02   3.95211794e+02]
Note: Absorber inlet is subcooled
282.0 40.0 118.503290347 1.02330129865e-24 6.50517317455 

[  3.55912759e-01   2.78165366e+02   3.12266246e+02   3.13081530e+02
   3.10525879e+02   3.95211599e+02]
Note: Absorber inlet is subcooled
283.0 40.0 125.668352643 11321.4202022 inf 

[  3.45817971e-01   2.78162538e+02   3.12269094e+02   3.13087710e+02
   3.10525721e+02   3.95211696e+02]
Note: Absorber inlet is subcooled
284.0 40.0 122.085237224 1.4379498301e-19 14.5977787917 

[  3.35625683e-01   2.78159709e+02   3.12271944e+02   3.13093890e+02
   3.10531813e+02   3.95211794e+02]
Note: Absorber inlet is subcooled
285.0 40.0 118.445397237 8.46965972117e-25 6.41010293802 

[  3.23910046e-01   2.78160404e+02   3.12271207e+02   3.13092150e+02
   3.10522007e+02   3.95211766e+02]
Note: Absorber inlet is subcooled
286.0 40.0 114.350820564 1.36805456137e-30 2.35490579413 

[  3.35737635e-01   2.78159709e+02   3.12271943e+02   3.13093890e+02
   3.10525564e+02   3.95205544e+02]
Note: Absorber inlet is subcooled
287.0 40.0 118.497862144 1.00532378025e-24 6.48883049536 

[  3.42238299e-01   2.78161341e+02   3.12270302e+02   3.13090336e+02
   3.10518780e+02   3.95218851e+02]
Note: Absorber inlet is subcooled
288.0 41.0 120.852006903 7.87121190745e-24 12.1409564608 

[  3.34806745e-01   2.78160016e+02   3.12271630e+02   3.13093190e+02
   3.10531531e+02   3.95213180e+02]
Note: Absorber inlet is subcooled
289.0 41.0 118.161815202 4.40656621753e-28 6.64957026203 

[  3.32816438e-01   2.78159768e+02   3.12271876e+02   3.13093713e+02
   3.10524487e+02   3.95212138e+02]
Note: Absorber inlet is subcooled
290.0 41.0 117.482175418 3.73406594668e-29 5.70483826856 

[  3.35726018e-01   2.78158595e+02   3.12270848e+02   3.13093890e+02
   3.10525563e+02   3.95211794e+02]
Note: Absorber inlet is subcooled
291.0 41.0 118.502275951 1.38292518265e-27 7.18713798309 

[  3.36032360e-01   2.78158305e+02   3.12274713e+02   3.13093816e+02
   3.10525565e+02   3.95211942e+02]
Note: Absorber inlet is subcooled
292.0 41.0 118.599775713 1.93229738621e-27 7.33645420399 

[  3.35567583e-01   2.78159670e+02   3.12271983e+02   3.13095435e+02
   3.10525725e+02   3.95211793e+02]
Note: Absorber inlet is subcooled
293.0 41.0 118.44742756 1.21018438515e-27 7.09953601834 

[  3.36824877e-01   2.78162129e+02   3.12271979e+02   3.13092388e+02
   3.10524961e+02   3.95212070e+02]
Note: Absorber inlet is subcooled
294.0 41.0 118.904922607 7.31440671539e-27 7.87121378385 

[  3.35287097e-01   2.78159911e+02   3.12271983e+02   3.13093529e+02
   3.10527005e+02   3.95211820e+02]
Note: Absorber inlet is subcooled
295.0 42.0 118.344687786 5.82600014981e-31 7.66525816026 

[  3.35644264e-01   2.78159820e+02   3.12272049e+02   3.13093885e+02
   3.10525534e+02   3.95211032e+02]
Note: Absorber inlet is subcooled
296.0 42.0 118.47440066 9.71605831978e-31 7.89323789849 

[  3.36142980e-01   2.78158859e+02   3.12271187e+02   3.13093586e+02
   3.10525175e+02   3.95212646e+02]
Note: Absorber inlet is subcooled
297.0 42.0 118.652593564 1.85566910474e-30 8.21672987782 

[  3.35064448e-01   2.78159824e+02   3.12272035e+02   3.13093813e+02
   3.10525258e+02   3.95212030e+02]
Note: Absorber inlet is subcooled
298.0 42.0 118.272485278 4.34283046979e-31 7.54565740409 

[  3.35735765e-01   2.78159972e+02   3.12271656e+02   3.13093907e+02
   3.10525543e+02   3.95211792e+02]
Note: Absorber inlet is subcooled
299.0 42.0 118.509553074 1.12919810564e-30 7.95823878303 

[  3.36130185e-01   2.78159811e+02   3.12272554e+02   3.13093855e+02
   3.10525547e+02   3.95212040e+02]
Note: Absorber inlet is subcooled
300.0 42.0 118.646255892 1.92739841019e-30 8.20292827332 

[  3.35755873e-01   2.78159334e+02   3.12271850e+02   3.13093853e+02
   3.10525571e+02   3.95211816e+02]
Note: Absorber inlet is subcooled
301.0 42.0 118.513423222 1.09921499945e-30 7.96274809059 

[  3.35707458e-01   2.78159727e+02   3.12271942e+02   3.13093716e+02
   3.10525649e+02   3.95211796e+02]
Note: Absorber inlet is subcooled
302.0 43.0 118.497425961 3.60294268906e-34 8.76957096135 

[  3.35614604e-01   2.78159712e+02   3.12271997e+02   3.13094155e+02
   3.10525822e+02   3.95211823e+02]
Note: Absorber inlet is subcooled
303.0 43.0 118.463850823 3.10376833478e-34 8.70329987449 

[  3.35721745e-01   2.78159711e+02   3.12271992e+02   3.13093928e+02
   3.10525478e+02   3.95211629e+02]
Note: Absorber inlet is subcooled
304.0 43.0 118.502638522 3.68220182163e-34 8.77973974858 

[  3.35632107e-01   2.78159704e+02   3.12271936e+02   3.13093883e+02
   3.10525533e+02   3.95211807e+02]
Note: Absorber inlet is subcooled
305.0 43.0 118.471201745 3.20409088562e-34 8.71846564125 

[  3.35861839e-01   2.78159731e+02   3.12272002e+02   3.13093922e+02
   3.10525522e+02   3.95211904e+02]
Note: Absorber inlet is subcooled
306.0 43.0 118.55244498 4.59340870007e-34 8.878567788 

[  3.35720092e-01   2.78159709e+02   3.12271948e+02   3.13093947e+02
   3.10525645e+02   3.95211783e+02]
Note: Absorber inlet is subcooled
307.0 43.0 118.501790098 3.66803421587e-34 8.77805237218 

[  3.35721684e-01   2.78159757e+02   3.12271929e+02   3.13093894e+02
   3.10525561e+02   3.95211792e+02]
Note: Absorber inlet is subcooled
308.0 43.0 118.502928941 3.69946784906e-34 8.78072236229 

[  3.35754329e-01   2.78159652e+02   3.12271869e+02   3.13093885e+02
   3.10525551e+02   3.95211787e+02]
Note: Absorber inlet is subcooled
309.0 44.0 118.514192111 5.71096258082e-38 9.72849873818 

Out[205]:
     fun: -108.78569337262947
   maxcv: 0.0
 message: 'Optimization terminated successfully.'
    nfev: 43
  status: 1
 success: True
       x: array([  3.35754329e-01,   2.78159652e+02,   3.12271869e+02,
         3.13093885e+02,   3.10525551e+02,   3.95211787e+02])
In [206]:
x = opt.x
x
Out[206]:
array([  3.35754329e-01,   2.78159652e+02,   3.12271869e+02,
         3.13093885e+02,   3.10525551e+02,   3.95211787e+02])
In [ ]:
# Let's bebug bad points encountered during optimization.
# For reference, here is a good case.
good_X1 = numpy.array([0.51284472,
                       277.97717012,
                       312.16427764,
                       313.6952877,
                       310.24856734,
                       374.14020482])
# Now, let's compare some degenerate cases.
# This one requires absorber stream to compute points at high concentrations.
bad_X1 = numpy.array([0.2,
                      290.95402556,
                      306.64229519,
                      313.6952877,
                      300.86211291,
                      384.36256216])
# This one is not feasible, insofar as it yields positive rectifier heat flow,
# that is, heat *input* to rectifier, which should be rejected as "bad" design.
# The cause is that T_abs is very low, so the rich stream is very rich,
# and in fact the rectifier isn't needed...
# So, I'm adding a constraint to keep the absorber warmer than evaporator.
bad_X2 = numpy.array([1.,
                      293.03582607,
                      308.98651075,
                      313.6952877,
                      293.29251019,
                      386.99474])
# This one is also degenerate in the rectifier.
# Try increasing the padding on the constraints.
bad_X3 = numpy.array([1.,
                      292.26696584,
                      301.95207453,
                      314.19203364,
                      293.26696584,
                      389.23622472])
bad_X4 = numpy.array([1.,
                      277.22895294,
                      278.22895294,
                      308.37781922,
                      282.22895294,
                      417.08853392])
x = good_X1
In [207]:
ch = system_aqua1.makeChiller(x)
display(ch)
P.constraint(x)

State points

T P x h s u v Qu
rich_abs_outlet 310.526 4.906720.511574 -71.95660.399293 -72.56040.00123051 0
rich_pump_outlet 310.69115.1852 0.511574 -70.37590.400311 -72.24370.00123003-0.001
rich_shx_outlet 350.69815.1852 0.511574 112.588 0.954023 110.612 0.00130159-0.001
rich_gen_sat_liquid 352.14 15.1852 0.511574 119.782 0.974617 117.801 0.00130449 0
weak_gen_outlet 395.21215.1852 0.30058 347.264 1.53717 345.364 0.00125104 0
weak_shx_outlet 336.73 15.1852 0.30058 85.24030.820382 83.48960.00115292-0.001
weak_exp_outlet 336.908 4.906720.30058 85.24020.823901 84.67410.00115374-0.001
gen_vapor_outlet 352.14 15.1852 0.9880311425.08 4.57477 1269.12 0.102702 1
gen_reflux_inlet 352.14 15.1852 0.511424 119.346 0.973276 117.365 0.00130473 0
refrig_rect_outlet 313.09415.1852 0.9998691292.42 4.17683 1162.78 0.0853739 0.996888
refrig_cond_outlet 312.27215.1852 0.999869 186.569 0.643511 183.944 0.00172852 0
refrig_cehx_liquid_outlet295.16715.1852 0.999869 103.377 0.369785 100.875 0.00164742-0.001
refrig_exp_outlet 276.772 4.906720.999869 103.377 0.384285 94.01660.0190757 0.0691058
refrig_evap_outlet 278.16 4.906720.9998691273.24 4.59239 1147.59 0.256488 0.998
refrig_cehx_sat_vapor 283.618 4.906720.9998691276.72 4.6039 1150.62 0.257009 1
refrig_cehx_vapor_outlet 310.455 4.906720.9998691356.43 4.87414 1211.89 0.294582 1.001
rectifier_liquid 313.09415.1852 0.97787 174.346 0.664163 171.765 0.00169968 0
gen_vapor_formation 395.21215.1852 0.8952721637.8 5.11196 1459.42 0.11747 1
abs_vapor_final 336.908 4.906720.9731641448.95 5.15703 1290.6 0.322724 1

Performance variables

name unit value
Q_abs kW -181.559
Q_gen kW 191.268
Q_cond kW -112.029
Q_evap kW 118.514
Q_reflux kW -16.7246
Q_shx kW 61.431
Q_cehx kW 8.42784
W_pump kW 0.53072
COP kW/kW 0.619624
ZeroCheck kW 3.98942e-05
ZeroCheckSHX kW 5.60349e-06
ZeroCheckCEHX kW -2.1214e-05
Q_gen_rect_combo kW 174.543
Q_refrig_side kW -6.48495
ZeroCheckRect kg/s 0
m_rich kg/s 0.335754
m_weak kg/s 0.234448
m_gen_vapor kg/s 0.103822
m_gen_reflux kg/s 0.00251633
m_refrig kg/s 0.101306
check_rectifier_delta_TK 39.0464
is_degenerate bool 0
Out[207]:
[0.23575432887826739,
 0.66424567112173261,
 33.112217153465394,
 0.72201591814776978,
 22.365898497033925,
 81.117901789055793,
 83.686236363635032]
In [208]:
sys = system_aqua1.System(bdry, ch)
sys
Note: Absorber inlet is subcooled
C:\Users\user1\Miniconda3\envs\openachp\lib\site-packages\scipy\interpolate\_cubic.py:147: RuntimeWarning: divide by zero encountered in true_divide
  mk = (y[1:] - y[:-1]) / hk
C:\Users\user1\Miniconda3\envs\openachp\lib\site-packages\scipy\interpolate\_cubic.py:165: RuntimeWarning: invalid value encountered in true_divide
  whmean = (w1/mk[:-1] + w2/mk[1:]) / (w1 + w2)
Out[208]:
name deltaT epsilon UA Q
gen 2.66874 0.944904 35.1378 191.268
rect 7.90461 0.811331 1.21624 16.7246
abs 1.6352 0.920468 37.6556 181.559
cond 2.22017 0.966795 27.4181 112.029
evap 1.13766 0.982872 33.1324 118.514
total 0 0 134.56 0
In [209]:
1-sys.totalUA/P.UAgoal
Out[209]:
-0.3456010499915354
In [210]:
penalty1([(1 - sys.totalUA/P.UAgoal)], 1)
Out[210]:
0.11944008575525175
In [211]:
box = numpy.ones_like(x)
box[0] = 0.05
ss = numpy.linspace(-1,1,5)
yyy = numpy.zeros([len(box),len(ss)])
for i,boxi in enumerate(box):
    yy = numpy.zeros_like(ss)
    for j, s in enumerate(ss):
        x_vary = x.copy()
        x_vary[i] += boxi * s
        yyy[i,j] = P.objective(x_vary)
        if (numpy.array(P.constraint(x_vary)) < 0).any():
            yyy[i,j] = numpy.nan
[  2.85754329e-01   2.78159652e+02   3.12271869e+02   3.13093885e+02
   3.10525551e+02   3.95211787e+02]
Note: Absorber inlet is subcooled
C:\Users\user1\Miniconda3\envs\openachp\lib\site-packages\scipy\interpolate\_cubic.py:147: RuntimeWarning: divide by zero encountered in true_divide
  mk = (y[1:] - y[:-1]) / hk
C:\Users\user1\Miniconda3\envs\openachp\lib\site-packages\scipy\interpolate\_cubic.py:165: RuntimeWarning: invalid value encountered in true_divide
  whmean = (w1/mk[:-1] + w2/mk[1:]) / (w1 + w2)
310.0 44.0 100.865247344 1.43509581919e-55 0.0 

[  3.10754329e-01   2.78159652e+02   3.12271869e+02   3.13093885e+02
   3.10525551e+02   3.95211787e+02]
Note: Absorber inlet is subcooled
311.0 44.0 109.689719727 1.55608826459e-55 0.605129864949 

[  3.35754329e-01   2.78159652e+02   3.12271869e+02   3.13093885e+02
   3.10525551e+02   3.95211787e+02]
Note: Absorber inlet is subcooled
312.0 44.0 118.514192111 5.71096258082e-38 9.72849873818 

[  3.60754329e-01   2.78159652e+02   3.12271869e+02   3.13093885e+02
   3.10525551e+02   3.95211787e+02]
Note: Absorber inlet is subcooled
313.0 44.0 127.338664494 4.55957213255e+27 inf 

[  3.85754329e-01   2.78159652e+02   3.12271869e+02   3.13093885e+02
   3.10525551e+02   3.95211787e+02]
Note: Absorber inlet is subcooled
314.0 44.0 136.163136877 5.56939771765e+146 inf 

[  3.35754329e-01   2.77159652e+02   3.12271869e+02   3.13093885e+02
   3.10525551e+02   3.95211787e+02]
Note: Absorber inlet is subcooled
315.0 44.0 114.338968687 2.40329962213e-65 2.687489943 

[  3.35754329e-01   2.77659652e+02   3.12271869e+02   3.13093885e+02
   3.10525551e+02   3.95211787e+02]
Note: Absorber inlet is subcooled
316.0 45.0 116.420963016 2.15818192697e-66 5.69492376052 

[  3.35754329e-01   2.78159652e+02   3.12271869e+02   3.13093885e+02
   3.10525551e+02   3.95211787e+02]
Note: Absorber inlet is subcooled
317.0 45.0 118.514192111 3.34414797942e-42 10.751653882 

[  3.35754329e-01   2.78659652e+02   3.12271869e+02   3.13093885e+02
   3.10525551e+02   3.95211787e+02]
Note: Absorber inlet is subcooled
318.0 45.0 120.619095315 8.03664712607e-18 22.644273066 

[  3.35754329e-01   2.79159652e+02   3.12271869e+02   3.13093885e+02
   3.10525551e+02   3.95211787e+02]
Note: Absorber inlet is subcooled
319.0 45.0 122.736122265 20981707.0661 inf 

[  3.35754329e-01   2.78159652e+02   3.11271869e+02   3.13093885e+02
   3.10525551e+02   3.95211787e+02]
Note: Absorber inlet is subcooled
320.0 45.0 120.992907214 2.09561310353e-36 27.3760272697 

[  3.35754329e-01   2.78159652e+02   3.11771869e+02   3.13093885e+02
   3.10525551e+02   3.95211787e+02]
Note: Absorber inlet is subcooled
321.0 45.0 119.757606487 2.7056771193e-39 16.6787008014 

[  3.35754329e-01   2.78159652e+02   3.12271869e+02   3.13093885e+02
   3.10525551e+02   3.95211787e+02]
Note: Absorber inlet is subcooled
322.0 45.0 118.514192111 3.34414797942e-42 10.751653882 

[  3.35754329e-01   2.78159652e+02   3.12771869e+02   3.13093885e+02
   3.10525551e+02   3.95211787e+02]
Note: Absorber inlet is subcooled
323.0 46.0 117.262528974 4.08871513048e-50 7.81103304785 

[  3.35754329e-01   2.78159652e+02   3.13271869e+02   3.13093885e+02
   3.10525551e+02   3.95211787e+02]
Note: Absorber inlet is subcooled
324.0 46.0 116.002478241 inf inf 

[  3.35754329e-01   2.78159652e+02   3.12271869e+02   3.12093885e+02
   3.10525551e+02   3.95211787e+02]
C:\Users\user1\Miniconda3\envs\openachp\lib\site-packages\ipykernel_launcher.py:16: RuntimeWarning: overflow encountered in exp
  app.launch_new_instance()
Note: Absorber inlet is subcooled
325.0 46.0 118.514192111 inf inf 

[  3.35754329e-01   2.78159652e+02   3.12271869e+02   3.12593885e+02
   3.10525551e+02   3.95211787e+02]
Note: Absorber inlet is subcooled
326.0 46.0 118.514192111 7.02641439392e-47 11.8866935038 

[  3.35754329e-01   2.78159652e+02   3.12271869e+02   3.13093885e+02
   3.10525551e+02   3.95211787e+02]
Note: Absorber inlet is subcooled
327.0 46.0 118.514192111 7.02641439392e-47 11.8824151916 

[  3.35754329e-01   2.78159652e+02   3.12271869e+02   3.13593885e+02
   3.10525551e+02   3.95211787e+02]
Note: Absorber inlet is subcooled
328.0 46.0 118.514192111 7.02641439392e-47 11.8748164182 

[  3.35754329e-01   2.78159652e+02   3.12271869e+02   3.14093885e+02
   3.10525551e+02   3.95211787e+02]
Note: Absorber inlet is subcooled
329.0 46.0 118.514192111 7.02641439392e-47 11.8695190366 

[  3.35754329e-01   2.78159652e+02   3.12271869e+02   3.13093885e+02
   3.09525551e+02   3.95211787e+02]
Note: Absorber inlet is subcooled
330.0 47.0 122.29076179 1.10260036825e-23 50.3356763434 

[  3.35754329e-01   2.78159652e+02   3.12271869e+02   3.13093885e+02
   3.10025551e+02   3.95211787e+02]
Note: Absorber inlet is subcooled
331.0 47.0 120.394800265 1.12083178694e-46 23.7284793019 

[  3.35754329e-01   2.78159652e+02   3.12271869e+02   3.13093885e+02
   3.10525551e+02   3.95211787e+02]
Note: Absorber inlet is subcooled
332.0 47.0 118.514192111 4.75597565064e-52 13.1320997062 

[  3.35754329e-01   2.78159652e+02   3.12271869e+02   3.13093885e+02
   3.11025551e+02   3.95211787e+02]
Note: Absorber inlet is subcooled
333.0 47.0 116.648499244 2.22774921372e-57 7.58586929513 

[  3.35754329e-01   2.78159652e+02   3.12271869e+02   3.13093885e+02
   3.11525551e+02   3.95211787e+02]
Note: Absorber inlet is subcooled
334.0 47.0 114.797302064 1.14788946439e-62 4.34377867887 

[  3.35754329e-01   2.78159652e+02   3.12271869e+02   3.13093885e+02
   3.10525551e+02   3.94211787e+02]
Note: Absorber inlet is subcooled
335.0 47.0 116.827181785 7.21564317268e-57 7.07326294722 

[  3.35754329e-01   2.78159652e+02   3.12271869e+02   3.13093885e+02
   3.10525551e+02   3.94711787e+02]
Note: Absorber inlet is subcooled
336.0 47.0 117.674206991 1.89588670356e-54 9.72978744193 

[  3.35754329e-01   2.78159652e+02   3.12271869e+02   3.13093885e+02
   3.10525551e+02   3.95211787e+02]
Note: Absorber inlet is subcooled
337.0 48.0 118.514192111 9.20587431207e-58 14.5132146886 

[  3.35754329e-01   2.78159652e+02   3.12271869e+02   3.13093885e+02
   3.10525551e+02   3.95711787e+02]
Note: Absorber inlet is subcooled
338.0 48.0 119.34723381 3.92574721873e-55 19.3489855646 

[  3.35754329e-01   2.78159652e+02   3.12271869e+02   3.13093885e+02
   3.10525551e+02   3.96211787e+02]
Note: Absorber inlet is subcooled
339.0 48.0 120.17342605 1.59278198962e-52 25.634194823 

In [213]:
plt.close('all')
plt.figure(figsize=(10, 4))
ax1=plt.subplot(1,len(box), 1)
for i,_ in enumerate(box):
    ax=plt.subplot(1, len(box), i+1, sharey=ax1)
    xxx = x[i] + ss * box[i]
    plt.plot(xxx, yyy[i,:], '.-')
    plt.setp(ax.get_yticklabels(), visible=bool(i==0))
    plt.xlim(min(xxx),max(xxx))
plt.ylim(min(yyy.flat),min(yyy.flat)*0.9)
plt.show()
In [173]:
x = numpy.array([  0.31,   277.8,   313.,
         314.2,   311.5,   394.5])

Trial 2.2

Just brainstorming ... As a brute-force backup, I would like to have an optimize routine that is a gradient-based method where the gradient calculation and line-search algorithms simply react to passing over the feasible boundary using exceptions instead of constraint functions. (This matters because the total UA constraint function can only be computed when the feasible heat exchange constraints are already satisfied, so in some cases it would have to return infinite values.) In other words, this would be an adaptive step size method. In pseudo-code,

In [ ]:
def optimize(fun,x0,max_iter = 100):
    x = x0.copy()
    for iter in range(max_iter):
        if step == 0:
            break
        G, H = calcGradient(fun, x)
        dx = calcLineSearch(fun, x, G, H)
        x = x + dx
        f = fun(x)
    return x, f

def calcGradient(fun, x0, max_iter = 100):
    max_iter = 100
    # Initial step sizes by component
    deltax = ones_like(x0)
    # Function values to the right
    fplus = zeros_like(x0)
    # Function values to the left
    fminus = zeros_like(x0)
    f0 = fun(x0)
    for j, _ in enumerate(deltax):
        for i in range(max_iter):
            dx = numpy.diag(deltax)[j]
            try:
                xplus = x0 + dx
                xminus = x0 - dx
                fplus[j] = fun(xplus)
                fminus[j] = fun(xminus)
                break
            except:
                deltax[j] *= 0.5
    grad, hess = calcGradHelper(f0, deltax, fplus, fminus) # TODO
    return grad, hess

def calcLineSearch(fun, x0, G, H):
    max_iter = 100
    step = 0
    direction = lshelper1(x0, G, H)
    for i in range(max_iter):
        try:
            lshelper(f
        except:
            step *= 0.5
    return step * direction

Aside

Furthermore, here is another related strategy. I could optimize first without the constraint on total UA. That would give me a feasible system. Then, given the desired total UA, I could have a feasible starting point by then turning down the mass flow rate using a linesearch on that parameter.

Trial 2.3

Another idea (suggested in documention of early trials) is to do something different with the heat exchange feasibility constraints, utilizing energy imbalance. The final goal would be to allow UA values as inputs, and optimize the cooling capacity, as intended. An intermediate goal may be to simply demonstrate a solver with UA values as inputs (using some optimization routine to determine the temperature points that achieve the specified UA values).

Trial 2.4

New idea: use machine learning to build constraint functions, as follows. Let the optimization routine run. For each input evaluated, classify its feasibility (bonus: add dimensions for root cause of failure). Gather the data for classified points and generate a classification function through regression, etc. Restart the optimizer with this function

Trial 2.5

An extension of a previous idea, kind of the dual problem -- Perform optimization in two steps: first with no constraint on UA (cost) to determine maximum Q (output). Then for each Q in a set of lesser values, optimize with respect to cost. This will deliver an optimal cost vs output curve.

For the first step, since we are not interested in UA, we can be less strict with the constraints on heat exchange feasibility. Specifically, we can impose them via penalty constraints instead of barrier constraints.

In [92]:
class Problem_2_5_A:
    def __init__(self, bdry, UAgoal, mu=0.1):
        self.bdry = bdry
        self.UAgoal = UAgoal
        self.mu = mu
        self.Ncons = 7
        self.n_calls = 0
        # Soft constraints mode: this is sent to minimizer
        self.constraints = [{'type': 'ineq',
                             'fun': self.constraint,
                             'args': (i,)
                            } for i in range(self.Ncons)]
            
    def objective(self, xC):
        step_number = numpy.floor(self.n_calls / 7)
        self.n_calls += 1
        print(xC,flush=True)
        Q,B,P = 0.,0.,0.
        try:
            ch = system_aqua1.makeChiller(xC)
            sys = system_aqua1.System(self.bdry, ch)
            
            # Barriers
            # Magnitude tends to zero, slowly
            mu_B = 1000. * numpy.exp(-0.1 * step_number)
            length_scale_b = 1
            # Or ... magnitude fixed, but shape changes
            mu_B = 1000
            length_scale_b = 1 * numpy.exp(-0.1 * step_number)
            
            # These are zero at the boundary ...
            barriers = [ch.check_rectifier_delta_T]                        
            B = mu_B * barrier1(barriers,length_scale_b)
            
            # Penalties
            # Magnitude tends to infinite
            mu_P = 1 * numpy.exp(0.3 * step_number)
            penalties = [deltaT - 0.01
                         for name, deltaT, epsilon, UA, Qhx in sys.data]
            P = mu_P * penalty1(penalties,1)
            
            Q = sys.chiller.Q_evap
        except:
            Q = numpy.inf
        
        print(self.n_calls, step_number, Q, B, P, "\n", flush=True)
        return -Q + B + P
    
    def constraint(self, x, *args):
        cons = [x[0] - 0.1,
                1. - x[0],
                x[2] - x[1] - 1.0,
                x[3] - x[2] - 0.1,
                x[4] - x[1] - 10.0,
                x[5] - x[3] - 1.0,
                x[5] - x[4] - 1.0]
        if len(args) > 0:
            i, = args
            return cons[i]
        else:
            return cons

    def callback(self, x):
        print("Did an iteration at ", x)
In [93]:
P = Problem_2_5_A(bdry, UAgoal)
rT = T_heat_reject
x = numpy.array([0.05, 278.45, rT+7, rT+8, rT+5, 395.15])
In [119]:
P.n_calls = 7 * numpy.floor(P.n_calls / 7)
opt = scipy.optimize.minimize(P.objective, x, method="COBYLA",
                              constraints=P.constraints, callback=P.callback,
                              options={'disp':True,'maxiter':1000,'rhobeg':0.001})
opt
[  3.54011747e-01   2.78602326e+02   3.11987016e+02   3.13008619e+02
   3.09938338e+02   3.95252752e+02]
C:\Users\nfette\Miniconda3\envs\openACHP\lib\site-packages\scipy\optimize\_minimize.py:403: RuntimeWarning: Method COBYLA does not support callback.
  warn('Method %s does not support callback.' % method, RuntimeWarning)
Note: Absorber inlet is subcooled
C:\Users\nfette\Miniconda3\envs\openACHP\lib\site-packages\scipy\interpolate\_cubic.py:147: RuntimeWarning: divide by zero encountered in true_divide
  mk = (y[1:] - y[:-1]) / hk
C:\Users\nfette\Miniconda3\envs\openACHP\lib\site-packages\scipy\interpolate\_cubic.py:165: RuntimeWarning: invalid value encountered in true_divide
  whmean = (w1/mk[:-1] + w2/mk[1:]) / (w1 + w2)
183.0 26.0 130.079516499 3.2465978731e-217 0.0576251119286 

[  3.55011747e-01   2.78602326e+02   3.11987016e+02   3.13008619e+02
   3.09938338e+02   3.95252752e+02]
Note: Absorber inlet is subcooled
184.0 26.0 130.446960567 3.2465978731e-217 1.75826559506 

[  3.54011747e-01   2.78603326e+02   3.11987016e+02   3.13008619e+02
   3.09938338e+02   3.95252752e+02]
Note: Absorber inlet is subcooled
185.0 26.0 130.08398977 3.30312287019e-217 0.0914806458042 

[  3.54011747e-01   2.78602326e+02   3.11988016e+02   3.13008619e+02
   3.09938338e+02   3.95252752e+02]
Note: Absorber inlet is subcooled
186.0 26.0 130.076923393 3.19477325953e-217 0.0540044838839 

[  3.54011747e-01   2.78602326e+02   3.11988016e+02   3.13009619e+02
   3.09938338e+02   3.95252752e+02]
Note: Absorber inlet is subcooled
187.0 26.0 130.076923393 3.23807771555e-217 0.0540044838839 

[  3.54011747e-01   2.78602326e+02   3.11988016e+02   3.13008619e+02
   3.09939338e+02   3.95252752e+02]
Note: Absorber inlet is subcooled
188.0 26.0 130.072917502 3.14686552989e-217 0.0486421447564 

[  3.54011747e-01   2.78602326e+02   3.11988016e+02   3.13008619e+02
   3.09939338e+02   3.95253752e+02]
Note: Absorber inlet is subcooled
189.0 26.0 130.074644751 3.14686552989e-217 0.0509198869268 

[  3.53011991e-01   2.78602304e+02   3.11988017e+02   3.13008619e+02
   3.09939339e+02   3.95252752e+02]
Note: Absorber inlet is subcooled
190.0 27.0 129.705476555 2.58489344581e-240 0.0 

[  3.54509639e-01   2.78602281e+02   3.11988018e+02   3.13008619e+02
   3.09939340e+02   3.95252752e+02]
Note: Absorber inlet is subcooled
191.0 27.0 130.255636333 2.5836371161e-240 0.776204087277 

[  3.53988855e-01   2.78602078e+02   3.11988016e+02   3.13008619e+02
   3.09939338e+02   3.95252752e+02]
Note: Absorber inlet is subcooled
192.0 27.0 130.063392746 2.57380739799e-240 0.0438110675076 

[  3.53514711e-01   2.78602381e+02   3.11988016e+02   3.13008619e+02
   3.09939339e+02   3.95252752e+02]
Note: Absorber inlet is subcooled
193.0 27.0 129.890532457 2.58869373871e-240 0.0 

[  3.54010950e-01   2.78602327e+02   3.11988266e+02   3.13008619e+02
   3.09939338e+02   3.95252752e+02]
Note: Absorber inlet is subcooled
194.0 27.0 130.071976637 2.5745910952e-240 0.0640169357483 

[  3.53515697e-01   2.78602381e+02   3.11987984e+02   3.13008619e+02
   3.09939339e+02   3.95252752e+02]
Note: Absorber inlet is subcooled
195.0 27.0 129.89097716 2.59016014394e-240 0.0 

[  3.54011747e-01   2.78602326e+02   3.11988016e+02   3.13008869e+02
   3.09939338e+02   3.95252752e+02]
Note: Absorber inlet is subcooled
196.0 27.0 130.072917502 2.59569739233e-240 0.0656600275189 

[  3.53516659e-01   2.78602381e+02   3.11987985e+02   3.13008588e+02
   3.09939339e+02   3.95252752e+02]
Note: Absorber inlet is subcooled
197.0 28.0 129.891329747 7.94401007607e-266 0.0 

[  3.53764203e-01   2.78602354e+02   3.11988000e+02   3.13008603e+02
   3.09939339e+02   3.95252752e+02]
Note: Absorber inlet is subcooled
198.0 28.0 129.982123737 7.93910215056e-266 0.0 

[  3.54011902e-01   2.78602326e+02   3.11988016e+02   3.13008619e+02
   3.09939463e+02   3.95252752e+02]
Note: Absorber inlet is subcooled
199.0 28.0 130.072473635 7.91591325332e-266 0.0875800003394 

[  3.54130994e-01   2.78602328e+02   3.11987973e+02   3.13008573e+02
   3.09939128e+02   3.95252752e+02]
Note: Absorber inlet is subcooled
200.0 28.0 130.117694437 7.96617291264e-266 0.227005309549 

[  3.54011810e-01   2.78602326e+02   3.11988016e+02   3.13008619e+02
   3.09939338e+02   3.95252627e+02]
Note: Absorber inlet is subcooled
201.0 28.0 130.07272377 7.93415906736e-266 0.0881719523407 

[  3.54064468e-01   2.78602327e+02   3.11987997e+02   3.13008599e+02
   3.09939562e+02   3.95252846e+02]
Note: Absorber inlet is subcooled
202.0 28.0 130.091605282 7.9019316894e-266 0.138613782518 

[  3.54040083e-01   2.78602327e+02   3.11988006e+02   3.13008608e+02
   3.09939258e+02   3.95252803e+02]
Note: Absorber inlet is subcooled
203.0 28.0 130.083765748 7.94620850179e-266 0.116291007798 

[  3.54011070e-01   2.78602376e+02   3.11988016e+02   3.13008619e+02
   3.09939338e+02   3.95252752e+02]
Note: Absorber inlet is subcooled
204.0 29.0 130.072892451 5.0539807989e-294 0.12224190688 

[  3.53928280e-01   2.78602273e+02   3.11988013e+02   3.13008616e+02
   3.09939341e+02   3.95252764e+02]
Note: Absorber inlet is subcooled
205.0 29.0 130.042022571 5.04150739264e-294 0.0394228816244 

[  3.54011632e-01   2.78602326e+02   3.11988052e+02   3.13008584e+02
   3.09939338e+02   3.95252752e+02]
Note: Absorber inlet is subcooled
206.0 29.0 130.07278167 5.04096989069e-294 0.119205800851 

[  3.54026579e-01   2.78602304e+02   3.11988066e+02   3.13008701e+02
   3.09939340e+02   3.95252757e+02]
Note: Absorber inlet is subcooled
207.0 29.0 130.07814301 5.04750184842e-294 0.135729043157 

[  3.54051235e-01   2.78602285e+02   3.11987952e+02   3.13008610e+02
   3.09939387e+02   3.95252762e+02]
Note: Absorber inlet is subcooled
208.0 29.0 130.087226628 5.04438564639e-294 0.167294168501 

Out[119]:
     fun: -129.91993245970548
   maxcv: 0.0
 message: 'Optimization terminated successfully.'
    nfev: 26
  status: 1
 success: True
       x: array([  3.54051235e-01,   2.78602285e+02,   3.11987952e+02,
         3.13008610e+02,   3.09939387e+02,   3.95252762e+02])
In [120]:
x = opt.x
x
Out[120]:
array([  3.54051235e-01,   2.78602285e+02,   3.11987952e+02,
         3.13008610e+02,   3.09939387e+02,   3.95252762e+02])
In [121]:
ch = system_aqua1.makeChiller(x)
display(ch)

State points

T P x h s u v Qu
rich_abs_outlet 309.939 4.985710.518919 -74.179 0.390575 -74.79430.0012341 0
rich_pump_outlet 310.10215.0651 0.518919 -72.62440.391578 -74.48290.00123364-0.001
rich_shx_outlet 349.63215.0651 0.518919 108.341 0.940607 106.375 0.00130486-0.001
rich_gen_sat_liquid 350.54815.0651 0.518919 112.646 0.952907 110.677 0.00130693 0
weak_gen_outlet 395.25315.0651 0.298796 348.035 1.53813 346.152 0.00124995 0
weak_shx_outlet 336.33115.0651 0.298796 84.24510.816148 82.51020.00115162-0.001
weak_exp_outlet 336.506 4.985710.298796 84.24510.819599 83.67050.00115242-0.001
gen_vapor_outlet 350.54815.0651 0.9890691420.13 4.56407 1265 0.102976 1
gen_reflux_inlet 350.54815.0651 0.518918 112.644 0.952898 110.675 0.00130694 0
refrig_rect_outlet 313.00915.0651 0.9998691294.32 4.18595 1164.39 0.0862463 0.998019
refrig_cond_outlet 311.98815.0651 0.999869 185.16 0.639058 182.558 0.0017271 0
refrig_cehx_liquid_outlet295.20115.0651 0.999869 103.531 0.370375 101.049 0.00164758-0.001
refrig_exp_outlet 277.213 4.985710.999869 103.531 0.384338 94.32820.0184585 0.0676894
refrig_evap_outlet 278.602 4.985710.9998691273.75 4.58754 1147.98 0.252921 0.998
refrig_cehx_sat_vapor 284.063 4.985710.9998691290.14 4.64428 1160.96 0.259104 1
refrig_cehx_vapor_outlet 310.209 4.985710.9998691355.38 4.86333 1211.09 0.289409 1.001
rectifier_liquid 313.00915.0651 0.972268 169.756 0.662027 167.209 0.00169108 0
gen_vapor_formation 395.25315.0651 0.8940281639.65 5.11954 1461.18 0.118466 1
abs_vapor_final 336.506 4.985710.9744411446.2 5.1419 1288.13 0.317044 1

Performance variables

name unit value
Q_abs kW -197.396
Q_gen kW 207.383
Q_cond kW -123.3
Q_evap kW 130.087
Q_reflux kW -17.3245
Q_shx kW 64.0709
Q_cehx kW 9.07422
W_pump kW 0.550401
COP kW/kW 0.627282
ZeroCheck kW 2.52954e-05
ZeroCheckSHX kW 5.78258e-06
ZeroCheckCEHX kW -1.87975e-05
Q_gen_rect_combo kW 190.058
Q_refrig_side kW -6.78717
ZeroCheckRect kg/s 1.38778e-17
m_rich kg/s 0.354051
m_weak kg/s 0.242886
m_gen_vapor kg/s 0.113719
m_gen_reflux kg/s 0.00255368
m_refrig kg/s 0.111165
check_rectifier_delta_TK 37.5396
is_degenerate bool 0
In [122]:
P.constraint(x)
Out[122]:
[0.25405123492903559,
 0.64594876507096444,
 32.385666581057876,
 0.92065803869669482,
 21.337102338183058,
 81.244151889835393,
 84.313374171406906]
In [123]:
sys = system_aqua1.System(bdry, ch)
sys
Note: Absorber inlet is subcooled
C:\Users\nfette\Miniconda3\envs\openACHP\lib\site-packages\scipy\interpolate\_cubic.py:147: RuntimeWarning: divide by zero encountered in true_divide
  mk = (y[1:] - y[:-1]) / hk
C:\Users\nfette\Miniconda3\envs\openACHP\lib\site-packages\scipy\interpolate\_cubic.py:165: RuntimeWarning: invalid value encountered in true_divide
  whmean = (w1/mk[:-1] + w2/mk[1:]) / (w1 + w2)
Out[123]:
name deltaT epsilon UA Q
gen 0.163144 0.996723 78.2634 207.383
rect 7.88064 0.782762 1.34955 17.3245
abs 0.795894 0.959961 63.3603 197.396
cond 1.42142 0.967853 37.4399 123.3
evap 0.00472091 0.999394123.917 130.087
total 0 0 304.33 0
In [124]:
box = numpy.ones_like(x)
box[0] = 0.05
ss = numpy.linspace(-1,1,5)
yyy = numpy.zeros([len(box),len(ss)])
for i,boxi in enumerate(box):
    yy = numpy.zeros_like(ss)
    for j, s in enumerate(ss):
        x_vary = x.copy()
        x_vary[i] += boxi * s
        yyy[i,j] = P.objective(x_vary)
        if (numpy.array(P.constraint(x_vary)) < 0).any():
            yyy[i,j] = numpy.nan
[  3.04051235e-01   2.78602285e+02   3.11987952e+02   3.13008610e+02
   3.09939387e+02   3.95252762e+02]
Note: Absorber inlet is subcooled
C:\Users\nfette\Miniconda3\envs\openACHP\lib\site-packages\scipy\interpolate\_cubic.py:147: RuntimeWarning: divide by zero encountered in true_divide
  mk = (y[1:] - y[:-1]) / hk
C:\Users\nfette\Miniconda3\envs\openACHP\lib\site-packages\scipy\interpolate\_cubic.py:165: RuntimeWarning: invalid value encountered in true_divide
  whmean = (w1/mk[:-1] + w2/mk[1:]) / (w1 + w2)
209.0 29.0 111.715983459 5.04438564639e-294 0.0 

[  3.29051235e-01   2.78602285e+02   3.11987952e+02   3.13008610e+02
   3.09939387e+02   3.95252762e+02]
Note: Absorber inlet is subcooled
210.0 29.0 120.901605043 5.04438564639e-294 0.0 

[  3.54051235e-01   2.78602285e+02   3.11987952e+02   3.13008610e+02
   3.09939387e+02   3.95252762e+02]
Note: Absorber inlet is subcooled
211.0 30.0 130.087226628 0.0 0.225823506808 

[  3.79051235e-01   2.78602285e+02   3.11987952e+02   3.13008610e+02
   3.09939387e+02   3.95252762e+02]
Note: Absorber inlet is subcooled
212.0 30.0 139.272848213 0.0 91312.5800127 

[   0.40405123  278.60228502  311.98795161  313.00860964  309.93938736
  395.25276153]
Note: Absorber inlet is subcooled
213.0 30.0 148.458469798 0.0 382344.140072 

[  3.54051235e-01   2.77602285e+02   3.11987952e+02   3.13008610e+02
   3.09939387e+02   3.95252762e+02]
Note: Absorber inlet is subcooled
214.0 30.0 125.639385167 0.0 0.0 

[  3.54051235e-01   2.78102285e+02   3.11987952e+02   3.13008610e+02
   3.09939387e+02   3.95252762e+02]
Note: Absorber inlet is subcooled
215.0 30.0 127.856921936 0.0 0.0 

[  3.54051235e-01   2.78602285e+02   3.11987952e+02   3.13008610e+02
   3.09939387e+02   3.95252762e+02]
Note: Absorber inlet is subcooled
216.0 30.0 130.087226628 0.0 0.225823506808 

[  3.54051235e-01   2.79102285e+02   3.11987952e+02   3.13008610e+02
   3.09939387e+02   3.95252762e+02]
Note: Absorber inlet is subcooled
217.0 30.0 132.330795742 1.33397724377e-319 3292.04125993 

[  3.54051235e-01   2.79602285e+02   3.11987952e+02   3.13008610e+02
   3.09939387e+02   3.95252762e+02]
Note: Absorber inlet is subcooled
218.0 31.0 134.588138876 0.0 17656.5666321 

[  3.54051235e-01   2.78602285e+02   3.10987952e+02   3.13008610e+02
   3.09939387e+02   3.95252762e+02]
Note: Absorber inlet is subcooled
219.0 31.0 132.663846055 0.0 277.986858117 

[  3.54051235e-01   2.78602285e+02   3.11487952e+02   3.13008610e+02
   3.09939387e+02   3.95252762e+02]
Note: Absorber inlet is subcooled
C:\Users\nfette\Miniconda3\envs\openACHP\lib\site-packages\scipy\integrate\quadpack.py:364: IntegrationWarning: The maximum number of subdivisions (50) has been achieved.
  If increasing the limit yields no improvement it is advised to analyze 
  the integrand in order to determine the difficulties.  If the position of a 
  local difficulty can be determined (singularity, discontinuity) one will 
  probably gain from splitting up the interval and calling the integrator 
  on the subranges.  Perhaps a special-purpose integrator should be used.
  warnings.warn(msg, IntegrationWarning)
220.0 31.0 131.379717522 0.0 74.6268927787 

[  3.54051235e-01   2.78602285e+02   3.11987952e+02   3.13008610e+02
   3.09939387e+02   3.95252762e+02]
Note: Absorber inlet is subcooled
221.0 31.0 130.087226628 0.0 0.304829849622 

[  3.54051235e-01   2.78602285e+02   3.12487952e+02   3.13008610e+02
   3.09939387e+02   3.95252762e+02]
Note: Absorber inlet is subcooled
222.0 31.0 128.78623555 0.0 0.0 

[  3.54051235e-01   2.78602285e+02   3.12987952e+02   3.13008610e+02
   3.09939387e+02   3.95252762e+02]
Note: Absorber inlet is subcooled
223.0 31.0 127.476602663 0.0 9454794.92894 

[  3.54051235e-01   2.78602285e+02   3.11987952e+02   3.12008610e+02
   3.09939387e+02   3.95252762e+02]
Note: Absorber inlet is subcooled
224.0 31.0 130.087226628 0.0 10517865.4852 

[  3.54051235e-01   2.78602285e+02   3.11987952e+02   3.12508610e+02
   3.09939387e+02   3.95252762e+02]
Note: Absorber inlet is subcooled
225.0 32.0 130.087226628 0.0 0.411477257324 

[  3.54051235e-01   2.78602285e+02   3.11987952e+02   3.13008610e+02
   3.09939387e+02   3.95252762e+02]
Note: Absorber inlet is subcooled
226.0 32.0 130.087226628 0.0 0.411477257324 

[  3.54051235e-01   2.78602285e+02   3.11987952e+02   3.13508610e+02
   3.09939387e+02   3.95252762e+02]
Note: Absorber inlet is subcooled
227.0 32.0 130.087226628 0.0 0.411477257324 

[  3.54051235e-01   2.78602285e+02   3.11987952e+02   3.14008610e+02
   3.09939387e+02   3.95252762e+02]
Note: Absorber inlet is subcooled
228.0 32.0 130.087226628 0.0 0.411477257324 

[  3.54051235e-01   2.78602285e+02   3.11987952e+02   3.13008610e+02
   3.08939387e+02   3.95252762e+02]
Note: Absorber inlet is subcooled
229.0 32.0 134.127537546 0.0 2022.12107199 

[  3.54051235e-01   2.78602285e+02   3.11987952e+02   3.13008610e+02
   3.09439387e+02   3.95252762e+02]
Note: Absorber inlet is subcooled
230.0 32.0 132.098793535 0.0 232.982439684 

[  3.54051235e-01   2.78602285e+02   3.11987952e+02   3.13008610e+02
   3.09939387e+02   3.95252762e+02]
Note: Absorber inlet is subcooled
231.0 32.0 130.087226628 0.0 0.411477257324 

[  3.54051235e-01   2.78602285e+02   3.11987952e+02   3.13008610e+02
   3.10439387e+02   3.95252762e+02]
Note: Absorber inlet is subcooled
232.0 33.0 128.092331771 0.0 0.0 

[  3.54051235e-01   2.78602285e+02   3.11987952e+02   3.13008610e+02
   3.10939387e+02   3.95252762e+02]
Note: Absorber inlet is subcooled
233.0 33.0 126.11362588 0.0 0.0 

[  3.54051235e-01   2.78602285e+02   3.11987952e+02   3.13008610e+02
   3.09939387e+02   3.94252762e+02]
Note: Absorber inlet is subcooled
234.0 33.0 128.345359003 0.0 0.0 

[  3.54051235e-01   2.78602285e+02   3.11987952e+02   3.13008610e+02
   3.09939387e+02   3.94752762e+02]
Note: Absorber inlet is subcooled
235.0 33.0 129.219912195 0.0 0.0 

[  3.54051235e-01   2.78602285e+02   3.11987952e+02   3.13008610e+02
   3.09939387e+02   3.95252762e+02]
Note: Absorber inlet is subcooled
236.0 33.0 130.087226628 0.0 0.555436199917 

[  3.54051235e-01   2.78602285e+02   3.11987952e+02   3.13008610e+02
   3.09939387e+02   3.95752762e+02]
Note: Absorber inlet is subcooled
237.0 33.0 130.947401112 0.0 1003.50823437 

[  3.54051235e-01   2.78602285e+02   3.11987952e+02   3.13008610e+02
   3.09939387e+02   3.96252762e+02]
Note: Absorber inlet is subcooled
238.0 33.0 131.800531689 0.0 7052.36078554 

In [125]:
plt.close('all')
plt.figure(figsize=(10, 4))
ax1=plt.subplot(1,len(box), 1)
for i,_ in enumerate(box):
    ax=plt.subplot(1, len(box), i+1, sharey=ax1)
    xxx = x[i] + ss * box[i]
    plt.plot(xxx, yyy[i,:], '.-')
    plt.setp(ax.get_yticklabels(), visible=bool(i==0))
    plt.xlim(min(xxx),max(xxx))
plt.ylim(min(yyy.flat),min(yyy.flat)*0.9)
plt.show()

Now that we have a guess for the maximum cooling we might get, let's optimize the cost function instead, and ask for that much cooling using a constraint. I'm thinking that a penalty constraint might be good, so that we add this to the objective:

$$ P = \left(Q - Q_{goal}\right) ^ 2 $$
In [134]:
Q_opt = ch.Q_evap
Q_opt
Out[134]:
130.08722662820685
In [140]:
Q_goal = 0.9 * Q_opt
Q_goal
Out[140]:
117.07850396538616
In [170]:
class Problem_2_5_B:
    def __init__(self, bdry, Q_goal, mu=0.1):
        self.bdry = bdry
        self.Q_goal = Q_goal
        self.mu = mu
        self.Ncons = 7
        self.n_calls = 0
        # Soft constraints mode: this is sent to minimizer
        self.constraints = [{'type': 'ineq',
                             'fun': self.constraint,
                             'args': (i,)
                            } for i in range(self.Ncons)]
            
    def objective_raw(self, xC):
        try:
            ch = system_aqua1.makeChiller(xC)
            sys = system_aqua1.System(self.bdry, ch)
            UA = sys.totalUA
        except:
            UA = numpy.nan
        return UA

        
    def objective(self, xC):
        step_number = numpy.floor(self.n_calls / 7)
        self.n_calls += 1
        print(xC,flush=True)
        UA,B,P = 0.,0.,0.
        try:
            ch = system_aqua1.makeChiller(xC)
            sys = system_aqua1.System(self.bdry, ch)
            
            # Barriers
            # Magnitude tends to zero, slowly
            mu_B = 1000. * numpy.exp(-0.1 * step_number)
            length_scale_b = 1
            # Or ... magnitude fixed, but shape changes
            mu_B = 1000
            length_scale_b = 1 * numpy.exp(-0.1 * step_number)
            
            # These are zero at the boundary ...
            barriers = [ch.check_rectifier_delta_T] \
                       + [deltaT
                          for name, deltaT, epsilon, UA, Qhx in sys.data]
            B = mu_B * barrier1(barriers,length_scale_b)
            
            # Penalties
            # Magnitude tends to infinite
            mu_P = 1 * numpy.exp(0.3 * step_number)
            penalties = [ch.Q_evap - self.Q_goal]
            P = mu_P * penalty1(penalties,1)
            
            UA = sys.totalUA
        except:
            UA = numpy.inf
        
        print(self.n_calls, step_number, UA, B, P, "\n", flush=True)
        return UA + B + P
    
    def constraint(self, x, *args):
        cons = [x[0] - 0.1,
                1. - x[0],
                x[2] - x[1] - 1.0,
                x[3] - x[2] - 0.1,
                x[4] - x[1] - 10.0,
                x[5] - x[3] - 1.0,
                x[5] - x[4] - 1.0]
        if len(args) > 0:
            i, = args
            return cons[i]
        else:
            return cons

    def callback(self, x):
        print("Did an iteration at ", x)
In [171]:
P = Problem_2_5_B(bdry, Q_goal)
rT = T_heat_reject
x2 = x.copy()
In [159]:
P.n_calls = 7 * numpy.floor(P.n_calls / 7)
opt = scipy.optimize.minimize(P.objective, x2, method="COBYLA",
                              constraints=P.constraints, callback=P.callback,
                              options={'disp':True,'maxiter':100,'rhobeg':0.01})
opt
[  3.23139528e-01   2.78641275e+02   3.12291755e+02   3.13259021e+02
   3.10228713e+02   3.95250062e+02]
C:\Users\nfette\Miniconda3\envs\openACHP\lib\site-packages\scipy\optimize\_minimize.py:403: RuntimeWarning: Method COBYLA does not support callback.
  warn('Method %s does not support callback.' % method, RuntimeWarning)
Note: Absorber inlet is subcooled
C:\Users\nfette\Miniconda3\envs\openACHP\lib\site-packages\scipy\interpolate\_cubic.py:147: RuntimeWarning: divide by zero encountered in true_divide
  mk = (y[1:] - y[:-1]) / hk
C:\Users\nfette\Miniconda3\envs\openACHP\lib\site-packages\scipy\interpolate\_cubic.py:165: RuntimeWarning: invalid value encountered in true_divide
  whmean = (w1/mk[:-1] + w2/mk[1:]) / (w1 + w2)
176.0 25.0 138.19835208991435 0.118474468961 0.0 

[  3.33139528e-01   2.78641275e+02   3.12291755e+02   3.13259021e+02
   3.10228713e+02   3.95250062e+02]
Note: Absorber inlet is subcooled
177.0 25.0 153.19282242496664 1.65573909183 0.0 

[  3.23139528e-01   2.78651275e+02   3.12291755e+02   3.13259021e+02
   3.10228713e+02   3.95250062e+02]
Note: Absorber inlet is subcooled
178.0 25.0 138.537420450732 0.137721707638 0.0 

[  3.23139528e-01   2.78641275e+02   3.12301755e+02   3.13259021e+02
   3.10228713e+02   3.95250062e+02]
Note: Absorber inlet is subcooled
179.0 25.0 138.02564423858303 0.11642563615 0.0 

[  3.23139528e-01   2.78641275e+02   3.12301755e+02   3.13269021e+02
   3.10228713e+02   3.95250062e+02]
Note: Absorber inlet is subcooled
180.0 25.0 138.02541019578706 0.11642563615 0.0 

[  3.23139528e-01   2.78641275e+02   3.12301755e+02   3.13269021e+02
   3.10238713e+02   3.95250062e+02]
Note: Absorber inlet is subcooled
181.0 25.0 137.76089753239407 0.113325785726 1.90304608771 

[  3.23139528e-01   2.78641275e+02   3.12301755e+02   3.13269021e+02
   3.10228713e+02   3.95260062e+02]
Note: Absorber inlet is subcooled
182.0 25.0 138.11933928130702 0.117784938847 0.0 

[  3.13191132e-01   2.78641059e+02   3.12301860e+02   3.13269021e+02
   3.10227729e+02   3.95250004e+02]
Note: Absorber inlet is subcooled
183.0 26.0 126.25881782715973 0.00255257049659 31599.5423089 

[  3.28139528e-01   2.78641275e+02   3.12301755e+02   3.13269021e+02
   3.10228713e+02   3.95250062e+02]
Note: Absorber inlet is subcooled
184.0 26.0 144.99245977792415 0.192442355152 0.0 

[  3.23139500e-01   2.78638775e+02   3.12301755e+02   3.13269021e+02
   3.10228713e+02   3.95250062e+02]
Note: Absorber inlet is subcooled
185.0 26.0 137.9415595512446 0.0429795187076 0.0929791504778 

[  3.18174167e-01   2.78638684e+02   3.12301816e+02   3.13269021e+02
   3.10228137e+02   3.95250028e+02]
Note: Absorber inlet is subcooled
186.0 26.0 131.79251510559678 0.0101707660583 7939.0867178 

[  3.23170513e-01   2.78638775e+02   3.12304255e+02   3.13269021e+02
   3.10228713e+02   3.95250062e+02]
Note: Absorber inlet is subcooled
187.0 26.0 137.93913544693027 0.043161211038 0.00194016500195 

[  3.28170130e-01   2.78638775e+02   3.12304193e+02   3.13269021e+02
   3.10228713e+02   3.95250062e+02]
Note: Absorber inlet is subcooled
188.0 26.0 144.89640236352858 0.185329551922 0.0 

[  3.23170513e-01   2.78638775e+02   3.12304255e+02   3.13271521e+02
   3.10228713e+02   3.95250062e+02]
Note: Absorber inlet is subcooled
189.0 26.0 137.93907698114046 0.0431612105777 0.00194022274891 

[  3.18207977e-01   2.78638685e+02   3.12304447e+02   3.13271521e+02
   3.10228141e+02   3.95250028e+02]
Note: Absorber inlet is subcooled
190.0 27.0 131.78959358571416 0.00303699179993 10644.6754616 

[  3.23456625e-01   2.78638775e+02   3.12304251e+02   3.13271521e+02
   3.10226229e+02   3.95250062e+02]
Note: Absorber inlet is subcooled
191.0 27.0 138.38101250640327 0.0165576781242 0.0 

[  3.28137230e-01   2.78638775e+02   3.12304193e+02   3.13271521e+02
   3.10229286e+02   3.95250062e+02]
Note: Absorber inlet is subcooled
192.0 27.0 144.83136855962226 0.0741071005773 0.0 

[  3.23170502e-01   2.78638775e+02   3.12304255e+02   3.13271521e+02
   3.10228713e+02   3.95247562e+02]
Note: Absorber inlet is subcooled
193.0 27.0 137.91562184924953 0.0149248998166 0.0782271139283 

[  3.18175364e-01   2.78638683e+02   3.12304450e+02   3.13271521e+02
   3.10228734e+02   3.95250097e+02]
Note: Absorber inlet is subcooled
194.0 27.0 131.73679051802557 0.0029997222913 10809.3981417 

[  3.20672938e-01   2.78638729e+02   3.12304353e+02   3.13271521e+02
   3.10228724e+02   3.95250079e+02]
Note: Absorber inlet is subcooled
195.0 27.0 134.75616756934772 0.00669666290503 2705.03044583 

[  3.24412182e-01   2.78638775e+02   3.12304239e+02   3.13271521e+02
   3.10228856e+02   3.95250062e+02]
Note: Absorber inlet is subcooled
196.0 27.0 139.5841318766684 0.0223348953682 0.0 

[  3.23170491e-01   2.78638150e+02   3.12304255e+02   3.13271521e+02
   3.10228713e+02   3.95250062e+02]
Note: Absorber inlet is subcooled
197.0 28.0 137.91813679181814 0.00459398472166 0.052880816282 

[  3.21921731e-01   2.78638137e+02   3.12304306e+02   3.13271521e+02
   3.10228728e+02   3.95250071e+02]
Note: Absorber inlet is subcooled
198.0 28.0 136.3056217795911 0.00294356690507 924.912261782 

[  3.23196083e-01   2.78638150e+02   3.12304879e+02   3.13271521e+02
   3.10228715e+02   3.95250062e+02]
Note: Absorber inlet is subcooled
199.0 28.0 137.94083333831196 0.0046292303216 0.0 

[  3.24436786e-01   2.78638150e+02   3.12304828e+02   3.13271521e+02
   3.10228858e+02   3.95250062e+02]
Note: Absorber inlet is subcooled
200.0 28.0 139.58531636409097 0.00719901414724 0.0 

[  3.23124417e-01   2.78638150e+02   3.12304880e+02   3.13271521e+02
   3.10229336e+02   3.95250062e+02]
Note: Absorber inlet is subcooled
201.0 28.0 137.830647792373 0.00450248878287 2.54051460496 

[  3.22934256e-01   2.78638146e+02   3.12304909e+02   3.13271521e+02
   3.10227493e+02   3.95250065e+02]
Note: Absorber inlet is subcooled
202.0 28.0 137.63086512755058 0.00423500069504 33.0063063046 

[  3.23195896e-01   2.78638150e+02   3.12304879e+02   3.13271521e+02
   3.10228717e+02   3.95250687e+02]
Note: Absorber inlet is subcooled
203.0 28.0 137.94640932305342 0.00463342703171 0.0 

[  3.22994816e-01   2.78638149e+02   3.12304885e+02   3.13271521e+02
   3.10229949e+02   3.95250058e+02]
Note: Absorber inlet is subcooled
204.0 29.0 137.64522589915805 0.00116881441655 32.0825565008 

[  3.23196083e-01   2.78638150e+02   3.12304879e+02   3.13272146e+02
   3.10228715e+02   3.95250062e+02]
Note: Absorber inlet is subcooled
205.0 29.0 137.9408187875418 0.00127146745137 0.0 

[  3.23275337e-01   2.78638149e+02   3.12304882e+02   3.13272146e+02
   3.10227468e+02   3.95250064e+02]
Note: Absorber inlet is subcooled
206.0 29.0 138.07772782877868 0.0013182628598 0.0 

[  3.22573113e-01   2.78638144e+02   3.12304927e+02   3.13272149e+02
   3.10228727e+02   3.95250057e+02]
Note: Absorber inlet is subcooled
207.0 29.0 137.1297810912522 0.000994572490398 294.635677088 

[  3.23819297e-01   2.78638150e+02   3.12304854e+02   3.13272146e+02
   3.10228755e+02   3.95250062e+02]
Note: Absorber inlet is subcooled
208.0 29.0 138.76176955303305 0.00162495783917 0.0 

[  3.22884610e-01   2.78638147e+02   3.12304903e+02   3.13272147e+02
   3.10228721e+02   3.95250059e+02]
Note: Absorber inlet is subcooled
209.0 29.0 137.53387916997 0.00112453001743 70.8055021656 

[  3.23199180e-01   2.78638150e+02   3.12304878e+02   3.13272146e+02
   3.10228871e+02   3.95250061e+02]
Note: Absorber inlet is subcooled
210.0 29.0 137.94073692913108 0.00127223067623 0.0 

[  3.23511359e-01   2.78638150e+02   3.12304866e+02   3.13272146e+02
   3.10228865e+02   3.95250061e+02]
Note: Absorber inlet is subcooled
211.0 30.0 138.35119748488498 0.000349535840483 0.0 

[  3.23199165e-01   2.78637994e+02   3.12304878e+02   3.13272146e+02
   3.10228871e+02   3.95250061e+02]
Note: Absorber inlet is subcooled
212.0 30.0 137.9354895196941 0.000303922753886 0.0 

[  3.22887826e-01   2.78637984e+02   3.12304903e+02   3.13272147e+02
   3.10228878e+02   3.95250059e+02]
Note: Absorber inlet is subcooled
213.0 30.0 137.52854306959324 0.000265318931774 95.6938079625 

[  3.23211300e-01   2.78637994e+02   3.12305034e+02   3.13272146e+02
   3.10228872e+02   3.95250061e+02]
Note: Absorber inlet is subcooled
214.0 30.0 137.94868460909987 0.000305395400112 0.0 

[  3.23510653e-01   2.78637994e+02   3.12304854e+02   3.13272146e+02
   3.10228865e+02   3.95250061e+02]
Note: Absorber inlet is subcooled
215.0 30.0 138.34519547164257 0.000348082792236 0.0 

[  3.23199207e-01   2.78637994e+02   3.12304878e+02   3.13272146e+02
   3.10228871e+02   3.95249905e+02]
Note: Absorber inlet is subcooled
216.0 30.0 137.93408970605464 0.000303838083875 0.0 

[  3.22886952e-01   2.78637984e+02   3.12304883e+02   3.13272147e+02
   3.10228877e+02   3.95249903e+02]
Note: Absorber inlet is subcooled
217.0 30.0 137.52630767117532 0.0002651547836 96.6036498962 

[  3.23199848e-01   2.78637994e+02   3.12304878e+02   3.13272302e+02
   3.10228871e+02   3.95249905e+02]
Note: Absorber inlet is subcooled
218.0 31.0 137.93492603309292 6.27051179198e-05 0.0 

[  3.23510692e-01   2.78637994e+02   3.12304854e+02   3.13272145e+02
   3.10228865e+02   3.95249905e+02]
Note: Absorber inlet is subcooled
219.0 31.0 138.3437832789862 7.28263703382e-05 0.0 

[  3.23043078e-01   2.78637989e+02   3.12304881e+02   3.13272146e+02
   3.10228874e+02   3.95249904e+02]
Note: Absorber inlet is subcooled
220.0 31.0 137.7298352782445 5.81412976511e-05 30.2527135588 

[  3.23298881e-01   2.78637994e+02   3.12304871e+02   3.13272146e+02
   3.10228869e+02   3.95249905e+02]
Note: Absorber inlet is subcooled
221.0 31.0 138.0648716512608 6.57668283439e-05 0.0 

[  3.23200198e-01   2.78637994e+02   3.12304878e+02   3.13272146e+02
   3.10228921e+02   3.95249905e+02]
Note: Absorber inlet is subcooled
222.0 31.0 137.93406342834837 6.27004958696e-05 0.0 

[  3.23100284e-01   2.78637991e+02   3.12304880e+02   3.13272146e+02
   3.10228923e+02   3.95249904e+02]
Note: Absorber inlet is subcooled
223.0 31.0 137.80325649488984 5.9752197614e-05 11.2269654583 

[  3.23201711e-01   2.78637944e+02   3.12304878e+02   3.13272146e+02
   3.10228921e+02   3.95249905e+02]
Note: Absorber inlet is subcooled
224.0 31.0 137.93437475328474 6.26599586024e-05 0.0 

[  3.23299823e-01   2.78637997e+02   3.12304870e+02   3.13272146e+02
   3.10228919e+02   3.95249905e+02]
Note: Absorber inlet is subcooled
225.0 32.0 138.0648863875587 1.1555744577e-05 0.0 

[  3.23196277e-01   2.78637994e+02   3.12304828e+02   3.13272146e+02
   3.10228921e+02   3.95249905e+02]
Note: Absorber inlet is subcooled
226.0 32.0 137.92979221852897 1.09372746236e-05 0.0 

[  3.23096349e-01   2.78637991e+02   3.12304830e+02   3.13272146e+02
   3.10228923e+02   3.95249904e+02]
Note: Absorber inlet is subcooled
227.0 32.0 137.7989944411681 1.03703770245e-05 16.4138635888 

Out[159]:
     fun: 154.21286840036998
   maxcv: 0.0
 message: 'Optimization terminated successfully.'
    nfev: 52
  status: 1
 success: True
       x: array([  3.23096349e-01,   2.78637991e+02,   3.12304830e+02,
         3.13272146e+02,   3.10228923e+02,   3.95249904e+02])
In [182]:
x2 = opt.x
x2
Out[182]:
array([  3.23096349e-01,   2.78637991e+02,   3.12304830e+02,
         3.13272146e+02,   3.10228923e+02,   3.95249904e+02])
In [161]:
ch2 = system_aqua1.makeChiller(x2)
display(ch2)

State points

T P x h s u v Qu
rich_abs_outlet 310.229 4.992130.517237 -72.97910.394813 -73.59490.00123348 0
rich_pump_outlet 310.39415.1992 0.517237 -71.40560.395827 -73.27970.00123301-0.001
rich_shx_outlet 350.05515.1992 0.517237 110.147 0.946042 108.164 0.00130451-0.001
rich_gen_sat_liquid 351.20615.1992 0.517237 115.56 0.961482 113.573 0.00130711 0
weak_gen_outlet 395.25 15.1992 0.300604 347.435 1.53761 345.533 0.00125114 0
weak_shx_outlet 336.53615.1992 0.300604 84.39130.817873 82.63930.0011527 -0.001
weak_exp_outlet 336.713 4.992130.300604 84.39130.821369 83.81540.00115351-0.001
gen_vapor_outlet 351.20615.1992 0.9887561421.67 4.56463 1266.29 0.102232 1
gen_reflux_inlet 351.20615.1992 0.517235 115.557 0.961473 113.57 0.00130711 0
refrig_rect_outlet 313.27215.1992 0.9998691293.92 4.18118 1164.05 0.0854417 0.997783
refrig_cond_outlet 312.30515.1992 0.999869 186.732 0.644028 184.105 0.00172869 0
refrig_cehx_liquid_outlet295.39315.1992 0.999869 104.452 0.373417 101.946 0.00164842-0.001
refrig_exp_outlet 277.249 4.992130.999869 104.452 0.387603 95.17130.0185898 0.0682992
refrig_evap_outlet 278.638 4.992130.9998691273.79 4.58715 1148.02 0.252636 0.998
refrig_cehx_sat_vapor 284.09 4.992130.9998691290.15 4.64374 1160.97 0.258776 1
refrig_cehx_vapor_outlet 310.512 4.992130.9998691356.07 4.86496 1211.62 0.289356 1.001
rectifier_liquid 313.27215.1992 0.973847 172.236 0.666416 169.66 0.00169463 0
gen_vapor_formation 395.25 15.1992 0.8952381637.88 5.11174 1459.49 0.117368 1
abs_vapor_final 336.713 4.992130.9741551446.98 5.14349 1288.81 0.316831 1

Performance variables

name unit value
Q_abs kW -178.135
Q_gen kW 187.274
Q_cond kW -110.824
Q_evap kW 117.045
Q_reflux kW -15.8689
Q_shx kW 58.659
Q_cehx kW 8.23591
W_pump kW 0.508389
COP kW/kW 0.624994
ZeroCheck kW 2.9251e-05
ZeroCheckSHX kW 5.39052e-06
ZeroCheckCEHX kW -1.90222e-05
Q_gen_rect_combo kW 171.405
Q_refrig_side kW -6.22118
ZeroCheckRect kg/s 1.38778e-17
m_rich kg/s 0.323096
m_weak kg/s 0.223001
m_gen_vapor kg/s 0.102454
m_gen_reflux kg/s 0.00235899
m_refrig kg/s 0.100095
check_rectifier_delta_TK 37.9343
is_degenerate bool 0
In [162]:
P.constraint(x2)
Out[162]:
[0.22309634851689678,
 0.67690365148310327,
 32.666838464731313,
 0.86731647569055215,
 21.590932006864364,
 80.97775799049748,
 84.020980924054982]
In [163]:
sys2 = system_aqua1.System(bdry, ch2)
sys2
Note: Absorber inlet is subcooled
C:\Users\nfette\Miniconda3\envs\openACHP\lib\site-packages\scipy\interpolate\_cubic.py:147: RuntimeWarning: divide by zero encountered in true_divide
  mk = (y[1:] - y[:-1]) / hk
C:\Users\nfette\Miniconda3\envs\openACHP\lib\site-packages\scipy\interpolate\_cubic.py:165: RuntimeWarning: invalid value encountered in true_divide
  whmean = (w1/mk[:-1] + w2/mk[1:]) / (w1 + w2)
Out[163]:
name deltaT epsilon UA Q
gen 4.75025 0.92018 28.5516 187.274
rect 8.15244 0.782637 1.12489 15.8689
abs 1.1977 0.940514 42.4473 178.135
cond 2.31615 0.96672 26.5502 110.824
evap 0.749386 0.983799 39.125 117.045
total 0 0 137.799 0
In [183]:
box = numpy.ones_like(x2)
box[0] = 0.05
ss = numpy.linspace(-1,1,5)
yyy = numpy.zeros([len(box),len(ss)])
for i,boxi in enumerate(box):
    yy = numpy.zeros_like(ss)
    for j, s in enumerate(ss):
        x_vary = x2.copy()
        x_vary[i] += boxi * s
        yyy[i,j] = P.objective_raw(x_vary)
        if (numpy.array(P.constraint(x_vary)) < 0).any():
            yyy[i,j] = numpy.nan
Note: Absorber inlet is subcooled
C:\Users\nfette\Miniconda3\envs\openACHP\lib\site-packages\scipy\interpolate\_cubic.py:147: RuntimeWarning: divide by zero encountered in true_divide
  mk = (y[1:] - y[:-1]) / hk
C:\Users\nfette\Miniconda3\envs\openACHP\lib\site-packages\scipy\interpolate\_cubic.py:165: RuntimeWarning: invalid value encountered in true_divide
  whmean = (w1/mk[:-1] + w2/mk[1:]) / (w1 + w2)
Note: Absorber inlet is subcooled
Note: Absorber inlet is subcooled
Note: Absorber inlet is subcooled
C:\Users\nfette\Miniconda3\envs\openACHP\lib\site-packages\scipy\integrate\quadpack.py:364: IntegrationWarning: The maximum number of subdivisions (50) has been achieved.
  If increasing the limit yields no improvement it is advised to analyze 
  the integrand in order to determine the difficulties.  If the position of a 
  local difficulty can be determined (singularity, discontinuity) one will 
  probably gain from splitting up the interval and calling the integrator 
  on the subranges.  Perhaps a special-purpose integrator should be used.
  warnings.warn(msg, IntegrationWarning)
Note: Absorber inlet is subcooled
Note: Absorber inlet is subcooled
Note: Absorber inlet is subcooled
Note: Absorber inlet is subcooled
Note: Absorber inlet is subcooled
Note: Absorber inlet is subcooled
Note: Absorber inlet is subcooled
Note: Absorber inlet is subcooled
Note: Absorber inlet is subcooled
Note: Absorber inlet is subcooled
Note: Absorber inlet is subcooled
Note: Absorber inlet is subcooled
Note: Absorber inlet is subcooled
Note: Absorber inlet is subcooled
Note: Absorber inlet is subcooled
Note: Absorber inlet is subcooled
Note: Absorber inlet is subcooled
Note: Absorber inlet is subcooled
Note: Absorber inlet is subcooled
Note: Absorber inlet is subcooled
C:\Users\nfette\Miniconda3\envs\openACHP\lib\site-packages\scipy\integrate\quadpack.py:364: IntegrationWarning: The occurrence of roundoff error is detected, which prevents 
  the requested tolerance from being achieved.  The error may be 
  underestimated.
  warnings.warn(msg, IntegrationWarning)
Note: Absorber inlet is subcooled
Note: Absorber inlet is subcooled
Note: Absorber inlet is subcooled
Note: Absorber inlet is subcooled
Note: Absorber inlet is subcooled
Note: Absorber inlet is subcooled
In [184]:
yyy
Out[184]:
array([[  92.85015493,  111.63476306,  137.79899444,  191.02442712,
                  inf],
       [ 116.37203437,  125.02773452,  137.79899444,  170.21816617,
                  inf],
       [ 160.89854932,  147.57478175,  137.79899444,  130.09642707,
                  nan],
       [          nan,  137.81416839,  137.79899444,  137.78949491,
         137.78336925],
       [ 209.06261669,  155.59632572,  137.79899444,  126.91870377,
         119.04386542],
       [ 129.11684491,  133.29755777,  137.79899444,  142.67851534,
         148.01130393]])
In [187]:
plt.close('all')
plt.figure(figsize=(10, 4))
ax1=plt.subplot(1,len(box), 1)
for i,_ in enumerate(box):
    ax=plt.subplot(1, len(box), i+1, sharey=ax1)
    xxx = x2[i] + ss * box[i]
    plt.plot(xxx, yyy[i,:], 'o-')
    plt.setp(ax.get_yticklabels(), visible=bool(i==0))
    plt.xlim(min(xxx),max(xxx))
#plt.ylim(min(yyy.flat)*0.9,min(yyy.flat))
plt.show()

The sensitivity plot could go further by overlaying the constraint functions. Additionally, it would be nice to find the eigenvectors of the objective function and trace in those directions, indicating how far it is to the next constraint. But that is icing on the cake, so meanwhile I will continue with this approach in a new notebook and generate some parametric study data.

Scratch work

In [15]:
a=5
for i in range(9999):
    a*=0.5
    if a==0:
        break
i,a
Out[15]:
(1076, 0.0)
In [25]:
gradient_step = numpy.array([1.,2.,3.,4.])
for j, _ in enumerate(gradient_step):
    for i in range(2):
        gradient_step[j] *= 0.5
gradient_step
Out[25]:
array([ 0.25,  0.5 ,  0.75,  1.  ])
In [30]:
numpy.diag(gradient_step)[3]
Out[30]:
array([ 0.,  0.,  0.,  1.])
In [7]:
%%html
<pre id="TOC-markdown">TOC will be here</pre>
<script>
$("#TOC-markdown").html(
    $('h1,h2,h3,h4').filter(":has(a)").map(function(){return "  ".repeat($(this).prop("tagName")[1]) + "- ["+$(this).text()+"](" + $(this).children().attr("href") + ")";}).get().join("\n")
    );
</script>
TOC will be here
In [ ]: