Trial 2.3 - Use inputs that are more physical

The idea here is to do something different with the heat exchange feasibility constraints. In previous trials, we have written a solver that inputs temperatures to solve the chiller cycle; then heat exchange feasibility is achieved by handing various constraints to the optimizer.

Here we want to solve a system with UA inputs. One method to accomplish this can be easily implemented by using the existing code and a numerical solver as follows. With a guess for T inputs, we solve the system and for each heat exchanger, we can build the (T,q) curves on both hot and cold sides. Then we evaluate Q resulting from the given UA and compare it to Q determined by the chiller cycle. The vector of [disparity in Q for each heat exchanger] will be minimized by varying T. However, it should be noted that the numerical solver adds a computational burden, and furthermore my existing method to computate Q given UA also uses a numerical solve. So in short, although the choice of inputs may remove the need for some temperature constraints, it would yield a slow simulation.

In [1]:
%load_ext autoreload
%autoreload 2
In [2]:
import matplotlib
import numpy
from IPython.display import HTML, SVG
In [3]:
#matplotlib.use('svg')
import matplotlib.pyplot as plt
plt.rcParams['svg.fonttype'] = 'none'
plt.rcParams['font.sans-serif'] = 'Arial'
In [4]:
from io import BytesIO
def pltsvg():
    imgdata = BytesIO()
    plt.savefig(imgdata)
    imgdata.seek(0)
    display(SVG(data=imgdata.read()))
In [5]:
import ammonia1
import system_aqua1
import scipy.optimize
In [6]:
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)
In [7]:
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 [49]:
type(numpy.sum([1,2,3]))
Out[49]:
numpy.int32
In [50]:
class Problem_2_3:
    def __init__(self, bdry, UAs):
        """Note: UAs to input are like this:
            [gen,rect,abs,cond,evap]
        """
        self.bdry = bdry
        self.UAs = UAs
        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
        cost,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.1 * step_number)
        penalties = []
        P = mu_P * penalty1(penalties,1)

        Q = numpy.zeros_like(sys.Q)
        Q_excess = numpy.zeros_like(sys.Q)
        for i, (name, hx) in enumerate(sys.hxs.items()):
            Q[i] = hx.calcQ(self.UAs[i])
            Q_excess[i] = Q - sys.Q[name]
        print(Q)
        cost = numpy.sum(numpy.square(Q_excess))
        #except Exception as e:
        #    print(e)
        #    cost = numpy.inf
        
        print(self.n_calls, step_number, cost, B, P, "\n", flush=True)
        return cost + 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 [51]:
# [gen,rect,abs,cond,evap]
P = Problem_2_3(bdry, [30,1,30,30,30])
In [52]:
rT = T_heat_reject
x = numpy.array([0.5, 278.45, rT+7, rT+8, rT+5, 395.15])
In [53]:
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':100,'rhobeg':0.1})
opt
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)
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)
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
~\Miniconda3\envs\openachp\lib\site-packages\scipy\optimize\slsqp.py in _minimize_slsqp(func, x0, args, jac, bounds, constraints, maxiter, ftol, iprint, disp, eps, callback, **unknown_options)
    367             try:
--> 368                 fx = float(np.asarray(func(x)))
    369             except:

~\Miniconda3\envs\openachp\lib\site-packages\scipy\optimize\optimize.py in function_wrapper(*wrapper_args)
    291         ncalls[0] += 1
--> 292         return function(*(wrapper_args + args))
    293 

~\Documents\GitHub\openACHP\src\HRHX_integral_model.py in <lambda>(Q)
    241     def calcQ(self,UA):
--> 242         func = lambda Q:(self.calcUA(Q)-UA)**2
    243         constraints = [{"type":"ineq",

~\Documents\GitHub\openACHP\src\HRHX_integral_model.py in calcUA(self, Q, eff)
    235             raise ValueError("Q given [{}] is higher than Q maximum [{}];"\
--> 236             " effectiveness [{}] > 1.".format(Q,self.Qmax,epsilon))
    237         if eff:

ValueError: Q given [[ 207.97481375]] is higher than Q maximum [207.97481374133434]; effectiveness [[ 1.]] > 1.

During handling of the above exception, another exception occurred:

ValueError                                Traceback (most recent call last)
<ipython-input-53-6f8785abde7a> in <module>()
      2 opt = scipy.optimize.minimize(P.objective, x, method="COBYLA",
      3                               constraints=P.constraints, callback=P.callback,
----> 4                               options={'disp':True,'maxiter':100,'rhobeg':0.1})
      5 opt

~\Miniconda3\envs\openachp\lib\site-packages\scipy\optimize\_minimize.py in minimize(fun, x0, args, method, jac, hess, hessp, bounds, constraints, tol, callback, options)
    453                              **options)
    454     elif meth == 'cobyla':
--> 455         return _minimize_cobyla(fun, x0, args, constraints, **options)
    456     elif meth == 'slsqp':
    457         return _minimize_slsqp(fun, x0, args, jac, bounds,

~\Miniconda3\envs\openachp\lib\site-packages\scipy\optimize\cobyla.py in _minimize_cobyla(fun, x0, args, constraints, rhobeg, tol, iprint, maxiter, disp, catol, **unknown_options)
    256     xopt, info = _cobyla.minimize(calcfc, m=m, x=np.copy(x0), rhobeg=rhobeg,
    257                                   rhoend=rhoend, iprint=iprint, maxfun=maxfun,
--> 258                                   dinfo=info)
    259 
    260     if info[3] > catol:

~\Miniconda3\envs\openachp\lib\site-packages\scipy\optimize\cobyla.py in calcfc(x, con)
    246 
    247     def calcfc(x, con):
--> 248         f = fun(x, *args)
    249         i = 0
    250         for size, c in izip(cons_lengths, constraints):

<ipython-input-50-535d6cde612e> in objective(self, xC)
     43         Q_excess = numpy.zeros_like(sys.Q)
     44         for i, (name, hx) in enumerate(sys.hxs.items()):
---> 45             Q[i] = hx.calcQ(self.UAs[i])
     46             Q_excess[i] = Q - sys.Q[name]
     47         print(Q)

~\Documents\GitHub\openACHP\src\HRHX_integral_model.py in calcQ(self, UA)
    245                         {"type":"ineq",
    246                        "fun":lambda Q: self.Qmax-Q}]
--> 247         return scipy.optimize.minimize(func,0,constraints=constraints).x[0]
    248     def calcQmax(self,extra=False,brute=False):
    249         # Preliminary Max Q based on inlet temperatures only

~\Miniconda3\envs\openachp\lib\site-packages\scipy\optimize\_minimize.py in minimize(fun, x0, args, method, jac, hess, hessp, bounds, constraints, tol, callback, options)
    456     elif meth == 'slsqp':
    457         return _minimize_slsqp(fun, x0, args, jac, bounds,
--> 458                                constraints, callback=callback, **options)
    459     elif meth == 'dogleg':
    460         return _minimize_dogleg(fun, x0, args, jac, hess,

~\Miniconda3\envs\openachp\lib\site-packages\scipy\optimize\slsqp.py in _minimize_slsqp(func, x0, args, jac, bounds, constraints, maxiter, ftol, iprint, disp, eps, callback, **unknown_options)
    368                 fx = float(np.asarray(func(x)))
    369             except:
--> 370                 raise ValueError("Objective function must return a scalar")
    371             # Compute the constraints
    372             if cons['eq']:

ValueError: Objective function must return a scalar
In [33]:
x = opt.x
x
Out[33]:
array([   0.5 ,  278.45,  312.  ,  313.  ,  310.  ,  395.15])
In [34]:
ch = system_aqua1.makeChiller(x)
display(ch)
P.constraint(x)

State points

T P x h s u v Qu
rich_abs_outlet 310 4.958420.517331 -74.00790.391488 -74.61940.00123323 0
rich_pump_outlet 310.16315.0702 0.517331 -72.44940.392494 -74.30720.00123277-0.001
rich_shx_outlet 349.79115.0702 0.517331 108.915 0.942551 106.95 0.001304 -0.001
rich_gen_sat_liquid 350.84815.0702 0.517331 114.24 0.957881 112.272 0.00130605 0
weak_gen_outlet 395.15 15.0702 0.299303 347.389 1.53679 345.505 0.00125006 0
weak_shx_outlet 336.34115.0702 0.299303 84.07740.816023 82.34150.00115187-0.001
weak_exp_outlet 336.516 4.958420.299303 84.07730.819486 83.50580.00115267-0.001
gen_vapor_outlet 350.84815.0702 0.9888561421.16 4.56687 1265.85 0.103057 1
gen_reflux_inlet 350.84815.0702 0.517197 113.862 0.956699 111.894 0.00130628 0
refrig_rect_outlet 313 15.0702 0.9998691294.15 4.18526 1164.24 0.0861989 0.997922
refrig_cond_outlet 312 15.0702 0.999869 185.219 0.639247 182.616 0.00172716 0
refrig_cehx_liquid_outlet295.14115.0702 0.999869 103.248 0.369414 100.766 0.00164732-0.001
refrig_exp_outlet 277.061 4.958420.999869 103.248 0.383493 94.01390.0186233 0.0679941
refrig_evap_outlet 278.45 4.958420.9998691273.57 4.5892 1147.85 0.25414 0.998
refrig_cehx_sat_vapor 283.822 4.958420.9998691276.98 4.6001 1150.82 0.254442 1
refrig_cehx_vapor_outlet 310.213 4.958420.9998691355.55 4.86642 1211.21 0.291086 1.001
rectifier_liquid 313 15.0702 0.972851 170.147 0.662002 167.597 0.00169189 0
gen_vapor_formation 395.15 15.0702 0.8945041638.85 5.11764 1460.43 0.118394 1
abs_vapor_final 336.516 4.958420.9742161446.6 5.14559 1288.5 0.318856 1

Performance variables

name unit value
Q_abs kW -276.894
Q_gen kW 291.075
Q_cond kW -172.559
Q_evap kW 182.113
Q_reflux kW -24.5144
Q_shx kW 90.6821
Q_cehx kW 12.7554
W_pump kW 0.779238
COP kW/kW 0.625657
ZeroCheck kW 3.99258e-05
ZeroCheckSHX kW 8.14384e-06
ZeroCheckCEHX kW -2.72807e-05
Q_gen_rect_combo kW 266.561
Q_refrig_side kW -9.55446
ZeroCheckRect kg/s 0
m_rich kg/s 0.5
m_weak kg/s 0.344391
m_gen_vapor kg/s 0.159242
m_gen_reflux kg/s 0.00363327
m_refrig kg/s 0.155609
check_rectifier_delta_TK 37.8481
is_degenerate bool 0
Out[34]:
[0.40000000000000002,
 0.5,
 32.550000000000011,
 0.90000000000000002,
 21.550000000000011,
 81.149999999999977,
 84.149999999999977]
In [35]:
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[35]:
name deltaT epsilon UA Q
gen -19.885 1.39957 inf 291.075
rect 7.43783 0.852975 3.02295 24.5144
abs 0.717942 0.968517180.552 276.894
cond -0.871111 1.11792 inf 172.559
evap -2.95602 1.37236 inf 182.113
total 0 0 inf 0

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 [ ]: