diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 642b1a5..7c7b737 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -14,4 +14,5 @@ jobs: options: "-S -C --check --diff" - name: Spell Check uses: crate-ci/typos@master - + with: + config: ./.github/workflows/typos.toml diff --git a/.github/workflows/typos.toml b/.github/workflows/typos.toml new file mode 100644 index 0000000..a7e28f0 --- /dev/null +++ b/.github/workflows/typos.toml @@ -0,0 +1,5 @@ +[default.extend-words] +# Ignore HDA +hda = "hda" +HDA = "HDA" +equil = "equil" diff --git a/gdplib/biofuel/model.py b/gdplib/biofuel/model.py index a568c6c..84820f4 100644 --- a/gdplib/biofuel/model.py +++ b/gdplib/biofuel/model.py @@ -44,7 +44,7 @@ def build_model(): Returns ------- Pyomo.ConcreteModel - The Pyomo concrete model which descibes the multiperiod location-allocation optimization model designed to determine the most cost-effective network layout and production allocation to meet market demands. + The Pyomo concrete model which describes the multiperiod location-allocation optimization model designed to determine the most cost-effective network layout and production allocation to meet market demands. References ---------- @@ -72,7 +72,7 @@ def discount_factor(m, t): Parameters ---------- m : Pyomo.ConcreteModel - Pyomo concrete model which descibes the multiperiod location-allocation optimization model + Pyomo concrete model which describes the multiperiod location-allocation optimization model t : int Index of time in months from 0 to 120 (10 years) @@ -97,7 +97,7 @@ def market_demand(m, mkt, t): Parameters ---------- m : Pyomo.ConcreteModel - Pyomo concrete model which descibes the multiperiod location-allocation optimization model + Pyomo concrete model which describes the multiperiod location-allocation optimization model mkt : int Index of the market from 1 to 10 t : int @@ -121,7 +121,7 @@ def available_supply(m, sup, t): Parameters ---------- m : Pyomo.ConcreteModel - Pyomo concrete model which descibes the multiperiod location-allocation optimization model + Pyomo concrete model which describes the multiperiod location-allocation optimization model sup : int Index of the supplier from 1 to 10 t : int @@ -149,7 +149,7 @@ def supplier_x(m, sup): Parameters ---------- m : Pyomo.ConcreteModel - Pyomo concrete model which descibes the multiperiod location-allocation optimization model + Pyomo concrete model which describes the multiperiod location-allocation optimization model sup : int Index of the supplier from 1 to 10 @@ -168,7 +168,7 @@ def supplier_y(m, sup): Parameters ---------- m : Pyomo.ConcreteModel - Pyomo concrete model which descibes the multiperiod location-allocation optimization model + Pyomo concrete model which describes the multiperiod location-allocation optimization model sup : int Index of the supplier from 1 to 10 @@ -187,7 +187,7 @@ def market_x(m, mkt): Parameters ---------- m : Pyomo.ConcreteModel - Pyomo concrete model which descibes the multiperiod location-allocation optimization model + Pyomo concrete model which describes the multiperiod location-allocation optimization model mkt : int Index of the market from 1 to 10 @@ -206,7 +206,7 @@ def market_y(m, mkt): Parameters ---------- m : Pyomo.ConcreteModel - Pyomo concrete model which descibes the multiperiod location-allocation optimization model + Pyomo concrete model which describes the multiperiod location-allocation optimization model mkt : int Index of the market from 1 to 10 @@ -225,7 +225,7 @@ def site_x(m, site): Parameters ---------- m : Pyomo.ConcreteModel - Pyomo concrete model which descibes the multiperiod location-allocation optimization model + Pyomo concrete model which describes the multiperiod location-allocation optimization model site : int Index of the facility site from 1 to 12 @@ -244,7 +244,7 @@ def site_y(m, site): Parameters ---------- m : Pyomo.ConcreteModel - Pyomo concrete model which descibes the multiperiod location-allocation optimization model + Pyomo concrete model which describes the multiperiod location-allocation optimization model site : int Index of the facility site from 1 to 12 @@ -263,7 +263,7 @@ def dist_supplier_to_site(m, sup, site): Parameters ---------- m : Pyomo.ConcreteModel - Pyomo concrete model which descibes the multiperiod location-allocation optimization model + Pyomo concrete model which describes the multiperiod location-allocation optimization model sup : int Index of the supplier from 1 to 10 site : int @@ -287,7 +287,7 @@ def dist_site_to_market(m, site, mkt): Parameters ---------- m : Pyomo.ConcreteModel - Pyomo concrete model which descibes the multiperiod location-allocation optimization model + Pyomo concrete model which describes the multiperiod location-allocation optimization model site : int Index of the facility site from 1 to 12 mkt : int @@ -362,7 +362,7 @@ def raw_material_unit_cost(m, sup, t): Parameters ---------- m : Pyomo.ConcreteModel - Pyomo concrete model which descibes the multiperiod location-allocation optimization model + Pyomo concrete model which describes the multiperiod location-allocation optimization model sup : int Index of the supplier from 1 to 10 t : int @@ -383,7 +383,7 @@ def module_unit_cost(m, t): Parameters ---------- m : Pyomo.ConcreteModel - Pyomo concrete model which descibes the multiperiod location-allocation optimization model + Pyomo concrete model which describes the multiperiod location-allocation optimization model t : int Index of time in months from 0 to 120 (10 years) @@ -402,7 +402,7 @@ def unit_production_cost(m, t): Parameters ---------- m : Pyomo.ConcreteModel - Pyomo concrete model which descibes the multiperiod location-allocation optimization model + Pyomo concrete model which describes the multiperiod location-allocation optimization model t : int Index of time in months from 0 to 120 (10 years) @@ -421,7 +421,7 @@ def transport_fixed_cost(m): Parameters ---------- m : Pyomo.ConcreteModel - Pyomo concrete model which descibes the multiperiod location-allocation optimization model + Pyomo concrete model which describes the multiperiod location-allocation optimization model Returns ------- @@ -438,7 +438,7 @@ def unit_product_transport_cost(m, t): Parameters ---------- m : Pyomo.ConcreteModel - Pyomo concrete model which descibes the multiperiod location-allocation optimization model + Pyomo concrete model which describes the multiperiod location-allocation optimization model t : int Index of time in months from 0 to 120 (10 years) @@ -457,7 +457,7 @@ def unit_raw_material_transport_cost(m, t): Parameters ---------- m : Pyomo.ConcreteModel - Pyomo concrete model which descibes the multiperiod location-allocation optimization model + Pyomo concrete model which describes the multiperiod location-allocation optimization model t : int Index of time in months from 0 to 120 (10 years) @@ -493,7 +493,7 @@ def supply_limits(m, sup, t): Parameters ---------- m : Pyomo.ConcreteModel - Pyomo concrete model which descibes the multiperiod location-allocation optimization model + Pyomo concrete model which describes the multiperiod location-allocation optimization model sup : int Index of the supplier from 1 to 10 t : int @@ -517,7 +517,7 @@ def demand_satisfaction(m, mkt, t): Parameters ---------- m : Pyomo.ConcreteModel - Pyomo concrete model which descibes the multiperiod location-allocation optimization model + Pyomo concrete model which describes the multiperiod location-allocation optimization model mkt : int Index of the market from 1 to 10 t : int @@ -541,7 +541,7 @@ def product_balance(m, site, t): Parameters ---------- m : Pyomo.ConcreteModel - Pyomo concrete model which descibes the multiperiod location-allocation optimization model + Pyomo concrete model which describes the multiperiod location-allocation optimization model site : int Index of the facility site from 1 to 12 t : int @@ -564,7 +564,7 @@ def require_raw_materials(m, site, t): Parameters ---------- m : Pyomo.ConcreteModel - Pyomo concrete model which descibes the multiperiod location-allocation optimization model + Pyomo concrete model which describes the multiperiod location-allocation optimization model site : int Index of the facility site from 1 to 12 t : int @@ -625,7 +625,7 @@ def site_type(m, site): Parameters ---------- m : Pyomo.ConcreteModel - Pyomo concrete model which descibes the multiperiod location-allocation optimization model + Pyomo concrete model which describes the multiperiod location-allocation optimization model site : int Index of the facility site from 1 to 12 @@ -644,7 +644,7 @@ def supply_route_active_or_not(m, sup, site): Parameters ---------- m : Pyomo.ConcreteModel - Pyomo concrete model which descibes the multiperiod location-allocation optimization model + Pyomo concrete model which describes the multiperiod location-allocation optimization model sup : int Index of the supplier from 1 to 10 site : int @@ -665,7 +665,7 @@ def product_route_active_or_not(m, site, mkt): Parameters ---------- m : Pyomo.ConcreteModel - Pyomo concrete model which descibes the multiperiod location-allocation optimization model + Pyomo concrete model which describes the multiperiod location-allocation optimization model site : int Index of the facility site from 1 to 12 mkt : int @@ -686,7 +686,7 @@ def raw_material_transport_cost(m, sup, site): Parameters ---------- m : Pyomo.ConcreteModel - Pyomo concrete model which descibes the multiperiod location-allocation optimization model + Pyomo concrete model which describes the multiperiod location-allocation optimization model sup : int _description_ site : int @@ -713,7 +713,7 @@ def raw_material_fixed_transport_cost(m): Parameters ---------- m : Pyomo.ConcreteModel - Pyomo concrete model which descibes the multiperiod location-allocation optimization model + Pyomo concrete model which describes the multiperiod location-allocation optimization model Returns ------- @@ -738,7 +738,7 @@ def product_transport_cost(m, site, mkt): Parameters ---------- m : Pyomo.ConcreteModel - Pyomo concrete model which descibes the multiperiod location-allocation optimization model + Pyomo concrete model which describes the multiperiod location-allocation optimization model site : int Index of the facility site from 1 to 12 mkt : int @@ -765,7 +765,7 @@ def product_fixed_transport_cost(m): Parameters ---------- m : Pyomo.ConcreteModel - Pyomo concrete model which descibes the multiperiod location-allocation optimization model + Pyomo concrete model which describes the multiperiod location-allocation optimization model Returns ------- @@ -792,7 +792,7 @@ def module_setup_cost(m, site, t): Parameters ---------- m : Pyomo.ConcreteModel - Pyomo concrete model which descibes the multiperiod location-allocation optimization model + Pyomo concrete model which describes the multiperiod location-allocation optimization model site : int Index of the facility site from 1 to 12 t : int @@ -817,7 +817,7 @@ def module_teardown_credit(m, site, t): Parameters ---------- m : Pyomo.ConcreteModel - Pyomo concrete model which descibes the multiperiod location-allocation optimization model + Pyomo concrete model which describes the multiperiod location-allocation optimization model site : int Index of the facility site from 1 to 12 t : int @@ -838,7 +838,7 @@ def conv_salvage_value(m, site): Parameters ---------- m : Pyomo.ConcreteModel - Pyomo concrete model which descibes the multiperiod location-allocation optimization model + Pyomo concrete model which describes the multiperiod location-allocation optimization model site : int Index of the facility site from 1 to 12 diff --git a/gdplib/gdp_col/column.py b/gdplib/gdp_col/column.py index 762a95a..16ee9ef 100644 --- a/gdplib/gdp_col/column.py +++ b/gdplib/gdp_col/column.py @@ -325,7 +325,7 @@ def monotonoic_temperature(_, t): m.gamma = Var( m.comps, m.trays, - doc='liquid activity coefficent of component on tray', + doc='liquid activity coefficient of component on tray', domain=NonNegativeReals, bounds=(0, 10), initialize=1, diff --git a/gdplib/gdp_col/fenske.py b/gdplib/gdp_col/fenske.py index 10e9813..d35aa8a 100644 --- a/gdplib/gdp_col/fenske.py +++ b/gdplib/gdp_col/fenske.py @@ -50,7 +50,7 @@ def calculate_Fenske(xD, xB): m.gamma = Var( m.comps, m.trays, - doc='liquid activity coefficent of component on tray', + doc='liquid activity coefficient of component on tray', domain=NonNegativeReals, bounds=(0, 10), initialize=1, diff --git a/gdplib/gdp_col/initialize.py b/gdplib/gdp_col/initialize.py index 2ae7c44..a5b0140 100644 --- a/gdplib/gdp_col/initialize.py +++ b/gdplib/gdp_col/initialize.py @@ -1,4 +1,5 @@ """Initialization routine for distillation column""" + from __future__ import division import pandas @@ -173,12 +174,7 @@ def set_value_if_not_fixed(var, val): m.Pvap[c, t].set_value( value( exp( - ( - k['A'] * x - + k['B'] * x**1.5 - + k['C'] * x**3 - + k['D'] * x**6 - ) + (k['A'] * x + k['B'] * x**1.5 + k['C'] * x**3 + k['D'] * x**6) / (1 - x) ) * k['Pc'] diff --git a/gdplib/hda/HDA_GDP_gdpopt.py b/gdplib/hda/HDA_GDP_gdpopt.py index ef9565b..cf817b1 100644 --- a/gdplib/hda/HDA_GDP_gdpopt.py +++ b/gdplib/hda/HDA_GDP_gdpopt.py @@ -18,12 +18,11 @@ def HDA_model(): # ## scalars - m.alpha = Param(initialize=0.3665, doc="compressor coefficient") - m.compeff = Param(initialize=0.750, doc="compressor effiency") + m.compeff = Param(initialize=0.750, doc="compressor efficiency") m.gam = Param(initialize=1.300, doc="ratio of cp to cv") m.abseff = Param(initialize=0.333, doc="absorber tray efficiency") - m.disteff = Param(initialize=0.5000, doc="column tray effiency") + m.disteff = Param(initialize=0.5000, doc="column tray efficiency") m.uflow = Param(initialize=50, doc="upper bound - flow logicals") m.upress = Param(initialize=4.0, doc="upper bound - pressure logicals") m.utemp = Param(initialize=7.0, doc="upper bound - temperature logicals") @@ -35,7 +34,6 @@ def HDA_model(): # ## sets - def strset(i): s = [] i = 1 @@ -47,9 +45,11 @@ def strset(i): s.append(i) i += i return s + m.str = Set(initialize=strset, doc="process streams") - m.compon = Set(initialize=['h2', 'ch4', 'ben', - 'tol', 'dip'], doc="chemical components") + m.compon = Set( + initialize=['h2', 'ch4', 'ben', 'tol', 'dip'], doc="chemical components" + ) m.abs = RangeSet(1) m.comp = RangeSet(4) m.dist = RangeSet(3) @@ -67,22 +67,28 @@ def strset(i): m.spl = RangeSet(3) m.valve = RangeSet(6) m.str2 = Set(initialize=strset, doc="process streams") - m.compon2 = Set(initialize=['h2', 'ch4', 'ben', - 'tol', 'dip'], doc="chemical components") + m.compon2 = Set( + initialize=['h2', 'ch4', 'ben', 'tol', 'dip'], doc="chemical components" + ) # parameters Heatvap = {} Heatvap['tol'] = 30890.00 - m.heatvap = Param(m.compon, initialize=Heatvap, default=0, - doc='heat of vaporization (kj per kg-mol)') + m.heatvap = Param( + m.compon, + initialize=Heatvap, + default=0, + doc='heat of vaporization (kj per kg-mol)', + ) Cppure = {} Cppure['h2'] = 30 Cppure['ch4'] = 40 Cppure['ben'] = 225 Cppure['tol'] = 225 Cppure['dip'] = 450 - m.cppure = Param(m.compon, initialize=Cppure, default=0, - doc='pure component heat capacities') + m.cppure = Param( + m.compon, initialize=Cppure, default=0, doc='pure component heat capacities' + ) Gcomp = {} Gcomp[7, 'h2'] = 0.95 Gcomp[7, 'ch4'] = 0.05 @@ -183,13 +189,16 @@ def strset(i): Gcomp[71, 'tol'] = 0.10 Gcomp[72, 'h2'] = 0.50 Gcomp[72, 'ch4'] = 0.50 - m.gcomp = Param(m.str, m.compon, initialize=Gcomp, - default=0, doc='guess composition values') + m.gcomp = Param( + m.str, m.compon, initialize=Gcomp, default=0, doc='guess composition values' + ) def cppara(compon, stream): return sum(m.cppure[compon] * m.gcomp[stream, compon] for compon in m.compon) - m.cp = Param(m.str, initialize=cppara, default=0, - doc='heat capacities ( kj per kgmole-k)') + + m.cp = Param( + m.str, initialize=cppara, default=0, doc='heat capacities ( kj per kgmole-k)' + ) Anta = {} Anta['h2'] = 13.6333 @@ -197,24 +206,21 @@ def cppara(compon, stream): Anta['ben'] = 15.9008 Anta['tol'] = 16.0137 Anta['dip'] = 16.6832 - m.anta = Param(m.compon, initialize=Anta, - default=0, doc='antoine coefficient') + m.anta = Param(m.compon, initialize=Anta, default=0, doc='antoine coefficient') Antb = {} Antb['h2'] = 164.9 Antb['ch4'] = 897.84 Antb['ben'] = 2788.51 Antb['tol'] = 3096.52 Antb['dip'] = 4602.23 - m.antb = Param(m.compon, initialize=Antb, - default=0, doc='antoine coefficient') + m.antb = Param(m.compon, initialize=Antb, default=0, doc='antoine coefficient') Antc = {} Antc['h2'] = 3.19 Antc['ch4'] = -7.16 Antc['ben'] = -52.36 Antc['tol'] = -53.67 Antc['dip'] = -70.42 - m.antc = Param(m.compon, initialize=Antc, - default=0, doc='antoine coefficient') + m.antc = Param(m.compon, initialize=Antc, default=0, doc='antoine coefficient') Perm = {} for i in m.compon: Perm[i] = 0 @@ -222,282 +228,540 @@ def cppara(compon, stream): Perm['ch4'] = 2.3e-06 def Permset(m, compon): - return Perm[compon] * (1. / 22400.) * 1.0e4 * 750.062 * 60. / 1000. - m.perm = Param(m.compon, initialize=Permset, - default=0, doc='permeability ') + return Perm[compon] * (1.0 / 22400.0) * 1.0e4 * 750.062 * 60.0 / 1000.0 + + m.perm = Param(m.compon, initialize=Permset, default=0, doc='permeability ') Cbeta = {} Cbeta['h2'] = 1.0003 Cbeta['ch4'] = 1.0008 - Cbeta['dip'] = 1.0e+04 - m.cbeta = Param(m.compon, initialize=Cbeta, default=0, - doc='constant values (exp(beta)) in absorber') + Cbeta['dip'] = 1.0e04 + m.cbeta = Param( + m.compon, + initialize=Cbeta, + default=0, + doc='constant values (exp(beta)) in absorber', + ) Aabs = {} Aabs['ben'] = 1.4 Aabs['tol'] = 4.0 - m.aabs = Param(m.compon, initialize=Aabs, - default=0, doc=' absorption factors') + m.aabs = Param(m.compon, initialize=Aabs, default=0, doc=' absorption factors') m.eps1 = Param(initialize=1e-4, doc='small number to avoid div. by zero') Heatrxn = {} - Heatrxn[1] = 50100. - Heatrxn[2] = 50100. - m.heatrxn = Param(m.rct, initialize=Heatrxn, default=0, - doc='heat of reaction (kj per kg-mol)') + Heatrxn[1] = 50100.0 + Heatrxn[2] = 50100.0 + m.heatrxn = Param( + m.rct, initialize=Heatrxn, default=0, doc='heat of reaction (kj per kg-mol)' + ) F1comp = {} F1comp['h2'] = 0.95 F1comp['ch4'] = 0.05 F1comp['dip'] = 0.00 F1comp['ben'] = 0.00 F1comp['tol'] = 0.00 - m.f1comp = Param(m.compon, initialize=F1comp, default=0, - doc='feedstock compositions (h2 feed)') + m.f1comp = Param( + m.compon, initialize=F1comp, default=0, doc='feedstock compositions (h2 feed)' + ) F66comp = {} F66comp['tol'] = 1.0 F66comp['h2'] = 0.00 F66comp['ch4'] = 0.00 F66comp['dip'] = 0.00 F66comp['ben'] = 0.00 - m.f66comp = Param(m.compon, initialize=F66comp, default=0, - doc='feedstock compositions (tol feed)') + m.f66comp = Param( + m.compon, + initialize=F66comp, + default=0, + doc='feedstock compositions (tol feed)', + ) F67comp = {} F67comp['tol'] = 1.0 F67comp['h2'] = 0.00 F67comp['ch4'] = 0.00 F67comp['dip'] = 0.00 F67comp['ben'] = 0.00 - m.f67comp = Param(m.compon, initialize=F67comp, default=0, - doc='feedstock compositions (tol feed)') + m.f67comp = Param( + m.compon, + initialize=F67comp, + default=0, + doc='feedstock compositions (tol feed)', + ) # # matching streams - m.ilabs = Set(initialize=[(1, 67)], - doc="abs-stream (inlet liquid) matches") - m.olabs = Set(initialize=[(1, 68)], - doc="abs-stream (outlet liquid) matches") - m.ivabs = Set(initialize=[(1, 63)], - doc=" abs-stream (inlet vapor) matches ") - m.ovabs = Set(initialize=[(1, 64)], - doc="abs-stream (outlet vapor) matches") + m.ilabs = Set(initialize=[(1, 67)], doc="abs-stream (inlet liquid) matches") + m.olabs = Set(initialize=[(1, 68)], doc="abs-stream (outlet liquid) matches") + m.ivabs = Set(initialize=[(1, 63)], doc=" abs-stream (inlet vapor) matches ") + m.ovabs = Set(initialize=[(1, 64)], doc="abs-stream (outlet vapor) matches") m.asolv = Set(initialize=[(1, 'tol')], doc="abs-solvent component matches") - m.anorm = Set(initialize=[(1, 'ben')], - doc="abs-comp matches (normal model)") - m.asimp = Set(initialize=[(1, 'h2'), (1, 'ch4'), - (1, 'dip')], doc="abs-heavy component matches") - - m.icomp = Set(initialize=[(1, 5), (2, 59), (3, 64), - (4, 56)], doc="compressor-stream (inlet) matches") - m.ocomp = Set(initialize=[(1, 6), (2, 60), (3, 65), - (4, 57)], doc=" compressor-stream (outlet) matches") - - m.idist = Set(initialize=[(1, 25), (2, 30), (3, 33)], - doc="dist-stream (inlet) matches") - m.vdist = Set(initialize=[(1, 26), (2, 31), (3, 34)], - doc="dist-stream (vapor) matches") - m.ldist = Set(initialize=[(1, 27), (2, 32), (3, 35)], - doc="dist-stream (liquid) matches") - m.dl = Set(initialize=[(1, 'h2'), (2, 'ch4'), - (3, 'ben')], doc="dist-light components matches") - m.dlkey = Set(initialize=[(1, 'ch4'), (2, 'ben'), - (3, 'tol')], doc="dist-heavy key component matches") - m.dhkey = Set(initialize=[(1, 'ben'), (2, 'tol'), - (3, 'dip')], doc="dist-heavy components matches ") - m.dh = Set(initialize=[(1, 'tol'), (1, 'dip'), - (2, 'dip')], doc="dist-key component matches") + m.anorm = Set(initialize=[(1, 'ben')], doc="abs-comp matches (normal model)") + m.asimp = Set( + initialize=[(1, 'h2'), (1, 'ch4'), (1, 'dip')], + doc="abs-heavy component matches", + ) + + m.icomp = Set( + initialize=[(1, 5), (2, 59), (3, 64), (4, 56)], + doc="compressor-stream (inlet) matches", + ) + m.ocomp = Set( + initialize=[(1, 6), (2, 60), (3, 65), (4, 57)], + doc=" compressor-stream (outlet) matches", + ) + + m.idist = Set( + initialize=[(1, 25), (2, 30), (3, 33)], doc="dist-stream (inlet) matches" + ) + m.vdist = Set( + initialize=[(1, 26), (2, 31), (3, 34)], doc="dist-stream (vapor) matches" + ) + m.ldist = Set( + initialize=[(1, 27), (2, 32), (3, 35)], doc="dist-stream (liquid) matches" + ) + m.dl = Set( + initialize=[(1, 'h2'), (2, 'ch4'), (3, 'ben')], + doc="dist-light components matches", + ) + m.dlkey = Set( + initialize=[(1, 'ch4'), (2, 'ben'), (3, 'tol')], + doc="dist-heavy key component matches", + ) + m.dhkey = Set( + initialize=[(1, 'ben'), (2, 'tol'), (3, 'dip')], + doc="dist-heavy components matches ", + ) + m.dh = Set( + initialize=[(1, 'tol'), (1, 'dip'), (2, 'dip')], + doc="dist-key component matches", + ) i = list(m.dlkey) q = list(m.dhkey) dkeyset = i + q m.dkey = Set(initialize=dkeyset, doc='dist-key component matches') - m.iflsh = Set(initialize=[(1, 17), (2, 46), (3, 39)], - doc="flsh-stream (inlet) matches") - m.vflsh = Set(initialize=[(1, 18), (2, 47), (3, 40)], - doc="flsh-stream (vapor) matches") - m.lflsh = Set(initialize=[(1, 19), (2, 48), (3, 41)], - doc="flsh-stream (liquid) matches") - m.fkey = Set(initialize=[(1, 'ch4'), (2, 'ch4'), - (3, 'tol')], doc="flash-key component matches") + m.iflsh = Set( + initialize=[(1, 17), (2, 46), (3, 39)], doc="flsh-stream (inlet) matches" + ) + m.vflsh = Set( + initialize=[(1, 18), (2, 47), (3, 40)], doc="flsh-stream (vapor) matches" + ) + m.lflsh = Set( + initialize=[(1, 19), (2, 48), (3, 41)], doc="flsh-stream (liquid) matches" + ) + m.fkey = Set( + initialize=[(1, 'ch4'), (2, 'ch4'), (3, 'tol')], + doc="flash-key component matches", + ) m.ifurn = Set(initialize=[(1, 70)], doc="furn-stream (inlet) matches") m.ofurn = Set(initialize=[(1, 9)], doc="furn-stream (outlet) matches") - m.ihec = Set(initialize=[(1, 71), (2, 45)], - doc="hec-stream (inlet) matches") - m.ohec = Set(initialize=[(1, 17), (2, 46)], - doc="hec-stream (outlet) matches") - - m.iheh = Set(initialize=[(1, 24), (2, 23), (3, 37), - (4, 61)], doc="heh-stream (inlet) matches") - m.oheh = Set(initialize=[(1, 25), (2, 44), (3, 38), - (4, 73)], doc="heh-stream (outlet) matches") - - m.icexch = Set(initialize=[(1, 8)], - doc="exch-cold stream (inlet) matches") - m.ocexch = Set(initialize=[(1, 70)], - doc="exch-cold stream (outlet) matches") - m.ihexch = Set(initialize=[(1, 16)], - doc="exch-hot stream (inlet) matches") - m.ohexch = Set(initialize=[(1, 71)], - doc="exch-hot stream (outlet) matches") - - m.imemb = Set(initialize=[(1, 3), (2, 54)], - doc="memb-stream (inlet) matches") - m.nmemb = Set(initialize=[(1, 4), (2, 55)], - doc=" memb-stream (non-permeate) matches") - m.pmemb = Set(initialize=[(1, 5), (2, 56)], - doc="memb-stream (permeate) matches") - m.mnorm = Set(initialize=[(1, 'h2'), (1, 'ch4'), - (2, 'h2'), (2, 'ch4')], doc="normal components ") - m.msimp = Set(initialize=[(1, 'ben'), (1, 'tol'), (1, 'dip'), (2, 'ben'), - (2, 'tol'), (2, 'dip')], doc="simplified flux components ") - - m.imxr1 = Set(initialize=[(1, 2), (1, 6), (2, 11), (2, 13), (3, 27), (3, 48), ( - 4, 34), (4, 40), (5, 49), (5, 50)], doc="mixer-stream (inlet) matches") - m.omxr1 = Set(initialize=[(1, 7), (2, 14), (3, 30), (4, 42), - (5, 51)], doc=" mixer-stream (outlet) matches") - m.mxr1spl1 = Set(initialize=[(1, 2, 2), (1, 6, 3), (2, 11, 10), (2, 13, 12), (3, 27, 24), (3, 48, 23), ( - 4, 34, 33), (4, 40, 37), (5, 49, 23), (5, 50, 24)], doc="1-mxr-inlet 1-spl-outlet matches") - - m.imxr = Set(initialize=[(1, 7), (1, 43), (1, 66), (1, 72), (2, 15), (2, 20), (3, 21), ( - 3, 69), (4, 51), (4, 62), (5, 57), (5, 60), (5, 65)], doc="mixer-stream (inlet) matches") - m.omxr = Set(initialize=[(1, 8), (2, 16), (3, 22), (4, 63), - (5, 72)], doc=" mixer-stream (outlet) matches ") - - m.ipump = Set(initialize=[(1, 42), (2, 68)], - doc="pump-stream (inlet) matches") - m.opump = Set(initialize=[(1, 43), (2, 69)], - doc="pump-stream (outlet) matches") - - m.irct = Set(initialize=[(1, 10), (2, 12)], - doc="reactor-stream (inlet) matches") - m.orct = Set(initialize=[(1, 11), (2, 13)], - doc="reactor-stream (outlet) matches") - m.rkey = Set(initialize=[(1, 'tol'), (2, 'tol')], - doc="reactor-key component matches") - - m.ispl1 = Set(initialize=[(1, 1), (2, 9), (3, 22), (4, 32), - (5, 52), (6, 58)], doc="splitter-stream (inlet) matches ") - m.ospl1 = Set(initialize=[(1, 2), (1, 3), (2, 10), (2, 12), (3, 23), (3, 24), (4, 33), ( - 4, 37), (5, 53), (5, 54), (6, 59), (6, 61)], doc="splitter-stream (outlet) matches") - - m.ispl = Set(initialize=[(1, 19), (2, 18), (3, 26)], - doc="splitter-stream (inlet) matches") - m.ospl = Set(initialize=[(1, 20), (1, 21), (2, 52), (2, 58), - (3, 28), (3, 29)], doc="splitter-stream (outlet) matches") - - m.ival = Set(initialize=[(1, 44), (2, 38), (3, 14), (4, 47), - (5, 29), (6, 73)], doc="exp.valve-stream (inlet) matches") - m.oval = Set(initialize=[(1, 45), (2, 39), (3, 15), (4, 49), - (5, 50), (6, 62)], doc="exp.valve-stream (outlet) matches") + m.ihec = Set(initialize=[(1, 71), (2, 45)], doc="hec-stream (inlet) matches") + m.ohec = Set(initialize=[(1, 17), (2, 46)], doc="hec-stream (outlet) matches") + + m.iheh = Set( + initialize=[(1, 24), (2, 23), (3, 37), (4, 61)], + doc="heh-stream (inlet) matches", + ) + m.oheh = Set( + initialize=[(1, 25), (2, 44), (3, 38), (4, 73)], + doc="heh-stream (outlet) matches", + ) + + m.icexch = Set(initialize=[(1, 8)], doc="exch-cold stream (inlet) matches") + m.ocexch = Set(initialize=[(1, 70)], doc="exch-cold stream (outlet) matches") + m.ihexch = Set(initialize=[(1, 16)], doc="exch-hot stream (inlet) matches") + m.ohexch = Set(initialize=[(1, 71)], doc="exch-hot stream (outlet) matches") + + m.imemb = Set(initialize=[(1, 3), (2, 54)], doc="memb-stream (inlet) matches") + m.nmemb = Set( + initialize=[(1, 4), (2, 55)], doc=" memb-stream (non-permeate) matches" + ) + m.pmemb = Set(initialize=[(1, 5), (2, 56)], doc="memb-stream (permeate) matches") + m.mnorm = Set( + initialize=[(1, 'h2'), (1, 'ch4'), (2, 'h2'), (2, 'ch4')], + doc="normal components ", + ) + m.msimp = Set( + initialize=[ + (1, 'ben'), + (1, 'tol'), + (1, 'dip'), + (2, 'ben'), + (2, 'tol'), + (2, 'dip'), + ], + doc="simplified flux components ", + ) + + m.imxr1 = Set( + initialize=[ + (1, 2), + (1, 6), + (2, 11), + (2, 13), + (3, 27), + (3, 48), + (4, 34), + (4, 40), + (5, 49), + (5, 50), + ], + doc="mixer-stream (inlet) matches", + ) + m.omxr1 = Set( + initialize=[(1, 7), (2, 14), (3, 30), (4, 42), (5, 51)], + doc=" mixer-stream (outlet) matches", + ) + m.mxr1spl1 = Set( + initialize=[ + (1, 2, 2), + (1, 6, 3), + (2, 11, 10), + (2, 13, 12), + (3, 27, 24), + (3, 48, 23), + (4, 34, 33), + (4, 40, 37), + (5, 49, 23), + (5, 50, 24), + ], + doc="1-mxr-inlet 1-spl-outlet matches", + ) + + m.imxr = Set( + initialize=[ + (1, 7), + (1, 43), + (1, 66), + (1, 72), + (2, 15), + (2, 20), + (3, 21), + (3, 69), + (4, 51), + (4, 62), + (5, 57), + (5, 60), + (5, 65), + ], + doc="mixer-stream (inlet) matches", + ) + m.omxr = Set( + initialize=[(1, 8), (2, 16), (3, 22), (4, 63), (5, 72)], + doc=" mixer-stream (outlet) matches ", + ) + + m.ipump = Set(initialize=[(1, 42), (2, 68)], doc="pump-stream (inlet) matches") + m.opump = Set(initialize=[(1, 43), (2, 69)], doc="pump-stream (outlet) matches") + + m.irct = Set(initialize=[(1, 10), (2, 12)], doc="reactor-stream (inlet) matches") + m.orct = Set(initialize=[(1, 11), (2, 13)], doc="reactor-stream (outlet) matches") + m.rkey = Set( + initialize=[(1, 'tol'), (2, 'tol')], doc="reactor-key component matches" + ) + + m.ispl1 = Set( + initialize=[(1, 1), (2, 9), (3, 22), (4, 32), (5, 52), (6, 58)], + doc="splitter-stream (inlet) matches ", + ) + m.ospl1 = Set( + initialize=[ + (1, 2), + (1, 3), + (2, 10), + (2, 12), + (3, 23), + (3, 24), + (4, 33), + (4, 37), + (5, 53), + (5, 54), + (6, 59), + (6, 61), + ], + doc="splitter-stream (outlet) matches", + ) + + m.ispl = Set( + initialize=[(1, 19), (2, 18), (3, 26)], doc="splitter-stream (inlet) matches" + ) + m.ospl = Set( + initialize=[(1, 20), (1, 21), (2, 52), (2, 58), (3, 28), (3, 29)], + doc="splitter-stream (outlet) matches", + ) + + m.ival = Set( + initialize=[(1, 44), (2, 38), (3, 14), (4, 47), (5, 29), (6, 73)], + doc="exp.valve-stream (inlet) matches", + ) + m.oval = Set( + initialize=[(1, 45), (2, 39), (3, 15), (4, 49), (5, 50), (6, 62)], + doc="exp.valve-stream (outlet) matches", + ) # variables # absorber - m.nabs = Var(m.abs, within=NonNegativeReals, bounds=(0, 40), - initialize=1, doc='number of absorber trays') + m.nabs = Var( + m.abs, + within=NonNegativeReals, + bounds=(0, 40), + initialize=1, + doc='number of absorber trays', + ) m.gamma = Var(m.abs, m.compon, within=Reals, initialize=1) m.beta = Var(m.abs, m.compon, within=Reals, initialize=1) # compressor - m.elec = Var(m.comp, within=NonNegativeReals, bounds=(0, 100), - initialize=1, doc='electricity requirement (kw)') - m.presrat = Var(m.comp, within=NonNegativeReals, bounds=( - 1, 8/3), initialize=1, doc='ratio of outlet to inlet pressure') + m.elec = Var( + m.comp, + within=NonNegativeReals, + bounds=(0, 100), + initialize=1, + doc='electricity requirement (kw)', + ) + m.presrat = Var( + m.comp, + within=NonNegativeReals, + bounds=(1, 8 / 3), + initialize=1, + doc='ratio of outlet to inlet pressure', + ) # distillation m.nmin = Var(m.dist, within=NonNegativeReals, initialize=1) - m.ndist = Var(m.dist, within=NonNegativeReals, - initialize=1, doc='number of trays in column') - m.rmin = Var(m.dist, within=NonNegativeReals, - initialize=1, doc='minimum reflux ratio') - m.reflux = Var(m.dist, within=NonNegativeReals, - initialize=1, doc='reflux ratio') - m.distp = Var(m.dist, within=NonNegativeReals, initialize=1, - bounds=(0.1, 4.0), doc='column pressure') - m.avevlt = Var(m.dist, within=NonNegativeReals, - initialize=1, doc='average volatility') + m.ndist = Var( + m.dist, within=NonNegativeReals, initialize=1, doc='number of trays in column' + ) + m.rmin = Var( + m.dist, within=NonNegativeReals, initialize=1, doc='minimum reflux ratio' + ) + m.reflux = Var(m.dist, within=NonNegativeReals, initialize=1, doc='reflux ratio') + m.distp = Var( + m.dist, + within=NonNegativeReals, + initialize=1, + bounds=(0.1, 4.0), + doc='column pressure', + ) + m.avevlt = Var( + m.dist, within=NonNegativeReals, initialize=1, doc='average volatility' + ) # flash - m.flsht = Var(m.flsh, within=NonNegativeReals, initialize=1, - doc='flash temperature (100 k)') - m.flshp = Var(m.flsh, within=NonNegativeReals, initialize=1, - doc='flash pressure (mega-pascal)') - m.eflsh = Var(m.flsh, m.compon, within=NonNegativeReals, bounds=( - 0, 1), initialize=0.5, doc='vapor phase recovery in flash') + m.flsht = Var( + m.flsh, within=NonNegativeReals, initialize=1, doc='flash temperature (100 k)' + ) + m.flshp = Var( + m.flsh, + within=NonNegativeReals, + initialize=1, + doc='flash pressure (mega-pascal)', + ) + m.eflsh = Var( + m.flsh, + m.compon, + within=NonNegativeReals, + bounds=(0, 1), + initialize=0.5, + doc='vapor phase recovery in flash', + ) # furnace - m.qfuel = Var(m.furn, within=NonNegativeReals, bounds=( - None, 10), initialize=1, doc='heating requied (1.e+12 kj per yr)') + m.qfuel = Var( + m.furn, + within=NonNegativeReals, + bounds=(None, 10), + initialize=1, + doc='heating required (1.e+12 kj per yr)', + ) # cooler - m.qc = Var(m.hec, within=NonNegativeReals, bounds=(None, 10), - initialize=1, doc='utility requirement (1.e+12 kj per yr)') + m.qc = Var( + m.hec, + within=NonNegativeReals, + bounds=(None, 10), + initialize=1, + doc='utility requirement (1.e+12 kj per yr)', + ) # heater - m.qh = Var(m.heh, within=NonNegativeReals, bounds=(None, 10), - initialize=1, doc='utility requirement (1.e+12 kj per yr)') + m.qh = Var( + m.heh, + within=NonNegativeReals, + bounds=(None, 10), + initialize=1, + doc='utility requirement (1.e+12 kj per yr)', + ) # exchanger - m.qexch = Var(m.exch, within=NonNegativeReals, bounds=( - None, 10), initialize=1, doc='heat exchanged (1.e+12 kj per yr)') + m.qexch = Var( + m.exch, + within=NonNegativeReals, + bounds=(None, 10), + initialize=1, + doc='heat exchanged (1.e+12 kj per yr)', + ) # membrane - m.a = Var(m.memb, within=NonNegativeReals, bounds=(100, 10000), - initialize=1, doc='surface area for mass transfer ( m**2 )') + m.a = Var( + m.memb, + within=NonNegativeReals, + bounds=(100, 10000), + initialize=1, + doc='surface area for mass transfer ( m**2 )', + ) # mixer(1 input) - m.mxr1p = Var(m.mxr1, within=NonNegativeReals, bounds=( - 0.1, 4), initialize=0, doc='mixer temperature (100 k)') - m.mxr1t = Var(m.mxr1, within=NonNegativeReals, bounds=(3, 10), - initialize=0, doc='mixer pressure (m-pa)') + m.mxr1p = Var( + m.mxr1, + within=NonNegativeReals, + bounds=(0.1, 4), + initialize=0, + doc='mixer temperature (100 k)', + ) + m.mxr1t = Var( + m.mxr1, + within=NonNegativeReals, + bounds=(3, 10), + initialize=0, + doc='mixer pressure (m-pa)', + ) # mixer - m.mxrt = Var(m.mxr, within=NonNegativeReals, bounds=(3.0, 10), - initialize=3, doc='mixer temperature (100 k)') # ? - m.mxrp = Var(m.mxr, within=NonNegativeReals, bounds=(0.1, 4.0), - initialize=3, doc='mixer pressure (m-pa)') + m.mxrt = Var( + m.mxr, + within=NonNegativeReals, + bounds=(3.0, 10), + initialize=3, + doc='mixer temperature (100 k)', + ) # ? + m.mxrp = Var( + m.mxr, + within=NonNegativeReals, + bounds=(0.1, 4.0), + initialize=3, + doc='mixer pressure (m-pa)', + ) # reactor - m.rctt = Var(m.rct, within=NonNegativeReals, bounds=( - 8.9427, 9.7760), doc='reactor temperature (100 k)') - m.rctp = Var(m.rct, within=NonNegativeReals, bounds=( - 3.4474, 3.4474), doc=' reactor pressure (m-pa)') - m.rctvol = Var(m.rct, within=NonNegativeReals, bounds=( - None, 200), doc='reactor volume (cubic meter)') - m.krct = Var(m.rct, within=NonNegativeReals, initialize=1, - bounds=(0.0123471, 0.149543), doc='rate constant') - m.conv = Var(m.rct, m.compon, within=NonNegativeReals, bounds=( - None, 0.973), doc='conversion of key component') - m.sel = Var(m.rct, within=NonNegativeReals, bounds=( - None, 0.9964), doc='selectivity to benzene') - m.consum = Var(m.rct, m.compon, within=NonNegativeReals, bounds=( - 0, 10000000000), initialize=0, doc='consumption rate of key') - m.q = Var(m.rct, within=NonNegativeReals, bounds=( - 0, 10000000000), doc='heat removed (1.e+9 kj per yr)') + m.rctt = Var( + m.rct, + within=NonNegativeReals, + bounds=(8.9427, 9.7760), + doc='reactor temperature (100 k)', + ) + m.rctp = Var( + m.rct, + within=NonNegativeReals, + bounds=(3.4474, 3.4474), + doc=' reactor pressure (m-pa)', + ) + m.rctvol = Var( + m.rct, + within=NonNegativeReals, + bounds=(None, 200), + doc='reactor volume (cubic meter)', + ) + m.krct = Var( + m.rct, + within=NonNegativeReals, + initialize=1, + bounds=(0.0123471, 0.149543), + doc='rate constant', + ) + m.conv = Var( + m.rct, + m.compon, + within=NonNegativeReals, + bounds=(None, 0.973), + doc='conversion of key component', + ) + m.sel = Var( + m.rct, + within=NonNegativeReals, + bounds=(None, 0.9964), + doc='selectivity to benzene', + ) + m.consum = Var( + m.rct, + m.compon, + within=NonNegativeReals, + bounds=(0, 10000000000), + initialize=0, + doc='consumption rate of key', + ) + m.q = Var( + m.rct, + within=NonNegativeReals, + bounds=(0, 10000000000), + doc='heat removed (1.e+9 kj per yr)', + ) # splitter (1 output) - m.spl1t = Var(m.spl1, within=PositiveReals, bounds=( - 3.00, 10.00), doc='splitter temperature (100 k)') - m.spl1p = Var(m.spl1, within=PositiveReals, bounds=( - 0.1, 4.0), doc='splitter pressure (m-pa)') + m.spl1t = Var( + m.spl1, + within=PositiveReals, + bounds=(3.00, 10.00), + doc='splitter temperature (100 k)', + ) + m.spl1p = Var( + m.spl1, + within=PositiveReals, + bounds=(0.1, 4.0), + doc='splitter pressure (m-pa)', + ) # splitter - m.splp = Var(m.spl, within=Reals, bounds=(0.1, 4.0), - doc='splitter pressure (m-pa)') - m.splt = Var(m.spl, within=Reals, bounds=(3.0, 10.0), - doc='splitter temperature (100 k)') + m.splp = Var( + m.spl, within=Reals, bounds=(0.1, 4.0), doc='splitter pressure (m-pa)' + ) + m.splt = Var( + m.spl, within=Reals, bounds=(3.0, 10.0), doc='splitter temperature (100 k)' + ) # stream def bound_f(m, stream): if stream in range(8, 19): return (0, 50) elif stream in [52, 54, 56, 57, 58, 59, 60, 70, 71, 72]: - return(0, 50) + return (0, 50) else: return (0, 10) - m.f = Var(m.str, within=NonNegativeReals, bounds=bound_f, - initialize=1, doc='stream flowrates (kg-mole per min)') + + m.f = Var( + m.str, + within=NonNegativeReals, + bounds=bound_f, + initialize=1, + doc='stream flowrates (kg-mole per min)', + ) def bound_fc(m, stream, compon): if stream in range(8, 19) or stream in [52, 54, 56, 57, 58, 59, 60, 70, 71, 72]: return (0, 30) else: return (0, 10) - m.fc = Var(m.str, m.compon, within=Reals, bounds=bound_fc, - initialize=1, doc='component flowrates (kg-mole per min)') - m.p = Var(m.str, within=NonNegativeReals, bounds=(0.1, 4.0), - initialize=3.0, doc='stream pressure (mega_pascal)') - m.t = Var(m.str, within=NonNegativeReals, bounds=(3.0, 10.0), - initialize=3.0, doc='stream temperature (100 k)') - m.vp = Var(m.str, m.compon, within=NonNegativeReals, initialize=1, - bounds=(0, 10), doc='vapor pressure (mega-pascal)') + + m.fc = Var( + m.str, + m.compon, + within=Reals, + bounds=bound_fc, + initialize=1, + doc='component flowrates (kg-mole per min)', + ) + m.p = Var( + m.str, + within=NonNegativeReals, + bounds=(0.1, 4.0), + initialize=3.0, + doc='stream pressure (mega_pascal)', + ) + m.t = Var( + m.str, + within=NonNegativeReals, + bounds=(3.0, 10.0), + initialize=3.0, + doc='stream temperature (100 k)', + ) + m.vp = Var( + m.str, + m.compon, + within=NonNegativeReals, + initialize=1, + bounds=(0, 10), + doc='vapor pressure (mega-pascal)', + ) def boundsofe(m): if i == 20: @@ -506,8 +770,8 @@ def boundsofe(m): return (0.5, 1.0) else: return (None, 1.0) - m.e = Var(m.str, within=NonNegativeReals, - bounds=boundsofe, doc='split fraction') + + m.e = Var(m.str, within=NonNegativeReals, bounds=boundsofe, doc='split fraction') # obj function m.const = Param(initialize=22.5, doc='constant term in obj fcn') @@ -517,12 +781,12 @@ def boundsofe(m): for rct in m.rct: m.conv[rct, 'tol'].setub(0.973) m.sel.setub(1.0 - 0.0036) - m.reflux[1].setlb(0.02*1.2) - m.reflux[1].setub(0.10*1.2) - m.reflux[2].setlb(0.50*1.2) - m.reflux[2].setub(2.00*1.2) - m.reflux[3].setlb(0.02*1.2) - m.reflux[3].setub(0.1*1.2) + m.reflux[1].setlb(0.02 * 1.2) + m.reflux[1].setub(0.10 * 1.2) + m.reflux[2].setlb(0.50 * 1.2) + m.reflux[2].setub(2.00 * 1.2) + m.reflux[3].setlb(0.02 * 1.2) + m.reflux[3].setub(0.1 * 1.2) m.nmin[1].setlb(0) m.nmin[1].setub(4) m.nmin[2].setlb(8) @@ -530,11 +794,11 @@ def boundsofe(m): m.nmin[3].setlb(0) m.nmin[3].setub(4) m.ndist[1].setlb(0) - m.ndist[1].setub(4*2/m.disteff) + m.ndist[1].setub(4 * 2 / m.disteff) m.ndist[3].setlb(0) - m.ndist[3].setub(4*2/m.disteff) - m.ndist[2].setlb(8*2/m.disteff) - m.ndist[2].setub(14*2/m.disteff) + m.ndist[3].setub(4 * 2 / m.disteff) + m.ndist[2].setlb(8 * 2 / m.disteff) + m.ndist[2].setub(14 * 2 / m.disteff) m.rmin[1].setlb(0.02) m.rmin[1].setub(0.10) m.rmin[2].setlb(0.50) @@ -549,61 +813,144 @@ def boundsofe(m): m.t[26].setub(3.2) for i in range(49, 52): m.t[i].setlb(2.0) - m.t[27].setlb((m.antb['ben'] / (m.anta['ben'] - - log(m.distp[1].lb * 7500.6168)) - m.antc['ben']) / 100.) - m.t[27].setub((m.antb['ben'] / (m.anta['ben'] - - log(m.distp[1].ub * 7500.6168)) - m.antc['ben']) / 100.) - m.t[31].setlb((m.antb['ben'] / (m.anta['ben'] - - log(m.distp[2].lb * 7500.6168)) - m.antc['ben']) / 100.) - m.t[31].setub((m.antb['ben'] / (m.anta['ben'] - - log(m.distp[2].ub * 7500.6168)) - m.antc['ben']) / 100.) - m.t[32].setlb((m.antb['tol'] / (m.anta['tol'] - - log(m.distp[2].lb * 7500.6168)) - m.antc['tol']) / 100.) - m.t[32].setub((m.antb['tol'] / (m.anta['tol'] - - log(m.distp[2].ub * 7500.6168)) - m.antc['tol']) / 100.) - m.t[34].setlb((m.antb['tol'] / (m.anta['tol'] - - log(m.distp[3].lb * 7500.6168)) - m.antc['tol']) / 100.) - m.t[34].setub((m.antb['tol'] / (m.anta['tol'] - - log(m.distp[3].ub * 7500.6168)) - m.antc['tol']) / 100.) - m.t[35].setlb((m.antb['dip'] / (m.anta['dip'] - - log(m.distp[3].lb * 7500.6168)) - m.antc['dip']) / 100.) - m.t[35].setub((m.antb['dip'] / (m.anta['dip'] - - log(m.distp[3].ub * 7500.6168)) - m.antc['dip']) / 100.) + m.t[27].setlb( + ( + m.antb['ben'] / (m.anta['ben'] - log(m.distp[1].lb * 7500.6168)) + - m.antc['ben'] + ) + / 100.0 + ) + m.t[27].setub( + ( + m.antb['ben'] / (m.anta['ben'] - log(m.distp[1].ub * 7500.6168)) + - m.antc['ben'] + ) + / 100.0 + ) + m.t[31].setlb( + ( + m.antb['ben'] / (m.anta['ben'] - log(m.distp[2].lb * 7500.6168)) + - m.antc['ben'] + ) + / 100.0 + ) + m.t[31].setub( + ( + m.antb['ben'] / (m.anta['ben'] - log(m.distp[2].ub * 7500.6168)) + - m.antc['ben'] + ) + / 100.0 + ) + m.t[32].setlb( + ( + m.antb['tol'] / (m.anta['tol'] - log(m.distp[2].lb * 7500.6168)) + - m.antc['tol'] + ) + / 100.0 + ) + m.t[32].setub( + ( + m.antb['tol'] / (m.anta['tol'] - log(m.distp[2].ub * 7500.6168)) + - m.antc['tol'] + ) + / 100.0 + ) + m.t[34].setlb( + ( + m.antb['tol'] / (m.anta['tol'] - log(m.distp[3].lb * 7500.6168)) + - m.antc['tol'] + ) + / 100.0 + ) + m.t[34].setub( + ( + m.antb['tol'] / (m.anta['tol'] - log(m.distp[3].ub * 7500.6168)) + - m.antc['tol'] + ) + / 100.0 + ) + m.t[35].setlb( + ( + m.antb['dip'] / (m.anta['dip'] - log(m.distp[3].lb * 7500.6168)) + - m.antc['dip'] + ) + / 100.0 + ) + m.t[35].setub( + ( + m.antb['dip'] / (m.anta['dip'] - log(m.distp[3].ub * 7500.6168)) + - m.antc['dip'] + ) + / 100.0 + ) # absorber m.beta[1, 'ben'].setlb(0.00011776) m.beta[1, 'ben'].setub(5.72649) m.beta[1, 'tol'].setlb(0.00018483515) m.beta[1, 'tol'].setub(15) - m.gamma[1, 'tol'].setlb(log( - (1 - m.aabs['tol'] ** (m.nabs[1].lb * m.abseff + m.eps1)) / (1 - m.aabs['tol']))) - m.gamma[1, 'tol'].setub(min(15, log( - (1 - m.aabs['tol'] ** (m.nabs[1].ub * m.abseff + m.eps1)) / (1 - m.aabs['tol'])))) + m.gamma[1, 'tol'].setlb( + log( + (1 - m.aabs['tol'] ** (m.nabs[1].lb * m.abseff + m.eps1)) + / (1 - m.aabs['tol']) + ) + ) + m.gamma[1, 'tol'].setub( + min( + 15, + log( + (1 - m.aabs['tol'] ** (m.nabs[1].ub * m.abseff + m.eps1)) + / (1 - m.aabs['tol']) + ), + ) + ) for abso in m.abs: for compon in m.compon: - m.beta[abso, compon].setlb(log( - (1 - m.aabs[compon] ** (m.nabs[1].lb * m.abseff + m.eps1 + 1)) / (1 - m.aabs[compon]))) - m.beta[abso, compon].setub(min(15, log( - (1 - m.aabs[compon] ** (m.nabs[1].ub * m.abseff + m.eps1 + 1)) / (1 - m.aabs[compon])))) + m.beta[abso, compon].setlb( + log( + (1 - m.aabs[compon] ** (m.nabs[1].lb * m.abseff + m.eps1 + 1)) + / (1 - m.aabs[compon]) + ) + ) + m.beta[abso, compon].setub( + min( + 15, + log( + (1 - m.aabs[compon] ** (m.nabs[1].ub * m.abseff + m.eps1 + 1)) + / (1 - m.aabs[compon]) + ), + ) + ) m.t[67].setlb(3.0) m.t[67].setub(3.0) for compon in m.compon: - m.vp[67, compon].setlb((1. / 7500.6168) * exp(m.anta[compon] - - m.antb[compon] / (value(m.t[67]) * 100. + m.antc[compon]))) - m.vp[67, compon].setub((1. / 7500.6168) * exp(m.anta[compon] - - m.antb[compon] / (value(m.t[67]) * 100. + m.antc[compon]))) - - - flashdata_file = os.path.join(dir_path,'flashdata.csv') + m.vp[67, compon].setlb( + (1.0 / 7500.6168) + * exp( + m.anta[compon] + - m.antb[compon] / (value(m.t[67]) * 100.0 + m.antc[compon]) + ) + ) + m.vp[67, compon].setub( + (1.0 / 7500.6168) + * exp( + m.anta[compon] + - m.antb[compon] / (value(m.t[67]) * 100.0 + m.antc[compon]) + ) + ) + + flashdata_file = os.path.join(dir_path, 'flashdata.csv') flash = pd.read_csv(flashdata_file, header=0) number = flash.iloc[:, [4]].dropna().values two_digit_number = flash.iloc[:, [0]].dropna().values two_digit_compon = flash.iloc[:, [1]].dropna().values for i in range(len(two_digit_number)): m.eflsh[two_digit_number[i, 0], two_digit_compon[i, 0]].setlb( - flash.iloc[:, [2]].dropna().values[i, 0]) + flash.iloc[:, [2]].dropna().values[i, 0] + ) m.eflsh[two_digit_number[i, 0], two_digit_compon[i, 0]].setub( - flash.iloc[:, [3]].dropna().values[i, 0]) + flash.iloc[:, [3]].dropna().values[i, 0] + ) for i in range(len(number)): m.flshp[number[i, 0]].setlb(flash.iloc[:, [5]].dropna().values[i, 0]) m.flshp[number[i, 0]].setub(flash.iloc[:, [6]].dropna().values[i, 0]) @@ -622,10 +969,20 @@ def boundsofe(m): for stream in m.str: for compon in m.compon: - m.vp[stream, compon].setlb((1. / 7500.6168) * exp( - m.anta[compon] - m.antb[compon] / (m.t[stream].lb * 100. + m.antc[compon]))) - m.vp[stream, compon].setub((1. / 7500.6168) * exp( - m.anta[compon] - m.antb[compon] / (m.t[stream].ub * 100. + m.antc[compon]))) + m.vp[stream, compon].setlb( + (1.0 / 7500.6168) + * exp( + m.anta[compon] + - m.antb[compon] / (m.t[stream].lb * 100.0 + m.antc[compon]) + ) + ) + m.vp[stream, compon].setub( + (1.0 / 7500.6168) + * exp( + m.anta[compon] + - m.antb[compon] / (m.t[stream].ub * 100.0 + m.antc[compon]) + ) + ) m.p[1].setub(3.93) m.p[1].setlb(3.93) @@ -641,16 +998,14 @@ def boundsofe(m): if (dist, stream) in m.ldist and (dist, compon) in m.dlkey: m.avevlt[dist].setlb(m.vp[stream, compon].ub) if (dist, stream) in m.ldist and (dist, compon) in m.dhkey: - m.avevlt[dist].setlb( - m.avevlt[dist].lb/m.vp[stream, compon].ub) + m.avevlt[dist].setlb(m.avevlt[dist].lb / m.vp[stream, compon].ub) for dist in m.dist: for stream in m.str: for compon in m.compon: if (dist, stream) in m.vdist and (dist, compon) in m.dlkey: m.avevlt[dist].setub(m.vp[stream, compon].lb) if (dist, stream) in m.vdist and (dist, compon) in m.dhkey: - m.avevlt[dist].setub( - m.avevlt[dist].ub/m.vp[stream, compon].lb) + m.avevlt[dist].setub(m.avevlt[dist].ub / m.vp[stream, compon].lb) # ## initialization procedure @@ -677,7 +1032,7 @@ def boundsofe(m): m.qfuel[1] = 0.0475341 m.q[2] = 54.3002 - file_1 = os.path.join(dir_path,'GAMS_init_stream_data.csv') + file_1 = os.path.join(dir_path, 'GAMS_init_stream_data.csv') stream = pd.read_csv(file_1, usecols=[0]) data = pd.read_csv(file_1, usecols=[1]) temp = pd.read_csv(file_1, usecols=[3]) @@ -691,7 +1046,7 @@ def boundsofe(m): m.f[stream.to_numpy()[i, 0]] = flow.to_numpy()[i, 0] m.e[stream.to_numpy()[i, 0]] = e.to_numpy()[i, 0] - file_2 = os.path.join(dir_path,'GAMS_init_stream_compon_data.csv') + file_2 = os.path.join(dir_path, 'GAMS_init_stream_compon_data.csv') streamfc = pd.read_csv(file_2, usecols=[0]) comp = pd.read_csv(file_2, usecols=[1]) fc = pd.read_csv(file_2, usecols=[2]) @@ -700,12 +1055,10 @@ def boundsofe(m): vp = pd.read_csv(file_2, usecols=[5]) for i in range(len(streamfc)): - m.fc[streamfc.to_numpy()[i, 0], comp.to_numpy()[ - i, 0]] = fc.to_numpy()[i, 0] - m.vp[streamvp.to_numpy()[i, 0], compvp.to_numpy()[ - i, 0]] = vp.to_numpy()[i, 0] + m.fc[streamfc.to_numpy()[i, 0], comp.to_numpy()[i, 0]] = fc.to_numpy()[i, 0] + m.vp[streamvp.to_numpy()[i, 0], compvp.to_numpy()[i, 0]] = vp.to_numpy()[i, 0] - file_3 = os.path.join(dir_path,'GAMS_init_data.csv') + file_3 = os.path.join(dir_path, 'GAMS_init_data.csv') stream3 = pd.read_csv(file_3, usecols=[0]) a = pd.read_csv(file_3, usecols=[1]) avevlt = pd.read_csv(file_3, usecols=[3]) @@ -736,680 +1089,1187 @@ def boundsofe(m): splp = pd.read_csv(file_3, usecols=[51]) splt = pd.read_csv(file_3, usecols=[53]) - for i in range(2): - m.rctp[i+1] = rctp.to_numpy()[i, 0] - m.rctt[i+1] = rctt.to_numpy()[i, 0] - m.rctvol[i+1] = rctvol.to_numpy()[i, 0] - m.sel[i+1] = sel.to_numpy()[i, 0] - m.krct[i+1] = krct.to_numpy()[i, 0] - m.consum[i+1, 'tol'] = consum.to_numpy()[i, 0] - m.conv[i+1, 'tol'] = conv.to_numpy()[i, 0] + m.rctp[i + 1] = rctp.to_numpy()[i, 0] + m.rctt[i + 1] = rctt.to_numpy()[i, 0] + m.rctvol[i + 1] = rctvol.to_numpy()[i, 0] + m.sel[i + 1] = sel.to_numpy()[i, 0] + m.krct[i + 1] = krct.to_numpy()[i, 0] + m.consum[i + 1, 'tol'] = consum.to_numpy()[i, 0] + m.conv[i + 1, 'tol'] = conv.to_numpy()[i, 0] m.a[stream3.to_numpy()[i, 0]] = a.to_numpy()[i, 0] - m.qc[i+1] = qc.to_numpy()[i, 0] + m.qc[i + 1] = qc.to_numpy()[i, 0] for i in range(3): - m.avevlt[i+1] = avevlt.to_numpy()[i, 0] - m.distp[i+1] = disp.to_numpy()[i, 0] - m.flshp[i+1] = flshp.to_numpy()[i, 0] - m.flsht[i+1] = flsht.to_numpy()[i, 0] - m.ndist[i+1] = ndist.to_numpy()[i, 0] - m.nmin[i+1] = nmin.to_numpy()[i, 0] - m.reflux[i+1] = reflux.to_numpy()[i, 0] - m.rmin[i+1] = rmin.to_numpy()[i, 0] - m.splp[i+1] = splp.to_numpy()[i, 0] - m.splt[i+1] = splt.to_numpy()[i, 0] + m.avevlt[i + 1] = avevlt.to_numpy()[i, 0] + m.distp[i + 1] = disp.to_numpy()[i, 0] + m.flshp[i + 1] = flshp.to_numpy()[i, 0] + m.flsht[i + 1] = flsht.to_numpy()[i, 0] + m.ndist[i + 1] = ndist.to_numpy()[i, 0] + m.nmin[i + 1] = nmin.to_numpy()[i, 0] + m.reflux[i + 1] = reflux.to_numpy()[i, 0] + m.rmin[i + 1] = rmin.to_numpy()[i, 0] + m.splp[i + 1] = splp.to_numpy()[i, 0] + m.splt[i + 1] = splt.to_numpy()[i, 0] for i in range(5): m.beta[1, comp1.to_numpy()[i, 0]] = beta.to_numpy()[i, 0] - m.mxrp[i+1] = mxrp.to_numpy()[i, 0] + m.mxrp[i + 1] = mxrp.to_numpy()[i, 0] for i in range(4): - m.qh[i+1] = qh.to_numpy()[i, 0] + m.qh[i + 1] = qh.to_numpy()[i, 0] for i in range(len(stream4)): - m.eflsh[stream4.to_numpy()[i, 0], comp2.to_numpy()[ - i, 0]] = eflsh.to_numpy()[i, 0] + m.eflsh[stream4.to_numpy()[i, 0], comp2.to_numpy()[i, 0]] = eflsh.to_numpy()[ + i, 0 + ] for i in range(6): - m.spl1p[i+1] = spl1p.to_numpy()[i, 0] - m.spl1t[i+1] = spl1t.to_numpy()[i, 0] + m.spl1p[i + 1] = spl1p.to_numpy()[i, 0] + m.spl1t[i + 1] = spl1t.to_numpy()[i, 0] # ## constraints - m.specrec = Constraint(expr=m.fc[72, 'h2'] >= 0.5 * m.f[72]) m.specprod = Constraint(expr=m.fc[31, 'ben'] >= 0.9997 * m.f[31]) def Fbal(_m, stream): return m.f[stream] == sum(m.fc[stream, compon] for compon in m.compon) + m.fbal = Constraint(m.str, rule=Fbal) def H2feed(m, compon): return m.fc[1, compon] == m.f[1] * m.f1comp[compon] + m.h2feed = Constraint(m.compon, rule=H2feed) def Tolfeed(_m, compon): return m.fc[66, compon] == m.f[66] * m.f66comp[compon] + m.tolfeed = Constraint(m.compon, rule=Tolfeed) def Tolabs(_m, compon): return m.fc[67, compon] == m.f[67] * m.f67comp[compon] + m.tolabs = Constraint(m.compon, rule=Tolabs) def build_absorber(b, absorber): - " Function for absorber" + "Function for absorber" + def Absfact(_m, i, compon): if (i, compon) in m.anorm: - return sum(m.f[stream] * m.p[stream] for (absb, stream) in m.ilabs if absb == i) == sum(m.f[stream] for (absc, stream) in m.ivabs if absc == i) * m.aabs[compon] * sum(m.vp[stream, compon] for (absd, stream) in m.ilabs if absd == i) + return sum( + m.f[stream] * m.p[stream] for (absb, stream) in m.ilabs if absb == i + ) == sum( + m.f[stream] for (absc, stream) in m.ivabs if absc == i + ) * m.aabs[ + compon + ] * sum( + m.vp[stream, compon] for (absd, stream) in m.ilabs if absd == i + ) return Constraint.Skip + b.absfact = Constraint( - [absorber], m.compon, rule=Absfact, doc='absorbption factor equation') + [absorber], m.compon, rule=Absfact, doc='absorbption factor equation' + ) def Gameqn(_m, i, compon): if (i, compon) in m.asolv: - return m.gamma[i, compon] == log((1 - m.aabs[compon] ** (m.nabs[i] * m.abseff + m.eps1)) / (1 - m.aabs[compon])) + return m.gamma[i, compon] == log( + (1 - m.aabs[compon] ** (m.nabs[i] * m.abseff + m.eps1)) + / (1 - m.aabs[compon]) + ) return Constraint.Skip - b.gameqn = Constraint([absorber], m.compon, - rule=Gameqn, doc='definition of gamma') + + b.gameqn = Constraint( + [absorber], m.compon, rule=Gameqn, doc='definition of gamma' + ) def Betaeqn(_m, i, compon): if (i, compon) not in m.asimp: - return m.beta[i, compon] == log((1 - m.aabs[compon] ** (m.nabs[i] * m.abseff + 1)) / (1 - m.aabs[compon])) + return m.beta[i, compon] == log( + (1 - m.aabs[compon] ** (m.nabs[i] * m.abseff + 1)) + / (1 - m.aabs[compon]) + ) return Constraint.Skip def Abssvrec(_m, i, compon): if (i, compon) in m.asolv: - return sum(m.fc[stream, compon] for (i, stream) in m.ovabs) * exp(m.beta[i, compon]) == sum(m.fc[stream, compon] for (i_, stream) in m.ivabs) + exp(m.gamma[i, compon]) * sum(m.fc[stream, compon] for (i_, stream) in m.ilabs) + return sum(m.fc[stream, compon] for (i, stream) in m.ovabs) * exp( + m.beta[i, compon] + ) == sum(m.fc[stream, compon] for (i_, stream) in m.ivabs) + exp( + m.gamma[i, compon] + ) * sum( + m.fc[stream, compon] for (i_, stream) in m.ilabs + ) return Constraint.Skip def Absrec(_m, i, compon): if (i, compon) in m.anorm: - return sum(m.fc[i, compon] for (abs, i) in m.ovabs) * exp(m.beta[i, compon]) == sum(m.fc[i, compon] for(abs, i) in m.ivabs) + return sum(m.fc[i, compon] for (abs, i) in m.ovabs) * exp( + m.beta[i, compon] + ) == sum(m.fc[i, compon] for (abs, i) in m.ivabs) return Constraint.Skip def abssimp(_m, absorb, compon): if (absorb, compon) in m.asimp: - return sum(m.fc[i, compon] for (absorb, i) in m.ovabs) == sum(m.fc[i, compon] for (absorb, i) in m.ivabs) / m.cbeta[compon] + return ( + sum(m.fc[i, compon] for (absorb, i) in m.ovabs) + == sum(m.fc[i, compon] for (absorb, i) in m.ivabs) / m.cbeta[compon] + ) return Constraint.Skip def Abscmb(_m, i, compon): - return sum(m.fc[stream, compon] for (i, stream) in m.ilabs) + sum(m.fc[stream, compon] for (i, stream) in m.ivabs) == sum(m.fc[stream, compon] for (i, stream) in m.olabs) + sum(m.fc[stream, compon] for (i, stream) in m.ovabs) - b.abscmb = Constraint([absorber], m.compon, rule=Abscmb, - doc='overall component mass balance') + return sum(m.fc[stream, compon] for (i, stream) in m.ilabs) + sum( + m.fc[stream, compon] for (i, stream) in m.ivabs + ) == sum(m.fc[stream, compon] for (i, stream) in m.olabs) + sum( + m.fc[stream, compon] for (i, stream) in m.ovabs + ) + + b.abscmb = Constraint( + [absorber], m.compon, rule=Abscmb, doc='overall component mass balance' + ) def Abspl(_m, i): - return sum(m.p[stream] for (_, stream) in m.ilabs) == sum(m.p[stream] for (_, stream) in m.olabs) - b.abspl = Constraint([absorber], rule=Abspl, - doc='pressure relation for liquid') + return sum(m.p[stream] for (_, stream) in m.ilabs) == sum( + m.p[stream] for (_, stream) in m.olabs + ) + + b.abspl = Constraint([absorber], rule=Abspl, doc='pressure relation for liquid') def Abstl(_m, i): - return sum(m.t[stream] for (_, stream) in m.ilabs) == sum(m.t[stream] for (_, stream) in m.olabs) - b.abstl = Constraint([absorber], rule=Abstl, - doc=' temperature relation for liquid') + return sum(m.t[stream] for (_, stream) in m.ilabs) == sum( + m.t[stream] for (_, stream) in m.olabs + ) + + b.abstl = Constraint( + [absorber], rule=Abstl, doc=' temperature relation for liquid' + ) def Abspv(_m, i): - return sum(m.p[stream] for (_, stream) in m.ivabs) == sum(m.p[stream] for (_, stream) in m.ovabs) - b.abspv = Constraint([absorber], rule=Abspv, - doc=' pressure relation for vapor') + return sum(m.p[stream] for (_, stream) in m.ivabs) == sum( + m.p[stream] for (_, stream) in m.ovabs + ) + + b.abspv = Constraint([absorber], rule=Abspv, doc=' pressure relation for vapor') def Abspin(_m, i): - return sum(m.p[stream] for (_, stream) in m.ilabs) == sum(m.p[stream] for (_, stream) in m.ivabs) + return sum(m.p[stream] for (_, stream) in m.ilabs) == sum( + m.p[stream] for (_, stream) in m.ivabs + ) + b.absp = Constraint([absorber], rule=Abspin) def Absttop(_m, i): - return sum(m.t[stream] for (_, stream) in m.ilabs) == sum(m.t[stream] for (_, stream) in m.ovabs) - b.abst = Constraint([absorber], rule=Absttop, - doc='temperature relation at top') + return sum(m.t[stream] for (_, stream) in m.ilabs) == sum( + m.t[stream] for (_, stream) in m.ovabs + ) + + b.abst = Constraint([absorber], rule=Absttop, doc='temperature relation at top') b.abssimp = Constraint( - [absorber], m.compon, rule=abssimp, doc=' recovery of simplified components') - b.absrec = Constraint([absorber], m.compon, - rule=Absrec, doc='recovery of non-solvent') - b.abssvrec = Constraint([absorber], m.compon, - rule=Abssvrec, doc='recovery of solvent') - b.betaeqn = Constraint([absorber], m.compon, - rule=Betaeqn, doc='definition of beta') + [absorber], m.compon, rule=abssimp, doc=' recovery of simplified components' + ) + b.absrec = Constraint( + [absorber], m.compon, rule=Absrec, doc='recovery of non-solvent' + ) + b.abssvrec = Constraint( + [absorber], m.compon, rule=Abssvrec, doc='recovery of solvent' + ) + b.betaeqn = Constraint( + [absorber], m.compon, rule=Betaeqn, doc='definition of beta' + ) def build_compressor(b, comp): def Compcmb(_m, comp1, compon): if comp1 == comp: - return sum(m.fc[stream, compon] for (comp_, stream) in m.ocomp if comp_ == comp1) == sum(m.fc[stream, compon] for (comp_, stream) in m.icomp if comp_ == comp1) + return sum( + m.fc[stream, compon] + for (comp_, stream) in m.ocomp + if comp_ == comp1 + ) == sum( + m.fc[stream, compon] + for (comp_, stream) in m.icomp + if comp_ == comp1 + ) return Constraint.Skip + b.compcmb = Constraint( - [comp], m.compon, rule=Compcmb, doc='component balance for compressor') + [comp], m.compon, rule=Compcmb, doc='component balance for compressor' + ) def Comphb(_m, comp1): if comp1 == comp: - return sum(m.t[stream] for (_, stream) in m.ocomp if _ == comp) == m.presrat[comp] * sum(m.t[stream] for (_, stream) in m.icomp if _ == comp) + return sum( + m.t[stream] for (_, stream) in m.ocomp if _ == comp + ) == m.presrat[comp] * sum( + m.t[stream] for (_, stream) in m.icomp if _ == comp + ) return Constraint.Skip - b.comphb = Constraint([comp], rule=Comphb, - doc='heat balance for compressor') + + b.comphb = Constraint([comp], rule=Comphb, doc='heat balance for compressor') def Compelec(_m, comp_): if comp_ == comp: - return m.elec[comp_] == m.alpha * (m.presrat[comp_] - 1) * sum(100. * m.t[stream] * m.f[stream] / 60. * (1./m.compeff) * (m.gam / (m.gam - 1.)) for (comp1, stream) in m.icomp if comp_ == comp1) - return Constraint.Skip - b.compelec = Constraint([comp], rule=Compelec, - doc="energy balance for compressor") + return m.elec[comp_] == m.alpha * (m.presrat[comp_] - 1) * sum( + 100.0 + * m.t[stream] + * m.f[stream] + / 60.0 + * (1.0 / m.compeff) + * (m.gam / (m.gam - 1.0)) + for (comp1, stream) in m.icomp + if comp_ == comp1 + ) + return Constraint.Skip + + b.compelec = Constraint( + [comp], rule=Compelec, doc="energy balance for compressor" + ) def Ratio(_m, comp_): if comp == comp_: - return m.presrat[comp_] ** (m.gam/(m.gam-1.)) == sum(m.p[stream] for (comp1, stream) in m.ocomp if comp_ == comp1) / sum(m.p[stream] for (comp1, stream) in m.icomp if comp1 == comp_) + return m.presrat[comp_] ** (m.gam / (m.gam - 1.0)) == sum( + m.p[stream] for (comp1, stream) in m.ocomp if comp_ == comp1 + ) / sum(m.p[stream] for (comp1, stream) in m.icomp if comp1 == comp_) return Constraint.Skip - b.ratio = Constraint([comp], rule=Ratio, - doc='pressure ratio (out to in)') + b.ratio = Constraint([comp], rule=Ratio, doc='pressure ratio (out to in)') m.vapor_pressure_unit_match = Param( - initialize=7500.6168, doc="unit match coeffieicnt for vapor pressure calculation") - m.actual_reflux_ratio = Param( - initialize=1.2, doc="actual reflux ratio coeffieicnt") + initialize=7500.6168, + doc="unit match coeffieicnt for vapor pressure calculation", + ) + m.actual_reflux_ratio = Param(initialize=1.2, doc="actual reflux ratio coeffieicnt") m.recovery_specification_coeffieicnt = Param( - initialize=0.05, doc="recovery specification coeffieicnt") + initialize=0.05, doc="recovery specification coeffieicnt" + ) def build_distillation(b, dist): def Antdistb(_m, dist_, stream, compon): - if (dist_, stream) in m.ldist and (dist_, compon) in m.dkey and dist_ == dist: - return log(m.vp[stream, compon] * m.vapor_pressure_unit_match) == m.anta[compon] - m.antb[compon] / (m.t[stream] * 100. + m.antc[compon]) + if ( + (dist_, stream) in m.ldist + and (dist_, compon) in m.dkey + and dist_ == dist + ): + return log( + m.vp[stream, compon] * m.vapor_pressure_unit_match + ) == m.anta[compon] - m.antb[compon] / ( + m.t[stream] * 100.0 + m.antc[compon] + ) return Constraint.Skip + b.antdistb = Constraint( - [dist], m.str, m.compon, rule=Antdistb, doc=' vapor pressure correlation (bot)') + [dist], + m.str, + m.compon, + rule=Antdistb, + doc=' vapor pressure correlation (bot)', + ) def Antdistt(_m, dist_, stream, compon): - if (dist_, stream) in m.vdist and (dist_, compon) in m.dkey and dist == dist_: - return log(m.vp[stream, compon] * m.vapor_pressure_unit_match) == m.anta[compon] - m.antb[compon] / (m.t[stream] * 100. + m.antc[compon]) + if ( + (dist_, stream) in m.vdist + and (dist_, compon) in m.dkey + and dist == dist_ + ): + return log( + m.vp[stream, compon] * m.vapor_pressure_unit_match + ) == m.anta[compon] - m.antb[compon] / ( + m.t[stream] * 100.0 + m.antc[compon] + ) return Constraint.Skip + b.antdistt = Constraint( - [dist], m.str, m.compon, rule=Antdistt, doc='vapor pressure correlation (top)') + [dist], + m.str, + m.compon, + rule=Antdistt, + doc='vapor pressure correlation (top)', + ) def Relvol(_m, dist_): if dist == dist_: - divided1 = sum(sum(m.vp[stream, compon] for (dist_, compon) in m.dlkey if dist_ == dist)/sum(m.vp[stream, compon] - for (dist_, compon) in m.dhkey if dist_ == dist) for (dist_, stream) in m.vdist if dist_ == dist) - divided2 = sum(sum(m.vp[stream, compon] for (dist_, compon) in m.dlkey if dist_ == dist)/sum(m.vp[stream, compon] - for (dist_, compon) in m.dhkey if dist_ == dist) for (dist_, stream) in m.ldist if dist_ == dist) + divided1 = sum( + sum( + m.vp[stream, compon] + for (dist_, compon) in m.dlkey + if dist_ == dist + ) + / sum( + m.vp[stream, compon] + for (dist_, compon) in m.dhkey + if dist_ == dist + ) + for (dist_, stream) in m.vdist + if dist_ == dist + ) + divided2 = sum( + sum( + m.vp[stream, compon] + for (dist_, compon) in m.dlkey + if dist_ == dist + ) + / sum( + m.vp[stream, compon] + for (dist_, compon) in m.dhkey + if dist_ == dist + ) + for (dist_, stream) in m.ldist + if dist_ == dist + ) return m.avevlt[dist] == sqrt(divided1 * divided2) return Constraint.Skip - b.relvol = Constraint([dist], rule=Relvol, - doc='average relative volatilty') + + b.relvol = Constraint([dist], rule=Relvol, doc='average relative volatilty') def Undwood(_m, dist_): if dist_ == dist: - return sum(m.fc[stream, compon] for (dist1, compon) in m.dlkey if dist1 == dist_ for (dist1, stream) in m.idist if dist1 == dist_) * m.rmin[dist_] * (m.avevlt[dist_] - 1) == sum(m.f[stream] for (dist1, stream) in m.idist if dist1 == dist_) + return sum( + m.fc[stream, compon] + for (dist1, compon) in m.dlkey + if dist1 == dist_ + for (dist1, stream) in m.idist + if dist1 == dist_ + ) * m.rmin[dist_] * (m.avevlt[dist_] - 1) == sum( + m.f[stream] for (dist1, stream) in m.idist if dist1 == dist_ + ) return Constraint.Skip - b.undwood = Constraint([dist], rule=Undwood, - doc='minimum reflux ratio equation') + + b.undwood = Constraint( + [dist], rule=Undwood, doc='minimum reflux ratio equation' + ) def Actreflux(_m, dist_): if dist_ == dist: return m.reflux[dist_] == m.actual_reflux_ratio * m.rmin[dist_] return Constraint.Skip - b.actreflux = Constraint( - [dist], rule=Actreflux, doc='actual reflux ratio') + + b.actreflux = Constraint([dist], rule=Actreflux, doc='actual reflux ratio') def Fenske(_m, dist_): if dist == dist_: - sum1 = sum((m.f[stream] + m.eps1)/(m.fc[stream, compon] + m.eps1) for (dist1, compon) - in m.dhkey if dist1 == dist_ for (dist1, stream) in m.vdist if dist1 == dist_) - sum2 = sum((m.f[stream] + m.eps1)/(m.fc[stream, compon] + m.eps1) for (dist1, compon) - in m.dlkey if dist1 == dist_ for (dist1, stream) in m.ldist if dist1 == dist_) + sum1 = sum( + (m.f[stream] + m.eps1) / (m.fc[stream, compon] + m.eps1) + for (dist1, compon) in m.dhkey + if dist1 == dist_ + for (dist1, stream) in m.vdist + if dist1 == dist_ + ) + sum2 = sum( + (m.f[stream] + m.eps1) / (m.fc[stream, compon] + m.eps1) + for (dist1, compon) in m.dlkey + if dist1 == dist_ + for (dist1, stream) in m.ldist + if dist1 == dist_ + ) return m.nmin[dist_] * log(m.avevlt[dist_]) == log(sum1 * sum2) return Constraint.Skip - b.fenske = Constraint([dist], rule=Fenske, - doc='minimum number of trays') + + b.fenske = Constraint([dist], rule=Fenske, doc='minimum number of trays') def Acttray(_m, dist_): if dist == dist_: - return m.ndist[dist_] == m.nmin[dist_] * 2. / m.disteff + return m.ndist[dist_] == m.nmin[dist_] * 2.0 / m.disteff return Constraint.Skip - b.acttray = Constraint([dist], rule=Acttray, - doc='actual number of trays') + + b.acttray = Constraint([dist], rule=Acttray, doc='actual number of trays') def Distspec(_m, dist_, stream, compon): - if (dist_, stream) in m.vdist and (dist_, compon) in m.dhkey and dist_ == dist: - return m.fc[stream, compon] <= m.recovery_specification_coeffieicnt * sum(m.fc[str2, compon] for (dist_, str2) in m.idist if dist == dist_) - return Constraint.Skip - b.distspec = Constraint([dist], m.str, m.compon, - rule=Distspec, doc='recovery specification') + if ( + (dist_, stream) in m.vdist + and (dist_, compon) in m.dhkey + and dist_ == dist + ): + return m.fc[ + stream, compon + ] <= m.recovery_specification_coeffieicnt * sum( + m.fc[str2, compon] for (dist_, str2) in m.idist if dist == dist_ + ) + return Constraint.Skip + + b.distspec = Constraint( + [dist], m.str, m.compon, rule=Distspec, doc='recovery specification' + ) def Distheav(_m, dist_, compon): if (dist_, compon) in m.dh and dist == dist_: - return sum(m.fc[str2, compon] for (dist_, str2) in m.idist if dist_ == dist) == sum(m.fc[str2, compon] for (dist_, str2) in m.ldist if dist_ == dist) + return sum( + m.fc[str2, compon] for (dist_, str2) in m.idist if dist_ == dist + ) == sum( + m.fc[str2, compon] for (dist_, str2) in m.ldist if dist_ == dist + ) return Constraint.Skip - b.distheav = Constraint( - [dist], m.compon, rule=Distheav, doc='heavy components') + + b.distheav = Constraint([dist], m.compon, rule=Distheav, doc='heavy components') def Distlite(_m, dist_, compon): if (dist_, compon) in m.dl and dist_ == dist: - return sum(m.fc[str2, compon] for (dist_, str2) in m.idist if dist == dist_) == sum(m.fc[str2, compon] for (dist_, str2) in m.vdist if dist == dist_) + return sum( + m.fc[str2, compon] for (dist_, str2) in m.idist if dist == dist_ + ) == sum( + m.fc[str2, compon] for (dist_, str2) in m.vdist if dist == dist_ + ) return Constraint.Skip - b.distlite = Constraint( - [dist], m.compon, rule=Distlite, doc='light components') + + b.distlite = Constraint([dist], m.compon, rule=Distlite, doc='light components') def Distpi(_m, dist_, stream): if (dist_, stream) in m.idist and dist_ == dist: return m.distp[dist_] <= m.p[stream] return Constraint.Skip - b.distpi = Constraint([dist], m.str, rule=Distpi, - doc='inlet pressure relation') + + b.distpi = Constraint([dist], m.str, rule=Distpi, doc='inlet pressure relation') def Distvpl(_m, dist_, stream): if (dist_, stream) in m.ldist and dist == dist_: - return m.distp[dist_] == sum(m.vp[stream, compon] for (dist_, compon) in m.dhkey if dist_ == dist) + return m.distp[dist_] == sum( + m.vp[stream, compon] for (dist_, compon) in m.dhkey if dist_ == dist + ) return Constraint.Skip - b.distvpl = Constraint([dist], m.str, rule=Distvpl, - doc='bottom vapor pressure relation') + + b.distvpl = Constraint( + [dist], m.str, rule=Distvpl, doc='bottom vapor pressure relation' + ) def Distvpv(_m, dist_, stream): if dist > 1 and (dist, stream) in m.vdist and dist_ == dist: - return m.distp[dist_] == sum(m.vp[stream, compon] for (dist_, compon) in m.dlkey if dist_ == dist) + return m.distp[dist_] == sum( + m.vp[stream, compon] for (dist_, compon) in m.dlkey if dist_ == dist + ) return Constraint.Skip - b.distvpv = Constraint([dist], m.str, rule=Distvpv, - doc='top vapor pressure relation') + + b.distvpv = Constraint( + [dist], m.str, rule=Distvpv, doc='top vapor pressure relation' + ) def Distpl(_m, dist_, stream): if (dist_, stream) in m.ldist and dist_ == dist: return m.distp[dist_] == m.p[stream] return Constraint.Skip - b.distpl = Constraint([dist], m.str, rule=Distpl, - doc='outlet pressure relation(liquid)') + + b.distpl = Constraint( + [dist], m.str, rule=Distpl, doc='outlet pressure relation(liquid)' + ) def Distpv(_m, dist_, stream): if (dist_, stream) in m.vdist and dist == dist_: return m.distp[dist_] == m.p[stream] return Constraint.Skip - b.distpv = Constraint([dist], m.str, rule=Distpv, - doc='outlet pressure relation(vapor)') + + b.distpv = Constraint( + [dist], m.str, rule=Distpv, doc='outlet pressure relation(vapor)' + ) def Distcmb(_m, dist_, compon): if dist_ == dist: - return sum(m.fc[stream, compon] for (dist1, stream) in m.idist if dist1 == dist_) == sum(m.fc[stream, compon] for (dist1, stream) in m.vdist if dist1 == dist_) + sum(m.fc[stream, compon] for (dist1, stream) in m.ldist if dist1 == dist_) + return sum( + m.fc[stream, compon] + for (dist1, stream) in m.idist + if dist1 == dist_ + ) == sum( + m.fc[stream, compon] + for (dist1, stream) in m.vdist + if dist1 == dist_ + ) + sum( + m.fc[stream, compon] + for (dist1, stream) in m.ldist + if dist1 == dist_ + ) return Constraint.Skip - b.distcmb = Constraint( - [dist], m.compon, rule=Distcmb, doc='component mass balance') - + b.distcmb = Constraint( + [dist], m.compon, rule=Distcmb, doc='component mass balance' + ) def build_flash(b, flsh): def Flshcmb(_m, flsh_, compon): if flsh_ in m.flsh and compon in m.compon and flsh_ == flsh: - return sum(m.fc[stream, compon] for (flsh1, stream) in m.iflsh if flsh1 == flsh_) == sum(m.fc[stream, compon] for (flsh1, stream) in m.vflsh if flsh1 == flsh_) + sum(m.fc[stream, compon] for (flsh1, stream) in m.lflsh if flsh1 == flsh_) + return sum( + m.fc[stream, compon] + for (flsh1, stream) in m.iflsh + if flsh1 == flsh_ + ) == sum( + m.fc[stream, compon] + for (flsh1, stream) in m.vflsh + if flsh1 == flsh_ + ) + sum( + m.fc[stream, compon] + for (flsh1, stream) in m.lflsh + if flsh1 == flsh_ + ) return Constraint.Skip + b.flshcmb = Constraint( - [flsh], m.compon, rule=Flshcmb, doc='component mass balance') + [flsh], m.compon, rule=Flshcmb, doc='component mass balance' + ) def Antflsh(_m, flsh_, stream, compon): if (flsh_, stream) in m.lflsh and flsh_ == flsh: - return log(m.vp[stream, compon] * m.vapor_pressure_unit_match) == m.anta[compon] - m.antb[compon] / (m.t[stream] * 100. + m.antc[compon]) + return log( + m.vp[stream, compon] * m.vapor_pressure_unit_match + ) == m.anta[compon] - m.antb[compon] / ( + m.t[stream] * 100.0 + m.antc[compon] + ) return Constraint.Skip - b.antflsh = Constraint([flsh], m.str, m.compon, - rule=Antflsh, doc='flash pressure relation') + + b.antflsh = Constraint( + [flsh], m.str, m.compon, rule=Antflsh, doc='flash pressure relation' + ) def Flshrec(_m, flsh_, stream, compon): if (flsh_, stream) in m.lflsh and flsh_ == flsh: - return sum(m.eflsh[flsh1, compon2] for (flsh1, compon2) in m.fkey if flsh1 == flsh_) * (m.eflsh[flsh_, compon] * sum(m.vp[stream, compon2] for (flsh1, compon2) in m.fkey if flsh_ == flsh1) + (1. - m.eflsh[flsh_, compon]) * m.vp[stream, compon]) == sum(m.vp[stream, compon2] for (flsh1, compon2) in m.fkey if flsh_ == flsh1) * m.eflsh[flsh_, compon] + return ( + sum( + m.eflsh[flsh1, compon2] + for (flsh1, compon2) in m.fkey + if flsh1 == flsh_ + ) + * ( + m.eflsh[flsh_, compon] + * sum( + m.vp[stream, compon2] + for (flsh1, compon2) in m.fkey + if flsh_ == flsh1 + ) + + (1.0 - m.eflsh[flsh_, compon]) * m.vp[stream, compon] + ) + == sum( + m.vp[stream, compon2] + for (flsh1, compon2) in m.fkey + if flsh_ == flsh1 + ) + * m.eflsh[flsh_, compon] + ) return Constraint.Skip - b.flshrec = Constraint([flsh], m.str, m.compon, - rule=Flshrec, doc='vapor recovery relation') + + b.flshrec = Constraint( + [flsh], m.str, m.compon, rule=Flshrec, doc='vapor recovery relation' + ) def Flsheql(_m, flsh_, compon): if flsh in m.flsh and compon in m.compon and flsh_ == flsh: - return sum(m.fc[stream, compon] for (flsh1, stream) in m.vflsh if flsh1 == flsh_) == sum(m.fc[stream, compon] for (flsh1, stream) in m.iflsh if flsh1 == flsh_) * m.eflsh[flsh, compon] + return ( + sum( + m.fc[stream, compon] + for (flsh1, stream) in m.vflsh + if flsh1 == flsh_ + ) + == sum( + m.fc[stream, compon] + for (flsh1, stream) in m.iflsh + if flsh1 == flsh_ + ) + * m.eflsh[flsh, compon] + ) return Constraint.Skip + b.flsheql = Constraint( - [flsh], m.compon, rule=Flsheql, doc='equilibrium relation') + [flsh], m.compon, rule=Flsheql, doc='equilibrium relation' + ) def Flshpr(_m, flsh_, stream): if (flsh_, stream) in m.lflsh and flsh_ == flsh: - return m.flshp[flsh_] * m.f[stream] == sum(m.vp[stream, compon] * m.fc[stream, compon] for compon in m.compon) + return m.flshp[flsh_] * m.f[stream] == sum( + m.vp[stream, compon] * m.fc[stream, compon] for compon in m.compon + ) return Constraint.Skip - b.flshpr = Constraint([flsh], m.str, rule=Flshpr, - doc='flash pressure relation') + + b.flshpr = Constraint([flsh], m.str, rule=Flshpr, doc='flash pressure relation') def Flshpi(_m, flsh_, stream): if (flsh_, stream) in m.iflsh and flsh_ == flsh: return m.flshp[flsh_] == m.p[stream] return Constraint.Skip - b.flshpi = Constraint([flsh], m.str, rule=Flshpi, - doc='inlet pressure relation') + + b.flshpi = Constraint([flsh], m.str, rule=Flshpi, doc='inlet pressure relation') def Flshpl(_m, flsh_, stream): if (flsh_, stream) in m.lflsh and flsh_ == flsh: return m.flshp[flsh_] == m.p[stream] return Constraint.Skip - b.flshpl = Constraint([flsh], m.str, rule=Flshpl, - doc='outlet pressure relation(liquid)') + + b.flshpl = Constraint( + [flsh], m.str, rule=Flshpl, doc='outlet pressure relation(liquid)' + ) def Flshpv(_m, flsh_, stream): if (flsh_, stream) in m.vflsh and flsh_ == flsh: return m.flshp[flsh_] == m.p[stream] return Constraint.Skip - b.flshpv = Constraint([flsh], m.str, rule=Flshpv, - doc='outlet pressure relation(vapor)') + + b.flshpv = Constraint( + [flsh], m.str, rule=Flshpv, doc='outlet pressure relation(vapor)' + ) def Flshti(_m, flsh_, stream): if (flsh_, stream) in m.iflsh and flsh_ == flsh: return m.flsht[flsh_] == m.t[stream] return Constraint.Skip - b.flshti = Constraint([flsh], m.str, rule=Flshti, - doc='inlet temp. relation') + + b.flshti = Constraint([flsh], m.str, rule=Flshti, doc='inlet temp. relation') def Flshtl(_m, flsh_, stream): if (flsh_, stream) in m.lflsh and flsh_ == flsh: return m.flsht[flsh_] == m.t[stream] return Constraint.Skip - b.flshtl = Constraint([flsh], m.str, rule=Flshtl, - doc='outlet temp. relation(liquid)') + + b.flshtl = Constraint( + [flsh], m.str, rule=Flshtl, doc='outlet temp. relation(liquid)' + ) def Flshtv(_m, flsh_, stream): if (flsh_, stream) in m.vflsh and flsh_ == flsh: return m.flsht[flsh_] == m.t[stream] return Constraint.Skip - b.flshtv = Constraint([flsh], m.str, rule=Flshtv, - doc='outlet temp. relation(vapor)') - + b.flshtv = Constraint( + [flsh], m.str, rule=Flshtv, doc='outlet temp. relation(vapor)' + ) + m.heat_unit_match = Param( - initialize=3600. * 8500. * 1.0e-12 / 60., doc="unit change on temp") + initialize=3600.0 * 8500.0 * 1.0e-12 / 60.0, doc="unit change on temp" + ) def build_furnace(b, furnace): def Furnhb(_m, furn): if furn == furnace: - return m.qfuel[furn] == (sum(m.cp[stream] * m.f[stream] * 100. * m.t[stream] for (furn, stream) in m.ofurn) - sum(m.cp[stream] * m.f[stream] * 100. * m.t[stream] for (furn, stream) in m.ifurn)) * m.heat_unit_match + return ( + m.qfuel[furn] + == ( + sum( + m.cp[stream] * m.f[stream] * 100.0 * m.t[stream] + for (furn, stream) in m.ofurn + ) + - sum( + m.cp[stream] * m.f[stream] * 100.0 * m.t[stream] + for (furn, stream) in m.ifurn + ) + ) + * m.heat_unit_match + ) return Constraint.Skip + b.furnhb = Constraint([furnace], rule=Furnhb, doc='heat balance') def Furncmb(_m, furn, compon): if furn == furnace: - return sum(m.fc[stream, compon] for (furn, stream) in m.ofurn) == sum(m.fc[stream, compon] for (furn, stream) in m.ifurn) + return sum(m.fc[stream, compon] for (furn, stream) in m.ofurn) == sum( + m.fc[stream, compon] for (furn, stream) in m.ifurn + ) return Constraint.Skip - b.furncmb = Constraint([furnace], m.compon, - rule=Furncmb, doc='component mass balance') + + b.furncmb = Constraint( + [furnace], m.compon, rule=Furncmb, doc='component mass balance' + ) def Furnp(_m, furn): if furn == furnace: - return sum(m.p[stream] for (furn, stream) in m.ofurn) == sum(m.p[stream] for (furn, stream) in m.ifurn) - m.furnpdrop + return ( + sum(m.p[stream] for (furn, stream) in m.ofurn) + == sum(m.p[stream] for (furn, stream) in m.ifurn) - m.furnpdrop + ) return Constraint.Skip - b.furnp = Constraint([furnace], rule=Furnp, doc=' pressure relation ') - + b.furnp = Constraint([furnace], rule=Furnp, doc=' pressure relation ') def build_cooler(b, cooler): def Heccmb(_m, hec, compon): - return sum(m.fc[stream, compon] for (hec_, stream) in m.ohec if hec_ == hec) == sum(m.fc[stream, compon] for (hec_, stream) in m.ihec if hec_ == hec) - b.heccmb = Constraint([cooler], m.compon, - rule=Heccmb, doc='heat balance') + return sum( + m.fc[stream, compon] for (hec_, stream) in m.ohec if hec_ == hec + ) == sum(m.fc[stream, compon] for (hec_, stream) in m.ihec if hec_ == hec) + + b.heccmb = Constraint([cooler], m.compon, rule=Heccmb, doc='heat balance') def Hechb(_m, hec): - return m.qc[hec] == (sum(m.cp[stream] * m.f[stream] * 100. * m.t[stream] for (hec_, stream) in m.ihec if hec_ == hec) - sum(m.cp[stream] * m.f[stream] * 100. * m.t[stream] for (hec_, stream) in m.ohec if hec_ == hec)) * m.heat_unit_match - b.hechb = Constraint([cooler], rule=Hechb, - doc='component mass balance') + return ( + m.qc[hec] + == ( + sum( + m.cp[stream] * m.f[stream] * 100.0 * m.t[stream] + for (hec_, stream) in m.ihec + if hec_ == hec + ) + - sum( + m.cp[stream] * m.f[stream] * 100.0 * m.t[stream] + for (hec_, stream) in m.ohec + if hec_ == hec + ) + ) + * m.heat_unit_match + ) + + b.hechb = Constraint([cooler], rule=Hechb, doc='component mass balance') def Hecp(_m, hec): - return sum(m.p[stream] for(hec_, stream) in m.ihec if hec_ == hec) == sum(m.p[stream] for(hec_, stream) in m.ohec if hec_ == hec) - b.hecp = Constraint([cooler], rule=Hecp, doc='pressure relation') + return sum(m.p[stream] for (hec_, stream) in m.ihec if hec_ == hec) == sum( + m.p[stream] for (hec_, stream) in m.ohec if hec_ == hec + ) - + b.hecp = Constraint([cooler], rule=Hecp, doc='pressure relation') def build_heater(b, heater): def Hehcmb(_m, heh, compon): if heh == heater and compon in m.compon: - return sum(m.fc[stream, compon] for (heh_, stream) in m.oheh if heh_ == heh) == sum(m.fc[stream, compon] for (heh_, stream) in m.iheh if heh == heh_) + return sum( + m.fc[stream, compon] for (heh_, stream) in m.oheh if heh_ == heh + ) == sum( + m.fc[stream, compon] for (heh_, stream) in m.iheh if heh == heh_ + ) return Constraint.Skip - b.hehcmb = Constraint(Set( - initialize=[heater]), m.compon, rule=Hehcmb, doc='component balance in heater') + + b.hehcmb = Constraint( + Set(initialize=[heater]), + m.compon, + rule=Hehcmb, + doc='component balance in heater', + ) def Hehhb(_m, heh): if heh == heater: - return m.qh[heh] == (sum(m.cp[stream] * m.f[stream] * 100. * m.t[stream] for (heh_, stream) in m.oheh if heh_ == heh) - - sum(m.cp[stream] * m.f[stream] * 100. * m.t[stream] for (heh_, stream) in m.iheh if heh_ == heh)) * m.heat_unit_match + return ( + m.qh[heh] + == ( + sum( + m.cp[stream] * m.f[stream] * 100.0 * m.t[stream] + for (heh_, stream) in m.oheh + if heh_ == heh + ) + - sum( + m.cp[stream] * m.f[stream] * 100.0 * m.t[stream] + for (heh_, stream) in m.iheh + if heh_ == heh + ) + ) + * m.heat_unit_match + ) return Constraint.Skip + b.hehhb = Constraint( - Set(initialize=[heater]), rule=Hehhb, doc='heat balance for heater') + Set(initialize=[heater]), rule=Hehhb, doc='heat balance for heater' + ) def hehp(_m, heh): if heh == heater: - return sum(m.p[stream] for(heh_, stream) in m.iheh if heh_ == heh) == sum(m.p[stream] for(heh_, stream) in m.oheh if heh == heh_) + return sum( + m.p[stream] for (heh_, stream) in m.iheh if heh_ == heh + ) == sum(m.p[stream] for (heh_, stream) in m.oheh if heh == heh_) return Constraint.Skip + b.Hehp = Constraint( - Set(initialize=[heater]), rule=hehp, doc='no pressure drop thru heater') + Set(initialize=[heater]), rule=hehp, doc='no pressure drop thru heater' + ) - m.exchanger_temp_drop = Param(initialize=0.25) def build_exchanger(b, exchanger): def Exchcmbc(_m, exch, compon): if exch in m.exch and compon in m.compon: - return sum(m.fc[stream, compon] for (exch_, stream) in m.ocexch if exch == exch_) == sum(m.fc[stream, compon] for (exch_, stream) in m.icexch if exch == exch_) + return sum( + m.fc[stream, compon] + for (exch_, stream) in m.ocexch + if exch == exch_ + ) == sum( + m.fc[stream, compon] + for (exch_, stream) in m.icexch + if exch == exch_ + ) return Constraint.Skip - b.exchcmbc = Constraint([exchanger], m.compon, - rule=Exchcmbc, doc='component balance (cold)') + + b.exchcmbc = Constraint( + [exchanger], m.compon, rule=Exchcmbc, doc='component balance (cold)' + ) def Exchcmbh(_m, exch, compon): if exch in m.exch and compon in m.compon: - return sum(m.fc[stream, compon] for (exch_, stream) in m.ohexch if exch == exch_) == sum(m.fc[stream, compon] for (exch_, stream) in m.ihexch if exch == exch_) + return sum( + m.fc[stream, compon] + for (exch_, stream) in m.ohexch + if exch == exch_ + ) == sum( + m.fc[stream, compon] + for (exch_, stream) in m.ihexch + if exch == exch_ + ) return Constraint.Skip - b.exchcmbh = Constraint([exchanger], m.compon, - rule=Exchcmbh, doc='component balance (hot)') + + b.exchcmbh = Constraint( + [exchanger], m.compon, rule=Exchcmbh, doc='component balance (hot)' + ) def Exchhbc(_m, exch): if exch in m.exch: - return (sum(m.cp[stream] * m.f[stream] * 100. * m.t[stream] for (exch_, stream) in m.ocexch if exch == exch_) - sum(m.cp[stream] * m.f[stream] * 100. * m.t[stream] for (exch_, stream) in m.icexch if exch == exch_)) * m.heat_unit_match == m.qexch[exch] + return ( + sum( + m.cp[stream] * m.f[stream] * 100.0 * m.t[stream] + for (exch_, stream) in m.ocexch + if exch == exch_ + ) + - sum( + m.cp[stream] * m.f[stream] * 100.0 * m.t[stream] + for (exch_, stream) in m.icexch + if exch == exch_ + ) + ) * m.heat_unit_match == m.qexch[exch] return Constraint.Skip - b.exchhbc = Constraint([exchanger], rule=Exchhbc, - doc='heat balance for cold stream') + + b.exchhbc = Constraint( + [exchanger], rule=Exchhbc, doc='heat balance for cold stream' + ) def Exchhbh(_m, exch): - return (sum(m.cp[stream] * m.f[stream] * 100. * m.t[stream] for (exch, stream) in m.ihexch) - sum(m.cp[stream] * m.f[stream] * 100. * m.t[stream] for (exch, stream) in m.ohexch)) * m.heat_unit_match == m.qexch[exch] - b.exchhbh = Constraint([exchanger], rule=Exchhbh, - doc='heat balance for hot stream') + return ( + sum( + m.cp[stream] * m.f[stream] * 100.0 * m.t[stream] + for (exch, stream) in m.ihexch + ) + - sum( + m.cp[stream] * m.f[stream] * 100.0 * m.t[stream] + for (exch, stream) in m.ohexch + ) + ) * m.heat_unit_match == m.qexch[exch] + + b.exchhbh = Constraint( + [exchanger], rule=Exchhbh, doc='heat balance for hot stream' + ) def Exchdtm1(_m, exch): - return sum(m.t[stream] for (exch, stream) in m.ohexch) >= sum(m.t[stream] for (exch, stream) in m.icexch) + m.exchanger_temp_drop - b.exchdtm1 = Constraint( - [exchanger], rule=Exchdtm1, doc='delta t min condition') + return ( + sum(m.t[stream] for (exch, stream) in m.ohexch) + >= sum(m.t[stream] for (exch, stream) in m.icexch) + + m.exchanger_temp_drop + ) + + b.exchdtm1 = Constraint([exchanger], rule=Exchdtm1, doc='delta t min condition') def Exchdtm2(_m, exch): - return sum(m.t[stream] for (exch, stream) in m.ocexch) <= sum(m.t[stream] for (exch, stream) in m.ihexch) - m.exchanger_temp_drop - b.exchdtm2 = Constraint( - [exchanger], rule=Exchdtm2, doc='delta t min condition') + return ( + sum(m.t[stream] for (exch, stream) in m.ocexch) + <= sum(m.t[stream] for (exch, stream) in m.ihexch) + - m.exchanger_temp_drop + ) + + b.exchdtm2 = Constraint([exchanger], rule=Exchdtm2, doc='delta t min condition') def Exchpc(_m, exch): - return sum(m.p[stream] for (exch, stream) in m.ocexch) == sum(m.p[stream] for (exch, stream) in m.icexch) - b.exchpc = Constraint([exchanger], rule=Exchpc, - doc='pressure relation (cold)') + return sum(m.p[stream] for (exch, stream) in m.ocexch) == sum( + m.p[stream] for (exch, stream) in m.icexch + ) + + b.exchpc = Constraint([exchanger], rule=Exchpc, doc='pressure relation (cold)') def Exchph(_m, exch): - return sum(m.p[stream] for (exch, stream) in m.ohexch) == sum(m.p[stream] for (exch, stream) in m.ihexch) - b.exchph = Constraint([exchanger], rule=Exchph, - doc='pressure relation (hot)') + return sum(m.p[stream] for (exch, stream) in m.ohexch) == sum( + m.p[stream] for (exch, stream) in m.ihexch + ) + + b.exchph = Constraint([exchanger], rule=Exchph, doc='pressure relation (hot)') - m.membrane_recovery_sepc = Param(initialize=0.50) m.membrane_purity_sepc = Param(initialize=0.50) def build_membrane(b, membrane): def Memcmb(_m, memb, stream, compon): if (memb, stream) in m.imemb and memb == membrane: - return m.fc[stream, compon] == sum(m.fc[stream, compon] for (memb_, stream) in m.pmemb if memb == memb_) + sum(m.fc[stream, compon] for (memb_, stream) in m.nmemb if memb == memb_) + return m.fc[stream, compon] == sum( + m.fc[stream, compon] for (memb_, stream) in m.pmemb if memb == memb_ + ) + sum( + m.fc[stream, compon] for (memb_, stream) in m.nmemb if memb == memb_ + ) return Constraint.Skip - b.memcmb = Constraint([membrane], m.str, m.compon, - rule=Memcmb, doc='component mass balance') + + b.memcmb = Constraint( + [membrane], m.str, m.compon, rule=Memcmb, doc='component mass balance' + ) def Flux(_m, memb, stream, compon): - if (memb, stream) in m.pmemb and (memb, compon) in m.mnorm and memb == membrane: - return m.fc[stream, compon] == m.a[memb] * m.perm[compon] / 2.0 * (sum(m.p[stream2] for (memb_, stream2) in m.imemb if memb_ == memb) * (sum((m.fc[stream2, compon] + m.eps1)/(m.f[stream2] + m.eps1) for (memb_, stream2) in m.imemb if memb_ == memb) + sum((m.fc[stream2, compon] + m.eps1)/(m.f[stream2] + m.eps1) for (memb_, stream2) in m.nmemb if memb_ == memb)) - 2.0 * m.p[stream] * (m.fc[stream, compon] + m.eps1) / (m.f[stream] + m.eps1)) + if ( + (memb, stream) in m.pmemb + and (memb, compon) in m.mnorm + and memb == membrane + ): + return m.fc[stream, compon] == m.a[memb] * m.perm[compon] / 2.0 * ( + sum(m.p[stream2] for (memb_, stream2) in m.imemb if memb_ == memb) + * ( + sum( + (m.fc[stream2, compon] + m.eps1) / (m.f[stream2] + m.eps1) + for (memb_, stream2) in m.imemb + if memb_ == memb + ) + + sum( + (m.fc[stream2, compon] + m.eps1) / (m.f[stream2] + m.eps1) + for (memb_, stream2) in m.nmemb + if memb_ == memb + ) + ) + - 2.0 + * m.p[stream] + * (m.fc[stream, compon] + m.eps1) + / (m.f[stream] + m.eps1) + ) return Constraint.Skip - b.flux = Constraint([membrane], m.str, m.compon, - rule=Flux, doc='mass flux relation') + + b.flux = Constraint( + [membrane], m.str, m.compon, rule=Flux, doc='mass flux relation' + ) def Simp(_m, memb, stream, compon): - if (memb, stream) in m.pmemb and (memb, compon) in m.msimp and memb == membrane: + if ( + (memb, stream) in m.pmemb + and (memb, compon) in m.msimp + and memb == membrane + ): return m.fc[stream, compon] == 0.0 return Constraint.Skip - b.simp = Constraint([membrane], m.str, m.compon, - rule=Simp, doc='mass flux relation (simplified)') + + b.simp = Constraint( + [membrane], + m.str, + m.compon, + rule=Simp, + doc='mass flux relation (simplified)', + ) def Memtp(_m, memb, stream): if (memb, stream) in m.pmemb and memb == membrane: - return m.t[stream] == sum(m.t[stream2] for (memb, stream2) in m.imemb if memb == membrane) + return m.t[stream] == sum( + m.t[stream2] for (memb, stream2) in m.imemb if memb == membrane + ) return Constraint.Skip - b.memtp = Constraint([membrane], m.str, rule=Memtp, - doc='temp relation for permeate') + + b.memtp = Constraint( + [membrane], m.str, rule=Memtp, doc='temp relation for permeate' + ) def Mempp(_m, memb, stream): if (memb, stream) in m.pmemb and memb == membrane: - return m.p[stream] <= sum(m.p[stream2] for (memb, stream2) in m.imemb if memb == membrane) + return m.p[stream] <= sum( + m.p[stream2] for (memb, stream2) in m.imemb if memb == membrane + ) return Constraint.Skip - b.mempp = Constraint([membrane], m.str, rule=Mempp, - doc='pressure relation for permeate') + + b.mempp = Constraint( + [membrane], m.str, rule=Mempp, doc='pressure relation for permeate' + ) def Memtn(_m, memb, stream): if (memb, stream) in m.nmemb and memb == membrane: - return m.t[stream] == sum(m.t[stream2] for (memb, stream2) in m.imemb if memb == membrane) + return m.t[stream] == sum( + m.t[stream2] for (memb, stream2) in m.imemb if memb == membrane + ) return Constraint.Skip - b.Memtn = Constraint([membrane], m.str, rule=Memtn, - doc='temp relation for non-permeate') + + b.Memtn = Constraint( + [membrane], m.str, rule=Memtn, doc='temp relation for non-permeate' + ) def Mempn(_m, memb, stream): if (memb, stream) in m.nmemb and memb == membrane: - return m.p[stream] == sum(m.p[stream] for (memb_, stream) in m.imemb if memb_ == memb) + return m.p[stream] == sum( + m.p[stream] for (memb_, stream) in m.imemb if memb_ == memb + ) return Constraint.Skip - b.Mempn = Constraint([membrane], m.str, rule=Mempn, - doc='pressure relation for non-permeate') + + b.Mempn = Constraint( + [membrane], m.str, rule=Mempn, doc='pressure relation for non-permeate' + ) def Rec(_m, memb_, stream): if (memb_, stream) in m.pmemb and memb_ == membrane: - return m.fc[stream, 'h2'] >= m.membrane_recovery_sepc * sum(m.fc[stream, 'h2'] for (memb, stream) in m.imemb if memb == memb_) + return m.fc[stream, 'h2'] >= m.membrane_recovery_sepc * sum( + m.fc[stream, 'h2'] for (memb, stream) in m.imemb if memb == memb_ + ) return Constraint.Skip + b.rec = Constraint([membrane], m.str, rule=Rec, doc='recovery spec') def Pure(_m, memb, stream): if (memb, stream) in m.pmemb and memb == membrane: return m.fc[stream, 'h2'] >= m.membrane_purity_sepc * m.f[stream] return Constraint.Skip - b.pure = Constraint([membrane], m.str, rule=Pure, doc='purity spec') - + b.pure = Constraint([membrane], m.str, rule=Pure, doc='purity spec') def build_multiple_mixer(b, multiple_mxr): def Mxrcmb(_b, mxr, compon): if mxr == multiple_mxr: - return sum(m.fc[stream, compon] for (mxr_, stream) in m.omxr if mxr == mxr_) == sum(m.fc[stream, compon] for (mxr_, stream) in m.imxr if mxr == mxr_) + return sum( + m.fc[stream, compon] for (mxr_, stream) in m.omxr if mxr == mxr_ + ) == sum( + m.fc[stream, compon] for (mxr_, stream) in m.imxr if mxr == mxr_ + ) return Constraint.Skip - b.mxrcmb = Constraint([multiple_mxr], m.compon, - rule=Mxrcmb, doc='component balance in mixer') + + b.mxrcmb = Constraint( + [multiple_mxr], m.compon, rule=Mxrcmb, doc='component balance in mixer' + ) def Mxrhb(_b, mxr): if mxr == multiple_mxr and mxr != 2: - return sum(m.f[stream] * m.t[stream] * m.cp[stream] for (mxr_, stream) in m.imxr if mxr == mxr_) == sum(m.f[stream] * m.t[stream] * m.cp[stream] for (mxr_, stream) in m.omxr if mxr == mxr_) + return sum( + m.f[stream] * m.t[stream] * m.cp[stream] + for (mxr_, stream) in m.imxr + if mxr == mxr_ + ) == sum( + m.f[stream] * m.t[stream] * m.cp[stream] + for (mxr_, stream) in m.omxr + if mxr == mxr_ + ) return Constraint.Skip - b.mxrhb = Constraint([multiple_mxr], rule=Mxrhb, - doc="heat balance in mixer") + + b.mxrhb = Constraint([multiple_mxr], rule=Mxrhb, doc="heat balance in mixer") def Mxrhbq(_b, mxr): if mxr == 2 and mxr == multiple_mxr: - return m.f[16] * m.t[16] == m.f[15] * m.t[15] - (m.fc[20, 'ben'] + m.fc[20, 'tol']) * m.heatvap['tol'] / (100. * m.cp[15]) + return m.f[16] * m.t[16] == m.f[15] * m.t[15] - ( + m.fc[20, 'ben'] + m.fc[20, 'tol'] + ) * m.heatvap['tol'] / (100.0 * m.cp[15]) return Constraint.Skip - b.mxrhbq = Constraint([multiple_mxr], rule=Mxrhbq, - doc=' heat balance in quench') + + b.mxrhbq = Constraint( + [multiple_mxr], rule=Mxrhbq, doc=' heat balance in quench' + ) def Mxrpi(_b, mxr, stream): if (mxr, stream) in m.imxr and mxr == multiple_mxr: return m.mxrp[mxr] == m.p[stream] return Constraint.Skip - b.mxrpi = Constraint([multiple_mxr], m.str, - rule=Mxrpi, doc='inlet pressure relation') + + b.mxrpi = Constraint( + [multiple_mxr], m.str, rule=Mxrpi, doc='inlet pressure relation' + ) def Mxrpo(_b, mxr, stream): if (mxr, stream) in m.omxr and mxr == multiple_mxr: return m.mxrp[mxr] == m.p[stream] return Constraint.Skip - b.mxrpo = Constraint([multiple_mxr], m.str, - rule=Mxrpo, doc='outlet pressure relation') - + b.mxrpo = Constraint( + [multiple_mxr], m.str, rule=Mxrpo, doc='outlet pressure relation' + ) def build_pump(b, pump_): def Pumpcmb(_m, pump, compon): if pump == pump_ and compon in m.compon: - return sum(m.fc[stream, compon] for (pump_, stream) in m.opump if pump == pump_) == sum(m.fc[stream, compon] for (pump_, stream) in m.ipump if pump_ == pump) + return sum( + m.fc[stream, compon] for (pump_, stream) in m.opump if pump == pump_ + ) == sum( + m.fc[stream, compon] for (pump_, stream) in m.ipump if pump_ == pump + ) return Constraint.Skip - b.pumpcmb = Constraint( - [pump_], m.compon, rule=Pumpcmb, doc='component balance') + + b.pumpcmb = Constraint([pump_], m.compon, rule=Pumpcmb, doc='component balance') def Pumphb(_m, pump): if pump == pump_: - return sum(m.t[stream] for (pump_, stream) in m.opump if pump == pump_) == sum(m.t[stream] for (pump_, stream) in m.ipump if pump == pump_) + return sum( + m.t[stream] for (pump_, stream) in m.opump if pump == pump_ + ) == sum(m.t[stream] for (pump_, stream) in m.ipump if pump == pump_) return Constraint.Skip + b.pumphb = Constraint([pump_], rule=Pumphb, doc='heat balance') def Pumppr(_m, pump): if pump == pump_: - return sum(m.p[stream] for (pump_, stream) in m.opump if pump == pump_) >= sum(m.p[stream] for (pump_, stream) in m.ipump if pump == pump_) + return sum( + m.p[stream] for (pump_, stream) in m.opump if pump == pump_ + ) >= sum(m.p[stream] for (pump_, stream) in m.ipump if pump == pump_) return Constraint.Skip - b.pumppr = Constraint([pump_], rule=Pumppr, doc='pressure relation') - + b.pumppr = Constraint([pump_], rule=Pumppr, doc='pressure relation') def build_multiple_splitter(b, multi_splitter): def Splcmb(_m, spl, stream, compon): if (spl, stream) in m.ospl and spl == multi_splitter: - return m.fc[stream, compon] == sum(m.e[stream]*m.fc[str2, compon] for (spl_, str2) in m.ispl if spl == spl_) + return m.fc[stream, compon] == sum( + m.e[stream] * m.fc[str2, compon] + for (spl_, str2) in m.ispl + if spl == spl_ + ) return Constraint.Skip - b.splcmb = Constraint([multi_splitter], m.str, m.compon, - rule=Splcmb, doc='component balance in splitter') + + b.splcmb = Constraint( + [multi_splitter], + m.str, + m.compon, + rule=Splcmb, + doc='component balance in splitter', + ) def Esum(_m, spl): if spl in m.spl and spl == multi_splitter: - return sum(m.e[stream] for (spl_, stream) in m.ospl if spl_ == spl) == 1.0 + return ( + sum(m.e[stream] for (spl_, stream) in m.ospl if spl_ == spl) == 1.0 + ) return Constraint.Skip - b.esum = Constraint([multi_splitter], rule=Esum, - doc='split fraction relation') + + b.esum = Constraint([multi_splitter], rule=Esum, doc='split fraction relation') def Splpi(_m, spl, stream): if (spl, stream) in m.ispl and spl == multi_splitter: return m.splp[spl] == m.p[stream] return Constraint.Skip - b.splpi = Constraint([multi_splitter], m.str, - rule=Splpi, doc='inlet pressure relation') + + b.splpi = Constraint( + [multi_splitter], m.str, rule=Splpi, doc='inlet pressure relation' + ) def Splpo(_m, spl, stream): if (spl, stream) in m.ospl and spl == multi_splitter: return m.splp[spl] == m.p[stream] return Constraint.Skip - b.splpo = Constraint([multi_splitter], m.str, - rule=Splpo, doc='outlet pressure relation') + + b.splpo = Constraint( + [multi_splitter], m.str, rule=Splpo, doc='outlet pressure relation' + ) def Splti(_m, spl, stream): if (spl, stream) in m.ispl and spl == multi_splitter: return m.splt[spl] == m.t[stream] return Constraint.Skip - b.splti = Constraint([multi_splitter], m.str, - rule=Splti, doc='inlet temperature relation') + + b.splti = Constraint( + [multi_splitter], m.str, rule=Splti, doc='inlet temperature relation' + ) def Splto(_m, spl, stream): if (spl, stream) in m.ospl and spl == multi_splitter: return m.splt[spl] == m.t[stream] return Constraint.Skip - b.splto = Constraint([multi_splitter], m.str, - rule=Splto, doc='outlet temperature relation') - + b.splto = Constraint( + [multi_splitter], m.str, rule=Splto, doc='outlet temperature relation' + ) def build_valve(b, valve_): def Valcmb(_m, valve, compon): - return sum(m.fc[stream, compon] for (valve_, stream) in m.oval if valve == valve_) == sum(m.fc[stream, compon] for (valve_, stream) in m.ival if valve == valve_) + return sum( + m.fc[stream, compon] for (valve_, stream) in m.oval if valve == valve_ + ) == sum( + m.fc[stream, compon] for (valve_, stream) in m.ival if valve == valve_ + ) + b.valcmb = Constraint([valve_], m.compon, rule=Valcmb, doc='valcmb') def Valt(_m, valve): - return sum(m.t[stream] / (m.p[stream] ** ((m.gam - 1.) / m.gam)) for (valv, stream) in m.oval if valv == valve) == sum(m.t[stream] / (m.p[stream] ** ((m.gam - 1.) / m.gam)) for (valv, stream) in m.ival if valv == valve) + return sum( + m.t[stream] / (m.p[stream] ** ((m.gam - 1.0) / m.gam)) + for (valv, stream) in m.oval + if valv == valve + ) == sum( + m.t[stream] / (m.p[stream] ** ((m.gam - 1.0) / m.gam)) + for (valv, stream) in m.ival + if valv == valve + ) + b.valt = Constraint([valve_], rule=Valt, doc='temperature relation') def Valp(_m, valve): - return sum(m.p[stream] for (valv, stream) in m.oval if valv == valve) <= sum(m.p[stream] for (valv, stream) in m.ival if valv == valve) - b.valp = Constraint([valve_], rule=Valp, doc='pressure relation') + return sum( + m.p[stream] for (valv, stream) in m.oval if valv == valve + ) <= sum(m.p[stream] for (valv, stream) in m.ival if valv == valve) + b.valp = Constraint([valve_], rule=Valp, doc='pressure relation') m.Prereference_factor = Param( - initialize=6.3e+10, doc="Pre-reference factor for reaction rate constant") - m.Ea_R = Param(initialize=-26167.) + initialize=6.3e10, doc="Pre-reference factor for reaction rate constant" + ) + m.Ea_R = Param(initialize=-26167.0) m.pressure_drop = Param(initialize=0.20684) m.selectivity_1 = Param(initialize=0.0036) m.selectivity_2 = Param(initialize=-1.544) @@ -1418,150 +2278,250 @@ def Valp(_m, valve): def build_reactor(b, rct): def rctspec(_m, rct, stream): if (rct, stream) in m.irct: - return m.fc[stream, 'h2'] >= 5 * (m.fc[stream, 'ben'] + m.fc[stream, 'tol'] + m.fc[stream, 'dip']) + return m.fc[stream, 'h2'] >= 5 * ( + m.fc[stream, 'ben'] + m.fc[stream, 'tol'] + m.fc[stream, 'dip'] + ) return Constraint.Skip - b.Rctspec = Constraint([rct], m.str, rule=rctspec, - doc='spec. on reactor feed stream') + + b.Rctspec = Constraint( + [rct], m.str, rule=rctspec, doc='spec. on reactor feed stream' + ) def rxnrate(_m, rct): - return m.krct[rct] == m.Prereference_factor * exp(m.Ea_R / (m.rctt[rct] * 100.)) - b.Rxnrate = Constraint([rct], rule=rxnrate, - doc='reaction rate constant') + return m.krct[rct] == m.Prereference_factor * exp( + m.Ea_R / (m.rctt[rct] * 100.0) + ) + + b.Rxnrate = Constraint([rct], rule=rxnrate, doc='reaction rate constant') def rctconv(_m, rct, stream, compon): if (rct, compon) in m.rkey and (rct, stream) in m.irct: - return 1. - m.conv[rct, compon] == (1. / (1. + m.conversion_coefficient * m.krct[rct] * m.rctvol[rct] * sqrt(m.fc[stream, compon] / 60 + m.eps1) * (m.f[stream] / 60. + m.eps1) ** (-3./2.))) ** 2. + return ( + 1.0 - m.conv[rct, compon] + == ( + 1.0 + / ( + 1.0 + + m.conversion_coefficient + * m.krct[rct] + * m.rctvol[rct] + * sqrt(m.fc[stream, compon] / 60 + m.eps1) + * (m.f[stream] / 60.0 + m.eps1) ** (-3.0 / 2.0) + ) + ) + ** 2.0 + ) return Constraint.Skip - b.Rctconv = Constraint([rct], m.str, m.compon, - rule=rctconv, doc="conversion of key component") + + b.Rctconv = Constraint( + [rct], m.str, m.compon, rule=rctconv, doc="conversion of key component" + ) def rctsel(_m, rct): - return (1. - m.sel[rct]) == m.selectivity_1 * (1. - m.conv[rct, 'tol']) ** m.selectivity_2 - b.Rctsel = Constraint([rct], rule=rctsel, - doc=' selectivity to benzene') + return (1.0 - m.sel[rct]) == m.selectivity_1 * ( + 1.0 - m.conv[rct, 'tol'] + ) ** m.selectivity_2 + + b.Rctsel = Constraint([rct], rule=rctsel, doc=' selectivity to benzene') def rctcns(_m, rct, stream, compon): if (rct, compon) in m.rkey and (rct, stream) in m.irct: - return m.consum[rct, compon] == m.conv[rct, compon] * m.fc[stream, compon] + return ( + m.consum[rct, compon] == m.conv[rct, compon] * m.fc[stream, compon] + ) return Constraint.Skip - b.Rctcns = Constraint([rct], m.str, m.compon, - rule=rctcns, doc='consumption rate of key comp.') + + b.Rctcns = Constraint( + [rct], m.str, m.compon, rule=rctcns, doc='consumption rate of key comp.' + ) def rctmbtol(_m, rct): - return sum(m.fc[stream, 'tol'] for (rct_, stream) in m.orct if rct_ == rct) == sum(m.fc[stream, 'tol'] for (rct_, stream) in m.irct if rct_ == rct) - m.consum[rct, 'tol'] - b.Rctmbtol = Constraint([rct], rule=rctmbtol, - doc='mass balance in reactor (tol)') + return ( + sum(m.fc[stream, 'tol'] for (rct_, stream) in m.orct if rct_ == rct) + == sum(m.fc[stream, 'tol'] for (rct_, stream) in m.irct if rct_ == rct) + - m.consum[rct, 'tol'] + ) + + b.Rctmbtol = Constraint( + [rct], rule=rctmbtol, doc='mass balance in reactor (tol)' + ) def rctmbben(_m, rct): - return sum(m.fc[stream, 'ben'] for (rct_, stream) in m.orct if rct_ == rct) == sum(m.fc[stream, 'ben'] for (rct_, stream) in m.irct if rct_ == rct) + m.consum[rct, 'tol'] * m.sel[rct] + return ( + sum(m.fc[stream, 'ben'] for (rct_, stream) in m.orct if rct_ == rct) + == sum(m.fc[stream, 'ben'] for (rct_, stream) in m.irct if rct_ == rct) + + m.consum[rct, 'tol'] * m.sel[rct] + ) + b.Rctmbben = Constraint([rct], rule=rctmbben) def rctmbdip(_m, rct): - return sum(m.fc[stream, 'dip'] for (rct1, stream) in m.orct if rct1 == rct) == sum(m.fc[stream, 'dip'] for (rct1, stream) in m.irct if rct1 == rct) + m.consum[rct, 'tol'] * 0.5 + (sum(m.fc[stream, 'ben'] for (rct1, stream) in m.irct if rct1 == rct) - sum(m.fc[stream, 'ben'] for (rct1, stream) in m.orct if rct1 == rct)) * 0.5 + return ( + sum(m.fc[stream, 'dip'] for (rct1, stream) in m.orct if rct1 == rct) + == sum(m.fc[stream, 'dip'] for (rct1, stream) in m.irct if rct1 == rct) + + m.consum[rct, 'tol'] * 0.5 + + ( + sum(m.fc[stream, 'ben'] for (rct1, stream) in m.irct if rct1 == rct) + - sum( + m.fc[stream, 'ben'] for (rct1, stream) in m.orct if rct1 == rct + ) + ) + * 0.5 + ) + b.Rctmbdip = Constraint([rct], rule=rctmbdip) def rctmbh2(_m, rct): - return sum(m.fc[stream, 'h2'] for (rct1, stream) in m.orct if rct1 == rct) == sum(m.fc[stream, 'h2'] for (rct1, stream) in m.irct if rct1 == rct) - m.consum[rct, 'tol'] - sum(m.fc[stream, 'dip'] for (rct1, stream) in m.irct if rct1 == rct) + sum(m.fc[stream, 'dip'] for (rct1, stream) in m.orct if rct1 == rct) + return sum( + m.fc[stream, 'h2'] for (rct1, stream) in m.orct if rct1 == rct + ) == sum( + m.fc[stream, 'h2'] for (rct1, stream) in m.irct if rct1 == rct + ) - m.consum[ + rct, 'tol' + ] - sum( + m.fc[stream, 'dip'] for (rct1, stream) in m.irct if rct1 == rct + ) + sum( + m.fc[stream, 'dip'] for (rct1, stream) in m.orct if rct1 == rct + ) + b.Rctmbh2 = Constraint([rct], rule=rctmbh2) def rctpi(_m, rct, stream): if (rct, stream) in m.irct: return m.rctp[rct] == m.p[stream] return Constraint.Skip - b.Rctpi = Constraint([rct], m.str, rule=rctpi, - doc='inlet pressure relation') + + b.Rctpi = Constraint([rct], m.str, rule=rctpi, doc='inlet pressure relation') def rctpo(_m, rct, stream): if (rct, stream) in m.orct: return m.rctp[rct] - m.pressure_drop == m.p[stream] return Constraint.Skip - b.Rctpo = Constraint([rct], m.str, rule=rctpo, - doc='outlet pressure relation') + + b.Rctpo = Constraint([rct], m.str, rule=rctpo, doc='outlet pressure relation') def rcttave(_m, rct): - return m.rctt[rct] == (sum(m.t[stream] for (rct1, stream) in m.irct if rct1 == rct) + sum(m.t[stream] for (rct1, stream) in m.orct if rct1 == rct))/2 - b.Rcttave = Constraint([rct], rule=rcttave, - doc='average temperature relation ') + return ( + m.rctt[rct] + == ( + sum(m.t[stream] for (rct1, stream) in m.irct if rct1 == rct) + + sum(m.t[stream] for (rct1, stream) in m.orct if rct1 == rct) + ) + / 2 + ) + + b.Rcttave = Constraint([rct], rule=rcttave, doc='average temperature relation ') def Rctmbch4(_m, rct): - return sum(m.fc[stream, 'ch4'] for (rct_, stream) in m.orct if rct_ == rct) == sum(m.fc[stream, 'ch4'] for (rct_, stream) in m.irct if rct == rct_) + m.consum[rct, 'tol'] - b.rctmbch4 = Constraint([rct], rule=Rctmbch4, - doc='mass balance in reactor (ch4)') + return ( + sum(m.fc[stream, 'ch4'] for (rct_, stream) in m.orct if rct_ == rct) + == sum(m.fc[stream, 'ch4'] for (rct_, stream) in m.irct if rct == rct_) + + m.consum[rct, 'tol'] + ) + + b.rctmbch4 = Constraint( + [rct], rule=Rctmbch4, doc='mass balance in reactor (ch4)' + ) def Rcthbadb(_m, rct): if rct == 1: - return m.heatrxn[rct] * m.consum[rct, 'tol'] / 100. == sum(m.cp[stream] * m.f[stream] * m.t[stream] for (rct_, stream) in m.orct if rct_ == rct) - sum(m.cp[stream] * m.f[stream] * m.t[stream] for (rct_, stream) in m.irct if rct_ == rct) + return m.heatrxn[rct] * m.consum[rct, 'tol'] / 100.0 == sum( + m.cp[stream] * m.f[stream] * m.t[stream] + for (rct_, stream) in m.orct + if rct_ == rct + ) - sum( + m.cp[stream] * m.f[stream] * m.t[stream] + for (rct_, stream) in m.irct + if rct_ == rct + ) return Constraint.Skip - b.rcthbadb = Constraint([rct], rule=Rcthbadb, - doc='heat balance (adiabatic)') + + b.rcthbadb = Constraint([rct], rule=Rcthbadb, doc='heat balance (adiabatic)') def Rcthbiso(_m, rct): if rct == 2: - return m.heatrxn[rct] * m.consum[rct, 'tol'] * 60. * 8500 * 1.0e-09 == m.q[rct] + return ( + m.heatrxn[rct] * m.consum[rct, 'tol'] * 60.0 * 8500 * 1.0e-09 + == m.q[rct] + ) return Constraint.Skip - b.rcthbiso = Constraint([rct], rule=Rcthbiso, - doc='temp relation (isothermal)') + + b.rcthbiso = Constraint([rct], rule=Rcthbiso, doc='temp relation (isothermal)') def Rctisot(_m, rct): if rct == 2: - return sum(m.t[stream] for (rct_, stream) in m.irct if rct_ == rct) == sum(m.t[stream] for (rct_, stream) in m.orct if rct_ == rct) + return sum( + m.t[stream] for (rct_, stream) in m.irct if rct_ == rct + ) == sum(m.t[stream] for (rct_, stream) in m.orct if rct_ == rct) return Constraint.Skip - b.rctisot = Constraint([rct], rule=Rctisot, - doc='temp relation (isothermal)') - + b.rctisot = Constraint([rct], rule=Rctisot, doc='temp relation (isothermal)') def build_single_mixer(b, mixer): def Mxr1cmb(m_, mxr1, str1, compon): if (mxr1, str1) in m.omxr1 and mxr1 == mixer: - return m.fc[str1, compon] == sum(m.fc[str2, compon] for (mxr1_, str2) in m.imxr1 if mxr1_ == mxr1) + return m.fc[str1, compon] == sum( + m.fc[str2, compon] for (mxr1_, str2) in m.imxr1 if mxr1_ == mxr1 + ) return Constraint.Skip - b.mxr1cmb = Constraint([mixer], m.str, m.compon, - rule=Mxr1cmb, doc='component balance in mixer') - m.single_mixer = Block(m.mxr1, rule=build_single_mixer) - + b.mxr1cmb = Constraint( + [mixer], m.str, m.compon, rule=Mxr1cmb, doc='component balance in mixer' + ) + + m.single_mixer = Block(m.mxr1, rule=build_single_mixer) # single output splitter def build_single_splitter(b, splitter): def Spl1cmb(m_, spl1, compon): - return sum(m.fc[str1, compon] for (spl1_, str1) in m.ospl1 if spl1_ == spl1) == sum(m.fc[str1, compon] for (spl1_, str1) in m.ispl1 if spl1_ == spl1) + return sum( + m.fc[str1, compon] for (spl1_, str1) in m.ospl1 if spl1_ == spl1 + ) == sum(m.fc[str1, compon] for (spl1_, str1) in m.ispl1 if spl1_ == spl1) + b.spl1cmb = Constraint( - [splitter], m.compon, rule=Spl1cmb, doc='component balance in splitter') + [splitter], m.compon, rule=Spl1cmb, doc='component balance in splitter' + ) def Spl1pi(m_, spl1, str1): if (spl1, str1) in m.ispl1: return m.spl1p[spl1] == m.p[str1] return Constraint.Skip - b.spl1pi = Constraint([splitter], m.str, rule=Spl1pi, - doc='inlet pressure relation') + + b.spl1pi = Constraint( + [splitter], m.str, rule=Spl1pi, doc='inlet pressure relation' + ) def Spl1po(m_, spl1, str1): if (spl1, str1) in m.ospl1: return m.spl1p[spl1] == m.p[str1] return Constraint.Skip - b.spl1po = Constraint([splitter], m.str, rule=Spl1po, - doc='outlet pressure relation') + + b.spl1po = Constraint( + [splitter], m.str, rule=Spl1po, doc='outlet pressure relation' + ) def Spl1ti(m_, spl1, str1): if (spl1, str1) in m.ispl1: return m.spl1t[spl1] == m.t[str1] return Constraint.Skip - b.spl1ti = Constraint([splitter], m.str, rule=Spl1ti, - doc='inlet temperature relation') + + b.spl1ti = Constraint( + [splitter], m.str, rule=Spl1ti, doc='inlet temperature relation' + ) def Spl1to(m_, spl1, str1): if (spl1, str1) in m.ospl1: return m.spl1t[spl1] == m.t[str1] return Constraint.Skip - b.spl1to = Constraint([splitter], m.str, rule=Spl1to, - doc='outlet temperature relation') + + b.spl1to = Constraint( + [splitter], m.str, rule=Spl1to, doc='outlet temperature relation' + ) + m.single_splitter = Block(m.spl1, rule=build_single_splitter) # ## GDP formulation - - m.one = Set(initialize=[1]) m.two = Set(initialize=[2]) m.three = Set(initialize=[3]) @@ -1569,7 +2529,6 @@ def Spl1to(m_, spl1, str1): m.five = Set(initialize=[5]) m.six = Set(initialize=[6]) - # first disjunction: Purify H2 inlet or not @m.Disjunct() def purify_H2(disj): @@ -1590,7 +2549,7 @@ def no_purify_H2(disj): @m.Disjunction() def inlet_treatment(m): - return[m.purify_H2, m.no_purify_H2] + return [m.purify_H2, m.no_purify_H2] m.multi_mixer_1 = Block(m.one, rule=build_multiple_mixer) m.furnace_1 = Block(m.one, rule=build_furnace) @@ -1615,9 +2574,7 @@ def isothermal_reactor(disj): @m.Disjunction() def reactor_selection(m): - return[m.adiabatic_reactor, m.isothermal_reactor] - - + return [m.adiabatic_reactor, m.isothermal_reactor] m.valve_3 = Block(m.three, rule=build_valve) m.multi_mixer_2 = Block(m.two, rule=build_multiple_mixer) @@ -1626,7 +2583,7 @@ def reactor_selection(m): m.flash_1 = Block(m.one, rule=build_flash) m.multi_splitter_2 = Block(m.two, rule=build_multiple_splitter) - # thrid disjunction: recycle methane with membrane or purge it + # third disjunction: recycle methane with membrane or purge it @m.Disjunct() def recycle_methane_purge(disj): disj.no_flow_54 = Constraint(expr=m.f[54] == 0) @@ -1641,11 +2598,10 @@ def recycle_methane_membrane(disj): disj.compressor_4 = Block(m.four, rule=build_compressor) @m.Disjunction() - def methane_treatmet(m): - return[m.recycle_methane_purge, m.recycle_methane_membrane] + def methane_treatments(m): + return [m.recycle_methane_purge, m.recycle_methane_membrane] - - # fourth disjunction: recycle hydrogen with absorber or not + # fourth disjunction: recycle hydrogen with absorber or not @m.Disjunct() def recycle_hydrogen(disj): disj.no_flow_61 = Constraint(expr=m.f[61] == 0) @@ -1676,16 +2632,11 @@ def absorber_hydrogen(disj): def recycle_selection(m): return [m.recycle_hydrogen, m.absorber_hydrogen] - - m.multi_mixer_5 = Block(m.five, rule=build_multiple_mixer) - - m.multi_mixer_3 = Block(m.three, rule=build_multiple_mixer) m.multi_splitter_1 = Block(m.one, rule=build_multiple_splitter) - # fifth disjunction: methane stabilizing selection @m.Disjunct() def methane_distillation_column(disj): @@ -1728,11 +2679,8 @@ def methane_flash_separation(disj): def H2_selection(m): return [m.methane_distillation_column, m.methane_flash_separation] - - m.benzene_column = Block(m.two, rule=build_distillation) - # sixth disjunction: toluene stabilizing selection @m.Disjunct() def toluene_distillation_column(disj): @@ -1760,52 +2708,177 @@ def toluene_flash_separation(disj): def toluene_selection(m): return [m.toluene_distillation_column, m.toluene_flash_separation] - - m.pump_1 = Block(m.one, rule=build_pump) m.abound = Constraint(expr=m.a[1] >= 0.0) # ## objective function - m.hydrogen_purge_value = Param(initialize = 1.08,doc = "heating value of hydrogen purge") - m.electricity_cost = Param(initialize = 0.04 * 24 * 365 / 1000 , doc ="electricity cost, value is 0.04 with the unit of kw/h, now is kw/yr/k$") - m.meathane_purge_value = Param(initialize = 3.37, doc = "heating value of meathane purge") - m.heating_cost = Param(initialize = 8000., doc = "Heating cost(steam) with unit 1e6 KJ") - m.cooling_cost = Param(initialize = 700.0, doc = "heating cost (water) with unit 1e6 KJ") - m.fuel_cost = Param(initialize = 4000.0 ,doc = "fuel cost with unit 1e6 KJ") - m.abs_fixed_cost = Param(initialize = 13, doc = "fixed cost of absober ($1e3 per year)") - m.abs_linear_coeffcient = Param(initialize = 1.2, doc = "linear coeffcient of absorber (times tray number) ($1e3 per year)") - m.compressor_fixed_cost = Param(initialize = 7.155, doc = "compressor fixed cost ($1e3 per year)") - m.compressor_fixed_cost_4 = Param(initialize = 4.866, doc = "compressor fixed cost for compressor 4 ($1e3 per year)") - m.compressor_linear_coeffcient = Param(initialize = 0.815 ,doc = "compressor linear coeffcient (vaporflow rate) ($1e3 per year)") - m.compressor_linear_coeffcient_4 = Param(initialize = 0.887 ,doc = "compressor linear coeffcient (vaporflow rate) ($1e3 per year)") - m.stabilizing_column_fixed_cost = Param(initialize = 1.126 ,doc = "stabilizing column fixed cost ($1e3 per year)") - m.stabilizing_column_linear_coeffcient = Param(initialize = 0.375 ,doc = "stabilizing column linear coeffcient (times number of trays) ($1e3 per year)") - m.benzene_column_fixed_cost = Param(initialize = 16.3 ,doc = "benzene column fixed cost ($1e3 per year)") - m.benzene_column_linear_coeffcient = Param(initialize = 1.55 ,doc = "benzene column linear coeffcient (times number of trays) ($1e3 per year)") - m.toluene_column_fixed_cost = Param(initialize = 3.9 ,doc = "toluene column fixed cost ($1e3 per year)") - m.toluene_column_linear_coeffcient = Param(initialize = 1.12 ,doc = "toluene column linear coeffcient (times number of trays) ($1e3 per year)") - m.furnace_fixed_cost = Param(initialize = 6.20 ,doc = "toluene column fixed cost ($1e3 per year)") - m.furnace_linear_coeffcient = Param(initialize = 1171.7 ,doc = "furnace column linear coeffcient (1e9KJ/yr) ($1e3 per year)") - m.membrane_seperator_fixed_cost = Param(initialize = 43.24 ,doc = "membrane seperator fixed cost ($1e3 per year)") - m.membrane_seperator_linear_coeffcient = Param(initialize = 49.0 ,doc = "furnace column linear coeffcient (times inlet flowrate) ($1e3 per year)") - m.adiabtic_reactor_fixed_cost = Param(initialize = 74.3 ,doc = "adiabtic reactor fixed cost ($1e3 per year)") - m.adiabtic_reactor_linear_coeffcient = Param(initialize = 1.257 ,doc = "adiabtic reactor linear coeffcient (times reactor volumn) ($1e3 per year)") - m.isothermal_reactor_fixed_cost = Param(initialize = 92.875 ,doc = "isothermal reactor fixed cost ($1e3 per year)") - m.isothermal_reactor_linear_coeffcient = Param(initialize = 1.57125 ,doc = "isothermal reactor linear coeffcient (times reactor volumn) ($1e3 per year)") - m.h2_feed_cost = Param(initialize = 2.5, doc = "h2 feed cost (95% h2,5% Ch4)") - m.toluene_feed_cost = Param(initialize = 14., doc = "toluene feed cost (100% toluene)") - m.benzene_product = Param(initialize = 19.9,doc = "benzene product profit(benzene >= 99.97%)") - m.diphenyl_product = Param(initialize = 11.84,doc= "diphenyl product profit(diphenyl = 100%)") + m.hydrogen_purge_value = Param( + initialize=1.08, doc="heating value of hydrogen purge" + ) + m.electricity_cost = Param( + initialize=0.04 * 24 * 365 / 1000, + doc="electricity cost, value is 0.04 with the unit of kw/h, now is kw/yr/k$", + ) + m.meathane_purge_value = Param( + initialize=3.37, doc="heating value of meathane purge" + ) + m.heating_cost = Param( + initialize=8000.0, doc="Heating cost(steam) with unit 1e6 KJ" + ) + m.cooling_cost = Param( + initialize=700.0, doc="heating cost (water) with unit 1e6 KJ" + ) + m.fuel_cost = Param(initialize=4000.0, doc="fuel cost with unit 1e6 KJ") + m.abs_fixed_cost = Param(initialize=13, doc="fixed cost of absober ($1e3 per year)") + m.abs_linear_coefficient = Param( + initialize=1.2, + doc="linear coefficient of absorber (times tray number) ($1e3 per year)", + ) + m.compressor_fixed_cost = Param( + initialize=7.155, doc="compressor fixed cost ($1e3 per year)" + ) + m.compressor_fixed_cost_4 = Param( + initialize=4.866, doc="compressor fixed cost for compressor 4 ($1e3 per year)" + ) + m.compressor_linear_coefficient = Param( + initialize=0.815, + doc="compressor linear coefficient (vaporflow rate) ($1e3 per year)", + ) + m.compressor_linear_coefficient_4 = Param( + initialize=0.887, + doc="compressor linear coefficient (vaporflow rate) ($1e3 per year)", + ) + m.stabilizing_column_fixed_cost = Param( + initialize=1.126, doc="stabilizing column fixed cost ($1e3 per year)" + ) + m.stabilizing_column_linear_coefficient = Param( + initialize=0.375, + doc="stabilizing column linear coefficient (times number of trays) ($1e3 per year)", + ) + m.benzene_column_fixed_cost = Param( + initialize=16.3, doc="benzene column fixed cost ($1e3 per year)" + ) + m.benzene_column_linear_coefficient = Param( + initialize=1.55, + doc="benzene column linear coefficient (times number of trays) ($1e3 per year)", + ) + m.toluene_column_fixed_cost = Param( + initialize=3.9, doc="toluene column fixed cost ($1e3 per year)" + ) + m.toluene_column_linear_coefficient = Param( + initialize=1.12, + doc="toluene column linear coefficient (times number of trays) ($1e3 per year)", + ) + m.furnace_fixed_cost = Param( + initialize=6.20, doc="toluene column fixed cost ($1e3 per year)" + ) + m.furnace_linear_coefficient = Param( + initialize=1171.7, + doc="furnace column linear coefficient (1e9KJ/yr) ($1e3 per year)", + ) + m.membrane_separator_fixed_cost = Param( + initialize=43.24, doc="membrane separator fixed cost ($1e3 per year)" + ) + m.membrane_separator_linear_coefficient = Param( + initialize=49.0, + doc="furnace column linear coefficient (times inlet flowrate) ($1e3 per year)", + ) + m.adiabtic_reactor_fixed_cost = Param( + initialize=74.3, doc="adiabtic reactor fixed cost ($1e3 per year)" + ) + m.adiabtic_reactor_linear_coefficient = Param( + initialize=1.257, + doc="adiabtic reactor linear coefficient (times reactor volume) ($1e3 per year)", + ) + m.isothermal_reactor_fixed_cost = Param( + initialize=92.875, doc="isothermal reactor fixed cost ($1e3 per year)" + ) + m.isothermal_reactor_linear_coefficient = Param( + initialize=1.57125, + doc="isothermal reactor linear coefficient (times reactor volume) ($1e3 per year)", + ) + m.h2_feed_cost = Param(initialize=2.5, doc="h2 feed cost (95% h2,5% Ch4)") + m.toluene_feed_cost = Param(initialize=14.0, doc="toluene feed cost (100% toluene)") + m.benzene_product = Param( + initialize=19.9, doc="benzene product profit(benzene >= 99.97%)" + ) + m.diphenyl_product = Param( + initialize=11.84, doc="diphenyl product profit(diphenyl = 100%)" + ) def profits_from_paper(m): - return 510. * (- m.h2_feed_cost * m.f[1] - m.toluene_feed_cost * (m.f[66] + m.f[67]) + m.benzene_product * m.f[31] + m.diphenyl_product * m.f[35] + m.hydrogen_purge_value * (m.fc[4, 'h2'] + m.fc[28, 'h2'] + m.fc[53, 'h2'] + m.fc[55, 'h2']) + m.meathane_purge_value * (m.fc[4, 'ch4'] + m.fc[28, 'ch4'] + m.fc[53, 'ch4'] + m.fc[55, 'ch4'])) - m.compressor_linear_coeffcient * (m.elec[1] + m.elec[2] + m.elec[3]) - m.compressor_linear_coeffcient * m.elec[4] - m.compressor_fixed_cost * (m.purify_H2.binary_indicator_var + m.recycle_hydrogen.binary_indicator_var + m.absorber_hydrogen.binary_indicator_var) - m.compressor_fixed_cost * m.recycle_methane_membrane.binary_indicator_var - sum((m.electricity_cost * m.elec[comp]) for comp in m.comp) - (m.adiabtic_reactor_fixed_cost * m.adiabatic_reactor.binary_indicator_var + m.adiabtic_reactor_linear_coeffcient * m.rctvol[1]) - (m.isothermal_reactor_fixed_cost * m.isothermal_reactor.binary_indicator_var + m.isothermal_reactor_linear_coeffcient * m.rctvol[2]) - m.cooling_cost/1000 * m.q[2] - (m.stabilizing_column_fixed_cost * m.methane_distillation_column.binary_indicator_var +m.stabilizing_column_linear_coeffcient * m.ndist[1]) - (m.benzene_column_fixed_cost+ m.benzene_column_linear_coeffcient * m.ndist[2]) - (m.toluene_column_fixed_cost * m.toluene_distillation_column.binary_indicator_var + m.toluene_column_linear_coeffcient * m.ndist[3]) - (m.membrane_seperator_fixed_cost * m.purify_H2.binary_indicator_var + m.membrane_seperator_linear_coeffcient * m.f[3]) - (m.membrane_seperator_fixed_cost * m.recycle_methane_membrane.binary_indicator_var + m.membrane_seperator_linear_coeffcient * m.f[54]) - (m.abs_fixed_cost * m.absorber_hydrogen.binary_indicator_var + m.abs_linear_coeffcient * m.nabs[1]) - ( m.fuel_cost * m.qfuel[1] + m.furnace_linear_coeffcient* m.qfuel[1] ) - sum(m.cooling_cost * m.qc[hec] for hec in m.hec) - sum(m.heating_cost * m.qh[heh] for heh in m.heh) - m.furnace_fixed_cost - m.obj = Objective(rule = profits_from_paper, sense=maximize) + return ( + 510.0 + * ( + -m.h2_feed_cost * m.f[1] + - m.toluene_feed_cost * (m.f[66] + m.f[67]) + + m.benzene_product * m.f[31] + + m.diphenyl_product * m.f[35] + + m.hydrogen_purge_value + * (m.fc[4, 'h2'] + m.fc[28, 'h2'] + m.fc[53, 'h2'] + m.fc[55, 'h2']) + + m.meathane_purge_value + * (m.fc[4, 'ch4'] + m.fc[28, 'ch4'] + m.fc[53, 'ch4'] + m.fc[55, 'ch4']) + ) + - m.compressor_linear_coefficient * (m.elec[1] + m.elec[2] + m.elec[3]) + - m.compressor_linear_coefficient * m.elec[4] + - m.compressor_fixed_cost + * ( + m.purify_H2.binary_indicator_var + + m.recycle_hydrogen.binary_indicator_var + + m.absorber_hydrogen.binary_indicator_var + ) + - m.compressor_fixed_cost * m.recycle_methane_membrane.binary_indicator_var + - sum((m.electricity_cost * m.elec[comp]) for comp in m.comp) + - ( + m.adiabtic_reactor_fixed_cost * m.adiabatic_reactor.binary_indicator_var + + m.adiabtic_reactor_linear_coefficient * m.rctvol[1] + ) + - ( + m.isothermal_reactor_fixed_cost + * m.isothermal_reactor.binary_indicator_var + + m.isothermal_reactor_linear_coefficient * m.rctvol[2] + ) + - m.cooling_cost / 1000 * m.q[2] + - ( + m.stabilizing_column_fixed_cost + * m.methane_distillation_column.binary_indicator_var + + m.stabilizing_column_linear_coefficient * m.ndist[1] + ) + - ( + m.benzene_column_fixed_cost + + m.benzene_column_linear_coefficient * m.ndist[2] + ) + - ( + m.toluene_column_fixed_cost + * m.toluene_distillation_column.binary_indicator_var + + m.toluene_column_linear_coefficient * m.ndist[3] + ) + - ( + m.membrane_separator_fixed_cost * m.purify_H2.binary_indicator_var + + m.membrane_separator_linear_coefficient * m.f[3] + ) + - ( + m.membrane_separator_fixed_cost + * m.recycle_methane_membrane.binary_indicator_var + + m.membrane_separator_linear_coefficient * m.f[54] + ) + - ( + m.abs_fixed_cost * m.absorber_hydrogen.binary_indicator_var + + m.abs_linear_coefficient * m.nabs[1] + ) + - (m.fuel_cost * m.qfuel[1] + m.furnace_linear_coefficient * m.qfuel[1]) + - sum(m.cooling_cost * m.qc[hec] for hec in m.hec) + - sum(m.heating_cost * m.qh[heh] for heh in m.heh) + - m.furnace_fixed_cost + ) + + m.obj = Objective(rule=profits_from_paper, sense=maximize) # def profits_GAMS_file(m): # "there are several differences between the data from GAMS file and the paper: 1. all the compressor share the same fixed and linear cost in paper but in GAMS they have different fixed and linear cost in GAMS file. 2. the fixed cost for absorber in GAMS file is 3.0 but in the paper is 13.0, but they are getting the same results 3. the electricity cost is not the same" - # return 510. * (- m.h2_feed_cost * m.f[1] - m.toluene_feed_cost * (m.f[66] + m.f[67]) + m.benzene_product * m.f[31] + m.diphenyl_product * m.f[35] + m.hydrogen_purge_value * (m.fc[4, 'h2'] + m.fc[28, 'h2'] + m.fc[53, 'h2'] + m.fc[55, 'h2']) + m.meathane_purge_value * (m.fc[4, 'ch4'] + m.fc[28, 'ch4'] + m.fc[53, 'ch4'] + m.fc[55, 'ch4'])) - m.compressor_linear_coeffcient * (m.elec[1] + m.elec[2] + m.elec[3]) - m.compressor_linear_coeffcient_4 * m.elec[4] - m.compressor_fixed_cost * (m.purify_H2.binary_indicator_var + m.recycle_hydrogen.binary_indicator_var + m.absorber_hydrogen.binary_indicator_var) - m.compressor_fixed_cost_4 * m.recycle_methane_membrane.binary_indicator_var - sum((m.costelec * m.elec[comp]) for comp in m.comp) - (m.adiabtic_reactor_fixed_cost * m.adiabatic_reactor.binary_indicator_var + m.adiabtic_reactor_linear_coeffcient * m.rctvol[1]) - (m.isothermal_reactor_fixed_cost * m.isothermal_reactor.binary_indicator_var + m.isothermal_reactor_linear_coeffcient * m.rctvol[2]) - m.cooling_cost/1000 * m.q[2] - (m.stabilizing_column_fixed_cost * m.methane_distillation_column.binary_indicator_var +m.stabilizing_column_linear_coeffcient * m.ndist[1]) - (m.benzene_column_fixed_cost + m.benzene_column_linear_coeffcient * m.ndist[2]) - (m.toluene_column_fixed_cost * m.toluene_distillation_column.binary_indicator_var + m.toluene_column_linear_coeffcient * m.ndist[3]) - (m.membrane_seperator_fixed_cost * m.purify_H2.binary_indicator_var + m.membrane_seperator_linear_coeffcient * m.f[3]) - (m.membrane_seperator_fixed_cost * m.recycle_methane_membrane.binary_indicator_var + m.membrane_seperator_linear_coeffcient * m.f[54]) - (3.0 * m.absorber_hydrogen.binary_indicator_var + m.abs_linear_coeffcient * m.nabs[1]) - (m.fuel_cost * m.qfuel[1] + m.furnace_linear_coeffcient* m.qfuel[1]) - sum(m.cooling_cost * m.qc[hec] for hec in m.hec) - sum(m.heating_cost * m.qh[heh] for heh in m.heh) - m.furnace_fixed_cost + # return 510. * (- m.h2_feed_cost * m.f[1] - m.toluene_feed_cost * (m.f[66] + m.f[67]) + m.benzene_product * m.f[31] + m.diphenyl_product * m.f[35] + m.hydrogen_purge_value * (m.fc[4, 'h2'] + m.fc[28, 'h2'] + m.fc[53, 'h2'] + m.fc[55, 'h2']) + m.meathane_purge_value * (m.fc[4, 'ch4'] + m.fc[28, 'ch4'] + m.fc[53, 'ch4'] + m.fc[55, 'ch4'])) - m.compressor_linear_coefficient * (m.elec[1] + m.elec[2] + m.elec[3]) - m.compressor_linear_coefficient_4 * m.elec[4] - m.compressor_fixed_cost * (m.purify_H2.binary_indicator_var + m.recycle_hydrogen.binary_indicator_var + m.absorber_hydrogen.binary_indicator_var) - m.compressor_fixed_cost_4 * m.recycle_methane_membrane.binary_indicator_var - sum((m.costelec * m.elec[comp]) for comp in m.comp) - (m.adiabtic_reactor_fixed_cost * m.adiabatic_reactor.binary_indicator_var + m.adiabtic_reactor_linear_coefficient * m.rctvol[1]) - (m.isothermal_reactor_fixed_cost * m.isothermal_reactor.binary_indicator_var + m.isothermal_reactor_linear_coefficient * m.rctvol[2]) - m.cooling_cost/1000 * m.q[2] - (m.stabilizing_column_fixed_cost * m.methane_distillation_column.binary_indicator_var +m.stabilizing_column_linear_coefficient * m.ndist[1]) - (m.benzene_column_fixed_cost + m.benzene_column_linear_coefficient * m.ndist[2]) - (m.toluene_column_fixed_cost * m.toluene_distillation_column.binary_indicator_var + m.toluene_column_linear_coefficient * m.ndist[3]) - (m.membrane_separator_fixed_cost * m.purify_H2.binary_indicator_var + m.membrane_separator_linear_coefficient * m.f[3]) - (m.membrane_separator_fixed_cost * m.recycle_methane_membrane.binary_indicator_var + m.membrane_separator_linear_coefficient * m.f[54]) - (3.0 * m.absorber_hydrogen.binary_indicator_var + m.abs_linear_coefficient * m.nabs[1]) - (m.fuel_cost * m.qfuel[1] + m.furnace_linear_coefficient* m.qfuel[1]) - sum(m.cooling_cost * m.qc[hec] for hec in m.hec) - sum(m.heating_cost * m.qh[heh] for heh in m.heh) - m.furnace_fixed_cost # m.obj = Objective(rule=profits_GAMS_file, sense=maximize) return m @@ -1813,50 +2886,51 @@ def profits_from_paper(m): # %% + def solve_with_gdpopt(m): ''' This function solves model m using GDPOpt ''' opt = SolverFactory('gdpopt') - res = opt.solve(m, tee=True, - strategy='LOA', - # strategy='GLOA', - time_limit=3600, - mip_solver='gams', - mip_solver_args=dict(solver='cplex', warmstart=True), - nlp_solver='gams', - nlp_solver_args=dict(solver='ipopth', warmstart=True,), - minlp_solver='gams', - minlp_solver_args=dict(solver='dicopt', warmstart=True), - subproblem_presolve=False, - # init_strategy='no_init', - set_cover_iterlim=20, - # calc_disjunctive_bounds=True - ) + res = opt.solve( + m, + tee=True, + strategy='LOA', + # strategy='GLOA', + time_limit=3600, + mip_solver='gams', + mip_solver_args=dict(solver='cplex', warmstart=True), + nlp_solver='gams', + nlp_solver_args=dict(solver='ipopth', warmstart=True), + minlp_solver='gams', + minlp_solver_args=dict(solver='dicopt', warmstart=True), + subproblem_presolve=False, + # init_strategy='no_init', + set_cover_iterlim=20, + # calc_disjunctive_bounds=True + ) return res + def solve_with_minlp(m): ''' - This function solves model m using minlp transformation by either Big-M or convex hull + This function solves model m using minlp transformation by either Big-M or convex hull ''' TransformationFactory('gdp.bigm').apply_to(m, bigM=60) # TransformationFactory('gdp.hull').apply_to(m) # result = SolverFactory('baron').solve(m, tee=True) result = SolverFactory('gams').solve( - m, solver='baron', tee=True, - add_options=[ - 'option reslim=120;' - ] - ); + m, solver='baron', tee=True, add_options=['option reslim=120;'] + ) return result - # %% + def infeasible_constraints(m): ''' This function checks infeasible constraint in the model @@ -1864,20 +2938,19 @@ def infeasible_constraints(m): log_infeasible_constraints(m) - # %% # enumeration each possible route selection by fixing binary variable values in every disjunctions -def enumerate_solutions(m): - H2_treatments = ['purify','none_purify'] - Reactor_selections = ['adiabatic_reactor','isothermal_reactor'] - Methane_recycle_selections = ['recycle_membrane','recycle_purge'] - Absorber_recycle_selections = ['no_absorber','yes_absorber'] - Methane_product_selections = ['methane_flash','methane_column'] - Toluene_product_selections = ['toluene_flash','toluene_column'] +def enumerate_solutions(m): + H2_treatments = ['purify', 'none_purify'] + Reactor_selections = ['adiabatic_reactor', 'isothermal_reactor'] + Methane_recycle_selections = ['recycle_membrane', 'recycle_purge'] + Absorber_recycle_selections = ['no_absorber', 'yes_absorber'] + Methane_product_selections = ['methane_flash', 'methane_column'] + Toluene_product_selections = ['toluene_flash', 'toluene_column'] for H2_treatment in H2_treatments: for Reactor_selection in Reactor_selections: @@ -1922,20 +2995,39 @@ def enumerate_solutions(m): m.toluene_flash_separation.indicator_var.fix(True) m.toluene_distillation_column.indicator_var.fix(False) opt = SolverFactory('gdpopt') - res = opt.solve(m,tee =False, - strategy = 'LOA', - time_limit = 3600, - mip_solver = 'gams', - mip_solver_args= dict(solver = 'gurobi', warmstart=True), - nlp_solver = 'gams', - nlp_solver_args= dict(solver = 'ipopth', add_options = ['option optcr = 0'],warmstart=True), - minlp_solver = 'gams', - minlp_solver_args= dict(solver = 'dicopt', warmstart=True), - subproblem_presolve=False, - init_strategy = 'no_init', - set_cover_iterlim = 20 - ) - print('{0:<30}{1:<30}{2:<30}{3:<30}{4:<30}{5:<30}{6:<30}{7:<30}'.format(H2_treatment, Reactor_selection, Methane_recycle_selection,Absorber_recycle_selection,Methane_product_selection,Toluene_product_selection,str(res.solver.termination_condition),value(m.obj))) + res = opt.solve( + m, + tee=False, + strategy='LOA', + time_limit=3600, + mip_solver='gams', + mip_solver_args=dict(solver='gurobi', warmstart=True), + nlp_solver='gams', + nlp_solver_args=dict( + solver='ipopth', + add_options=['option optcr = 0'], + warmstart=True, + ), + minlp_solver='gams', + minlp_solver_args=dict(solver='dicopt', warmstart=True), + subproblem_presolve=False, + init_strategy='no_init', + set_cover_iterlim=20, + ) + print( + '{0:<30}{1:<30}{2:<30}{3:<30}{4:<30}{5:<30}{6:<30}{7:<30}'.format( + H2_treatment, + Reactor_selection, + Methane_recycle_selection, + Absorber_recycle_selection, + Methane_product_selection, + Toluene_product_selection, + str(res.solver.termination_condition), + value(m.obj), + ) + ) + + # %% def show_decision(m): ''' @@ -1965,6 +3057,8 @@ def show_decision(m): print("toluene_column") else: print("toluene_flash") + + # %% @@ -1982,7 +3076,7 @@ def show_decision(m): # Check if constraints are violated infeasible_constraints(m) - # show optimial flowsheet selection + # show optimal flowsheet selection # show_decision(m) print(res) diff --git a/gdplib/kaibel/kaibel_init.py b/gdplib/kaibel/kaibel_init.py index 82400e7..ce28540 100644 --- a/gdplib/kaibel/kaibel_init.py +++ b/gdplib/kaibel/kaibel_init.py @@ -27,7 +27,17 @@ from __future__ import division -from pyomo.environ import (exp, log10, minimize, NonNegativeReals, Objective, RangeSet, SolverFactory, value, Var) +from pyomo.environ import ( + exp, + log10, + minimize, + NonNegativeReals, + Objective, + RangeSet, + SolverFactory, + value, + Var, +) from gdplib.kaibel.kaibel_prop import get_model_with_properties @@ -35,27 +45,26 @@ def initialize_kaibel(): m = get_model_with_properties() - ## Operating conditions - m.Preb = 1.2 # Reboiler pressure in bar - m.Pcon = 1.05 # Condenser pressure in bar + m.Preb = 1.2 # Reboiler pressure in bar + m.Pcon = 1.05 # Condenser pressure in bar m.Pf = 1.02 - Pnmin = {} # Pressure in bars - Pnmin[1] = m.Preb # Reboiler pressure in bars - Pnmin[3] = m.Pcon # Distillate pressure in bars - Pnmin[2] = m.Pf # Side feed pressure in bars + Pnmin = {} # Pressure in bars + Pnmin[1] = m.Preb # Reboiler pressure in bars + Pnmin[3] = m.Pcon # Distillate pressure in bars + Pnmin[2] = m.Pf # Side feed pressure in bars - xi_nmin = {} # Initial liquid composition: first number = column and - # second number = 1 reboiler, 2 side feed, and - # 3 for condenser + xi_nmin = {} # Initial liquid composition: first number = column and + # second number = 1 reboiler, 2 side feed, and + # 3 for condenser ## Column 1 - c_c1 = 4 # Components in Column 1 - lc_c1 = 3 # Ligh component in Column 1 - hc_c1 = 4 # Heavy component in Column 1 - inter1_c1 = 1 # Intermediate component in Column 1 - inter2_c1 = 2 # Intermediate component in Column 1 + c_c1 = 4 # Components in Column 1 + lc_c1 = 3 # Light component in Column 1 + hc_c1 = 4 # Heavy component in Column 1 + inter1_c1 = 1 # Intermediate component in Column 1 + inter2_c1 = 2 # Intermediate component in Column 1 xi_nmin[1, 1, hc_c1] = 0.999 xi_nmin[1, 1, lc_c1] = (1 - xi_nmin[1, 1, hc_c1]) / (c_c1 - 1) @@ -64,18 +73,19 @@ def initialize_kaibel(): xi_nmin[1, 3, lc_c1] = 0.33 xi_nmin[1, 3, inter1_c1] = 0.33 xi_nmin[1, 3, inter2_c1] = 0.33 - xi_nmin[1, 3, hc_c1] = 1 - (xi_nmin[1, 3, lc_c1] + xi_nmin[1, 3, inter1_c1] + - xi_nmin[1, 3, inter2_c1]) + xi_nmin[1, 3, hc_c1] = 1 - ( + xi_nmin[1, 3, lc_c1] + xi_nmin[1, 3, inter1_c1] + xi_nmin[1, 3, inter2_c1] + ) xi_nmin[1, 2, lc_c1] = 1 / c_c1 xi_nmin[1, 2, inter1_c1] = 1 / c_c1 xi_nmin[1, 2, inter2_c1] = 1 / c_c1 xi_nmin[1, 2, hc_c1] = 1 / c_c1 ## Column 2 - c_c2 = 3 # Light components in Column 2 - lc_c2 = 2 # Light component in Column 2 - hc_c2 = 3 # Heavy component in Column 2 - inter_c2 = 1 # Intermediate component in Column 2 + c_c2 = 3 # Light components in Column 2 + lc_c2 = 2 # Light component in Column 2 + hc_c2 = 3 # Heavy component in Column 2 + inter_c2 = 1 # Intermediate component in Column 2 xi_nmin[2, 1, hc_c2] = 0.999 xi_nmin[2, 1, lc_c2] = (1 - xi_nmin[2, 1, hc_c2]) / (c_c2 - 1) @@ -87,11 +97,10 @@ def initialize_kaibel(): xi_nmin[2, 2, inter_c2] = 1 / c_c2 xi_nmin[2, 2, hc_c2] = 1 / c_c2 - ## Column 3 - c_c3 = 2 # Components in Column 3 - lc_c3 = 1 # Light component in Column 3 - hc_c3 = 2 # Heavy component in Column 3 + c_c3 = 2 # Components in Column 3 + lc_c3 = 1 # Light component in Column 3 + hc_c3 = 2 # Heavy component in Column 3 xi_nmin[3, 1, hc_c3] = 0.999 xi_nmin[3, 1, lc_c3] = 1 - xi_nmin[3, 1, hc_c3] @@ -100,60 +109,62 @@ def initialize_kaibel(): xi_nmin[3, 2, lc_c3] = 0.50 xi_nmin[3, 2, hc_c3] = 0.50 - - #### mn = m.clone() mn.name = "Initialization Code" - mn.cols = RangeSet(3, - doc='Number of columns ') - mn.sec = RangeSet(3, - doc='Sections in column: 1 reb, 2 side feed, 3 cond') - mn.nc1 = RangeSet(c_c1, - doc='Number of components in Column 1') - mn.nc2 = RangeSet(c_c2, - doc='Number of components in Column 2') - mn.nc3 = RangeSet(c_c3, - doc='Number of components in Column 3') - - mn.Tnmin = Var(mn.cols, mn.sec, - doc='Temperature in K', bounds=(0, 500), - domain=NonNegativeReals) - mn.Tr1nmin = Var(mn.cols, mn.sec, mn.nc1, - doc='Temperature term for vapor pressure', - domain=NonNegativeReals, - bounds=(0, None)) - mn.Tr2nmin = Var(mn.cols, mn.sec, mn.nc2, - doc='Temperature term for vapor pressure', - domain=NonNegativeReals, - bounds=(0, None)) - mn.Tr3nmin = Var(mn.cols, mn.sec, mn.nc3, - doc='Temperature term for vapor pressure', - domain=NonNegativeReals, - bounds=(0, None)) - - - @mn.Constraint(mn.cols, mn.sec, mn.nc1, - doc="Temperature term for vapor pressure") + mn.cols = RangeSet(3, doc='Number of columns ') + mn.sec = RangeSet(3, doc='Sections in column: 1 reb, 2 side feed, 3 cond') + mn.nc1 = RangeSet(c_c1, doc='Number of components in Column 1') + mn.nc2 = RangeSet(c_c2, doc='Number of components in Column 2') + mn.nc3 = RangeSet(c_c3, doc='Number of components in Column 3') + + mn.Tnmin = Var( + mn.cols, + mn.sec, + doc='Temperature in K', + bounds=(0, 500), + domain=NonNegativeReals, + ) + mn.Tr1nmin = Var( + mn.cols, + mn.sec, + mn.nc1, + doc='Temperature term for vapor pressure', + domain=NonNegativeReals, + bounds=(0, None), + ) + mn.Tr2nmin = Var( + mn.cols, + mn.sec, + mn.nc2, + doc='Temperature term for vapor pressure', + domain=NonNegativeReals, + bounds=(0, None), + ) + mn.Tr3nmin = Var( + mn.cols, + mn.sec, + mn.nc3, + doc='Temperature term for vapor pressure', + domain=NonNegativeReals, + bounds=(0, None), + ) + + @mn.Constraint(mn.cols, mn.sec, mn.nc1, doc="Temperature term for vapor pressure") def _column1_reduced_temperature(mn, col, sec, nc): return mn.Tr1nmin[col, sec, nc] * mn.Tnmin[col, sec] == mn.prop[nc, 'TC'] - - @mn.Constraint(mn.cols, mn.sec, mn.nc2, - doc="Temperature term for vapor pressure") + @mn.Constraint(mn.cols, mn.sec, mn.nc2, doc="Temperature term for vapor pressure") def _column2_reduced_temperature(mn, col, sec, nc): return mn.Tr2nmin[col, sec, nc] * mn.Tnmin[col, sec] == mn.prop[nc, 'TC'] - - @mn.Constraint(mn.cols, mn.sec, mn.nc3, - doc="Temperature term for vapor pressure") + @mn.Constraint(mn.cols, mn.sec, mn.nc3, doc="Temperature term for vapor pressure") def _column3_reduced_temperature(mn, col, sec, nc): return mn.Tr3nmin[col, sec, nc] * mn.Tnmin[col, sec] == mn.prop[nc, 'TC'] - @mn.Constraint(mn.cols, mn.sec, doc="Boiling point temperature") def _equilibrium_equation(mn, col, sec): if col == 1: @@ -165,46 +176,51 @@ def _equilibrium_equation(mn, col, sec): elif col == 3: a = mn.Tr3nmin b = mn.nc3 - return sum( - xi_nmin[col, sec, nc] * mn.prop[nc, 'PC'] * exp( - a[col, sec, nc] * ( - mn.prop[nc, 'vpA'] * \ - (1 - mn.Tnmin[col, sec] / mn.prop[nc, 'TC']) - + mn.prop[nc, 'vpB'] * \ - (1 - mn.Tnmin[col, sec] / mn.prop[nc, 'TC'])**1.5 - + mn.prop[nc, 'vpC'] * \ - (1 - mn.Tnmin[col, sec] / mn.prop[nc, 'TC'])**3 - + mn.prop[nc, 'vpD'] * \ - (1 - mn.Tnmin[col, sec] / mn.prop[nc, 'TC'])**6 + return ( + sum( + xi_nmin[col, sec, nc] + * mn.prop[nc, 'PC'] + * exp( + a[col, sec, nc] + * ( + mn.prop[nc, 'vpA'] + * (1 - mn.Tnmin[col, sec] / mn.prop[nc, 'TC']) + + mn.prop[nc, 'vpB'] + * (1 - mn.Tnmin[col, sec] / mn.prop[nc, 'TC']) ** 1.5 + + mn.prop[nc, 'vpC'] + * (1 - mn.Tnmin[col, sec] / mn.prop[nc, 'TC']) ** 3 + + mn.prop[nc, 'vpD'] + * (1 - mn.Tnmin[col, sec] / mn.prop[nc, 'TC']) ** 6 + ) ) - ) / Pnmin[sec] for nc in b - ) == 1 + / Pnmin[sec] + for nc in b + ) + == 1 + ) - mn.OBJ = Objective(expr=1, sense=minimize) - #### SolverFactory('ipopt').solve(mn) - - yc = {} # Vapor composition - kl = {} # Light key component - kh = {} # Heavy key component - alpha = {} # Relative volatility of kl - ter = {} # Term to calculate the minimum number of trays - Nmin = {} # Minimum number of stages - Nminopt = {} # Total optimal minimum number of trays - Nfeed = {} # Side feed optimal location using Kirkbride's method: - # 1 = number of trays in rectifying section and - # 2 = number of trays in stripping section - side_feed = {} # Side feed location - av_alpha = {} # Average relative volatilities - xi_lhc = {} # Liquid composition in columns - rel = mn.Bdes / mn.Ddes # Ratio between products flowrates - ln = {} # Light component for the different columns - hn = {} # Heavy component for the different columns + yc = {} # Vapor composition + kl = {} # Light key component + kh = {} # Heavy key component + alpha = {} # Relative volatility of kl + ter = {} # Term to calculate the minimum number of trays + Nmin = {} # Minimum number of stages + Nminopt = {} # Total optimal minimum number of trays + Nfeed = {} # Side feed optimal location using Kirkbride's method: + # 1 = number of trays in rectifying section and + # 2 = number of trays in stripping section + side_feed = {} # Side feed location + av_alpha = {} # Average relative volatilities + xi_lhc = {} # Liquid composition in columns + rel = mn.Bdes / mn.Ddes # Ratio between products flowrates + ln = {} # Light component for the different columns + hn = {} # Heavy component for the different columns ln[1] = lc_c1 ln[2] = lc_c2 ln[3] = lc_c3 @@ -212,7 +228,6 @@ def _equilibrium_equation(mn, col, sec): hn[2] = hc_c2 hn[3] = hc_c3 - for col in mn.cols: if col == 1: b = mn.nc1 @@ -222,37 +237,38 @@ def _equilibrium_equation(mn, col, sec): b = mn.nc3 for sec in mn.sec: for nc in b: - yc[col, sec, nc] = xi_nmin[col, sec, nc] * mn.prop[nc, 'PC'] * exp( - mn.prop[nc, 'TC'] / value(mn.Tnmin[col, sec]) * ( - mn.prop[nc, 'vpA'] * \ - (1 - value(mn.Tnmin[col, sec]) / mn.prop[nc, 'TC']) - + mn.prop[nc, 'vpB'] * \ - (1 - value(mn.Tnmin[col, sec]) / mn.prop[nc, 'TC'])**1.5 - + mn.prop[nc, 'vpC'] * \ - (1 - value(mn.Tnmin[col, sec]) / mn.prop[nc, 'TC'])**3 - + mn.prop[nc, 'vpD'] * \ - (1 - value(mn.Tnmin[col, sec]) / mn.prop[nc, 'TC'])**6 + yc[col, sec, nc] = ( + xi_nmin[col, sec, nc] + * mn.prop[nc, 'PC'] + * exp( + mn.prop[nc, 'TC'] + / value(mn.Tnmin[col, sec]) + * ( + mn.prop[nc, 'vpA'] + * (1 - value(mn.Tnmin[col, sec]) / mn.prop[nc, 'TC']) + + mn.prop[nc, 'vpB'] + * (1 - value(mn.Tnmin[col, sec]) / mn.prop[nc, 'TC']) ** 1.5 + + mn.prop[nc, 'vpC'] + * (1 - value(mn.Tnmin[col, sec]) / mn.prop[nc, 'TC']) ** 3 + + mn.prop[nc, 'vpD'] + * (1 - value(mn.Tnmin[col, sec]) / mn.prop[nc, 'TC']) ** 6 + ) ) - ) / Pnmin[sec] + / Pnmin[sec] + ) for col in mn.cols: - xi_lhc[col, 4] = xi_nmin[col, 1, ln[col]] / \ - xi_nmin[col, 3, hn[col]] + xi_lhc[col, 4] = xi_nmin[col, 1, ln[col]] / xi_nmin[col, 3, hn[col]] for sec in mn.sec: - kl[col, sec] = yc[col, sec, ln[col]] / \ - xi_nmin[col, sec, ln[col]] - kh[col, sec] = yc[col, sec, hn[col]] / \ - xi_nmin[col, sec, hn[col]] - xi_lhc[col, sec] = xi_nmin[col, sec, hn[col]] / \ - xi_nmin[col, sec, ln[col]] + kl[col, sec] = yc[col, sec, ln[col]] / xi_nmin[col, sec, ln[col]] + kh[col, sec] = yc[col, sec, hn[col]] / xi_nmin[col, sec, hn[col]] + xi_lhc[col, sec] = xi_nmin[col, sec, hn[col]] / xi_nmin[col, sec, ln[col]] alpha[col, sec] = kl[col, sec] / kh[col, sec] for col in mn.cols: - av_alpha[col] = (alpha[col, 1] * alpha[col, 2] - * alpha[col, 3])**(1 / 3) - Nmin[col] = log10((1 / xi_lhc[col, 3]) * - xi_lhc[col, 1]) / log10(av_alpha[col]) - ter[col] = (rel * xi_lhc[col, 2] * (xi_lhc[col, 4]**2))**0.206 + av_alpha[col] = (alpha[col, 1] * alpha[col, 2] * alpha[col, 3]) ** (1 / 3) + Nmin[col] = log10((1 / xi_lhc[col, 3]) * xi_lhc[col, 1]) / log10(av_alpha[col]) + ter[col] = (rel * xi_lhc[col, 2] * (xi_lhc[col, 4] ** 2)) ** 0.206 Nfeed[1, col] = ter[col] * Nmin[col] / (1 + ter[col]) Nfeed[2, col] = Nfeed[1, col] / ter[col] side_feed[col] = Nfeed[2, col] @@ -264,7 +280,6 @@ def _equilibrium_equation(mn, col, sec): m.Tf0 = value(mn.Tnmin[1, 2]) m.TD0 = value(mn.Tnmin[2, 3]) - return m diff --git a/gdplib/kaibel/kaibel_prop.py b/gdplib/kaibel/kaibel_prop.py index bd43032..9243a9d 100644 --- a/gdplib/kaibel/kaibel_prop.py +++ b/gdplib/kaibel/kaibel_prop.py @@ -6,66 +6,62 @@ def get_model_with_properties(): - """Attach properties to the model.""" - + m = ConcreteModel() # ------------------------------------------------------------------ # Data # ------------------------------------------------------------------ - m.np = 25 # Number of possible tays - m.c = 4 # Number of components - m.lc = 1 # Light component - m.hc = 4 # Heavy component + m.np = 25 # Number of possible tays + m.c = 4 # Number of components + m.lc = 1 # Light component + m.hc = 4 # Heavy component #### Constant parameters - m.Rgas = 8.314 # Ideal gas constant in J/mol K - m.Tref = 298.15 # Reference temperature in K + m.Rgas = 8.314 # Ideal gas constant in J/mol K + m.Tref = 298.15 # Reference temperature in K #### Product specifications - m.xspec_lc = 0.99 # Final liquid composition for methanol (1) - m.xspec_hc = 0.99 # Fnal liquid composition for butanol (4) - m.xspec_inter2 = 0.99 # Final liquid composition for ethanol (2) - m.xspec_inter3 = 0.99 # Final liquid composition for propanol (3) - m.Ddes = 50 # Final flowrate in distillate in mol/s - m.Bdes = 50 # Final flowrate in bottoms in mol/s - m.Sdes = 50 # Final flowrate in side product streams in mol/s + m.xspec_lc = 0.99 # Final liquid composition for methanol (1) + m.xspec_hc = 0.99 # Fnal liquid composition for butanol (4) + m.xspec_inter2 = 0.99 # Final liquid composition for ethanol (2) + m.xspec_inter3 = 0.99 # Final liquid composition for propanol (3) + m.Ddes = 50 # Final flowrate in distillate in mol/s + m.Bdes = 50 # Final flowrate in bottoms in mol/s + m.Sdes = 50 # Final flowrate in side product streams in mol/s # #### Known initial values - m.Fi = m.Ddes + m.Bdes + 2 * m.Sdes # Side feed flowrate in mol/s - m.Vi = 400 # Initial value for vapor flowrate in mol/s - m.Li = 400 # Initial value for liquid flowrate in mol/s + m.Fi = m.Ddes + m.Bdes + 2 * m.Sdes # Side feed flowrate in mol/s + m.Vi = 400 # Initial value for vapor flowrate in mol/s + m.Li = 400 # Initial value for liquid flowrate in mol/s - m.Tf = 358 # Side feed temperature in K + m.Tf = 358 # Side feed temperature in K - m.Preb = 1.2 # Reboiler pressure in bar - m.Pbot = 1.12 # Bottom-most tray pressure in bar - m.Ptop = 1.08 # Top-most tray pressure in bar - m.Pcon = 1.05 # Condenser pressure in bar + m.Preb = 1.2 # Reboiler pressure in bar + m.Pbot = 1.12 # Bottom-most tray pressure in bar + m.Ptop = 1.08 # Top-most tray pressure in bar + m.Pcon = 1.05 # Condenser pressure in bar m.Pf = 1.02 - m.rr0 = 0.893 # Internal reflux ratio initial value - m.bu0 = 0.871 # Internal reflux ratio initial value - + m.rr0 = 0.893 # Internal reflux ratio initial value + m.bu0 = 0.871 # Internal reflux ratio initial value #### Scaling factors - m.Hscale = 1e3 - m.Qscale = 1e-3 + m.Hscale = 1e3 + m.Qscale = 1e-3 - #### Constants for the calculation of liquid heat capacity - m.cpc = {} # Constant 1 for liquid heat capacity - m.cpc2 = {} # Constant 2 for liquid heat capacity - m.cpc[1] = m.Rgas + m.cpc = {} # Constant 1 for liquid heat capacity + m.cpc2 = {} # Constant 2 for liquid heat capacity + m.cpc[1] = m.Rgas m.cpc[2] = 1 m.cpc2['A', 1] = 1 / 100 m.cpc2['B', 1] = 1 / 1e4 m.cpc2['A', 2] = 1 m.cpc2['B', 2] = 1 - # ------------------------------------------------------------------ # Physical Properties # @@ -86,9 +82,9 @@ def get_model_with_properties(): # # ------------------------------------------------------------------ - m.prop = {} # Properties of components: - cpL = {} # Ruczika-D method for liquid heat capacity calculation - # (Reference A, page 6.20) + m.prop = {} # Properties of components: + cpL = {} # Ruczika-D method for liquid heat capacity calculation + # (Reference A, page 6.20) sumA = {} sumB = {} sumC = {} @@ -107,45 +103,48 @@ def get_model_with_properties(): cpL['a', 'C(H3)(O)'] = 3.70344 cpL['b', 'C(H3)(O)'] = -1.12884 cpL['c', 'C(H3)(O)'] = 0.51239 - sumA[1] = (cpL['a', 'C(H3)(O)'] - + cpL['a', 'O(H)(C)']) - sumB[1] = (cpL['b', 'C(H3)(O)'] - + cpL['b', 'O(H)(C)']) - sumC[1] = (cpL['c', 'C(H3)(O)'] - + cpL['c', 'O(H)(C)']) - sumA[2] = (cpL['a', 'C(H3)(C)'] - + cpL['a', 'C(H2)(C)(O)'] - + cpL['a', 'O(H)(C)']) - sumB[2] = (cpL['b', 'C(H3)(C)'] - + cpL['b', 'C(H2)(C)(O)'] - + cpL['b', 'O(H)(C)']) - sumC[2] = (cpL['c', 'C(H3)(C)'] - + cpL['c', 'C(H2)(C)(O)'] - + cpL['c', 'O(H)(C)']) - sumA[3] = (cpL['a', 'C(H3)(C)'] - + cpL['a', 'C(H2)(C2)'] - + cpL['a', 'C(H2)(C)(O)'] - + cpL['a', 'O(H)(C)']) - sumB[3] = (cpL['b', 'C(H3)(C)'] - + cpL['b', 'C(H2)(C2)'] - + cpL['b', 'C(H2)(C)(O)'] - + cpL['b', 'O(H)(C)']) - sumC[3] = (cpL['c', 'C(H3)(C)'] - + cpL['c', 'C(H2)(C2)'] - + cpL['c', 'C(H2)(C)(O)'] - + cpL['c', 'O(H)(C)']) - sumA[4] = (cpL['a', 'C(H3)(C)'] - + 2 * cpL['a', 'C(H2)(C2)'] - + cpL['a', 'C(H2)(C)(O)'] - + cpL['a', 'O(H)(C)']) - sumB[4] = (cpL['b', 'C(H3)(C)'] - + 2 * cpL['b', 'C(H2)(C2)'] - + cpL['b', 'C(H2)(C)(O)'] - + cpL['b', 'O(H)(C)']) - sumC[4] = (cpL['c', 'C(H3)(C)'] - + 2 * cpL['c', 'C(H2)(C2)'] - + cpL['c', 'C(H2)(C)(O)'] - + cpL['c', 'O(H)(C)']) + sumA[1] = cpL['a', 'C(H3)(O)'] + cpL['a', 'O(H)(C)'] + sumB[1] = cpL['b', 'C(H3)(O)'] + cpL['b', 'O(H)(C)'] + sumC[1] = cpL['c', 'C(H3)(O)'] + cpL['c', 'O(H)(C)'] + sumA[2] = cpL['a', 'C(H3)(C)'] + cpL['a', 'C(H2)(C)(O)'] + cpL['a', 'O(H)(C)'] + sumB[2] = cpL['b', 'C(H3)(C)'] + cpL['b', 'C(H2)(C)(O)'] + cpL['b', 'O(H)(C)'] + sumC[2] = cpL['c', 'C(H3)(C)'] + cpL['c', 'C(H2)(C)(O)'] + cpL['c', 'O(H)(C)'] + sumA[3] = ( + cpL['a', 'C(H3)(C)'] + + cpL['a', 'C(H2)(C2)'] + + cpL['a', 'C(H2)(C)(O)'] + + cpL['a', 'O(H)(C)'] + ) + sumB[3] = ( + cpL['b', 'C(H3)(C)'] + + cpL['b', 'C(H2)(C2)'] + + cpL['b', 'C(H2)(C)(O)'] + + cpL['b', 'O(H)(C)'] + ) + sumC[3] = ( + cpL['c', 'C(H3)(C)'] + + cpL['c', 'C(H2)(C2)'] + + cpL['c', 'C(H2)(C)(O)'] + + cpL['c', 'O(H)(C)'] + ) + sumA[4] = ( + cpL['a', 'C(H3)(C)'] + + 2 * cpL['a', 'C(H2)(C2)'] + + cpL['a', 'C(H2)(C)(O)'] + + cpL['a', 'O(H)(C)'] + ) + sumB[4] = ( + cpL['b', 'C(H3)(C)'] + + 2 * cpL['b', 'C(H2)(C2)'] + + cpL['b', 'C(H2)(C)(O)'] + + cpL['b', 'O(H)(C)'] + ) + sumC[4] = ( + cpL['c', 'C(H3)(C)'] + + 2 * cpL['c', 'C(H2)(C2)'] + + cpL['c', 'C(H2)(C)(O)'] + + cpL['c', 'O(H)(C)'] + ) ## Methanol: component 1 m.prop[1, 'MW'] = 32.042 @@ -168,7 +167,6 @@ def get_model_with_properties(): m.prop[1, 'cpC', 2] = 2.587e-5 m.prop[1, 'cpD', 2] = -2.852e-8 - ## Ethanol: component 2 m.prop[2, 'MW'] = 46.069 m.prop[2, 'TB'] = 351.4 @@ -190,7 +188,6 @@ def get_model_with_properties(): m.prop[2, 'cpC', 2] = -8.390e-5 m.prop[2, 'cpD', 2] = 1.373e-9 - ## Propanol: component 3 m.prop[3, 'MW'] = 60.096 m.prop[3, 'TB'] = 370.3 @@ -212,7 +209,6 @@ def get_model_with_properties(): m.prop[3, 'cpC', 2] = -1.855e-4 m.prop[3, 'cpD', 2] = 4.296e-8 - ## Butanol: component 4 m.prop[4, 'MW'] = 74.123 m.prop[4, 'TB'] = 390.9 @@ -234,5 +230,4 @@ def get_model_with_properties(): m.prop[4, 'cpC', 2] = -2.242e-4 m.prop[4, 'cpD', 2] = 4.685e-8 - return m diff --git a/gdplib/kaibel/kaibel_side_flash.py b/gdplib/kaibel/kaibel_side_flash.py index 014d447..ca2960c 100644 --- a/gdplib/kaibel/kaibel_side_flash.py +++ b/gdplib/kaibel/kaibel_side_flash.py @@ -1,103 +1,108 @@ """ Side feed flash """ - from __future__ import division from pyomo.environ import ( - ConcreteModel, Constraint, exp, minimize, NonNegativeReals, Objective, Param, RangeSet, SolverFactory, value, Var, ) + ConcreteModel, + Constraint, + exp, + minimize, + NonNegativeReals, + Objective, + Param, + RangeSet, + SolverFactory, + value, + Var, +) def calc_side_feed_flash(m): msf = ConcreteModel('SIDE FEED FLASH') - msf.nc = RangeSet(1, m.c, doc='Number of components') m.xfi = {} for nc in msf.nc: m.xfi[nc] = 1 / m.c - msf.Tf = Param(doc='Side feed temperature in K', - initialize=m.Tf0) - msf.xf = Var(msf.nc, - doc='Side feed liquid composition', - domain=NonNegativeReals, - bounds=(0, 1), - initialize=m.xfi) - msf.yf = Var(msf.nc, - doc='Side feed vapor composition', - domain=NonNegativeReals, - bounds=(0, 1), - initialize=m.xfi) - msf.Keqf = Var(msf.nc, - doc='Vapor-liquid equilibrium constant', - domain=NonNegativeReals, - bounds=(0, 10), - initialize=0) - msf.Pvf = Var(msf.nc, - doc='Side feed vapor pressure in bar', - domain=NonNegativeReals, - bounds=(0, 10), - initialize=0) - msf.q = Var(doc='Vapor fraction', - bounds=(0, 1), - domain=NonNegativeReals, - initialize=0) - - + msf.Tf = Param(doc='Side feed temperature in K', initialize=m.Tf0) + msf.xf = Var( + msf.nc, + doc='Side feed liquid composition', + domain=NonNegativeReals, + bounds=(0, 1), + initialize=m.xfi, + ) + msf.yf = Var( + msf.nc, + doc='Side feed vapor composition', + domain=NonNegativeReals, + bounds=(0, 1), + initialize=m.xfi, + ) + msf.Keqf = Var( + msf.nc, + doc='Vapor-liquid equilibrium constant', + domain=NonNegativeReals, + bounds=(0, 10), + initialize=0, + ) + msf.Pvf = Var( + msf.nc, + doc='Side feed vapor pressure in bar', + domain=NonNegativeReals, + bounds=(0, 10), + initialize=0, + ) + msf.q = Var( + doc='Vapor fraction', bounds=(0, 1), domain=NonNegativeReals, initialize=0 + ) + @msf.Constraint(doc="Vapor fraction") def _algq(msf): - return sum(m.xfi[nc] * (1 - msf.Keqf[nc]) / \ - (1 + msf.q * (msf.Keqf[nc] - 1)) - for nc in msf.nc) == 0 + return ( + sum( + m.xfi[nc] * (1 - msf.Keqf[nc]) / (1 + msf.q * (msf.Keqf[nc] - 1)) + for nc in msf.nc + ) + == 0 + ) - - @msf.Constraint(msf.nc, - doc="Side feed liquid composition") + @msf.Constraint(msf.nc, doc="Side feed liquid composition") def _algx(msf, nc): return msf.xf[nc] * (1 + msf.q * (msf.Keqf[nc] - 1)) == m.xfi[nc] - - @msf.Constraint(msf.nc, - doc="Side feed vapor composition") + @msf.Constraint(msf.nc, doc="Side feed vapor composition") def _algy(msf, nc): return msf.yf[nc] == msf.xf[nc] * msf.Keqf[nc] - - @msf.Constraint(msf.nc, - doc="Vapor-liquid equilibrium constant") + @msf.Constraint(msf.nc, doc="Vapor-liquid equilibrium constant") def _algKeq(msf, nc): return msf.Keqf[nc] * m.Pf == msf.Pvf[nc] - - @msf.Constraint(msf.nc, - doc="Side feed vapor pressure") + @msf.Constraint(msf.nc, doc="Side feed vapor pressure") def _algPvf(msf, nc): return msf.Pvf[nc] == m.prop[nc, 'PC'] * exp( - m.prop[nc, 'TC'] / msf.Tf * ( - m.prop[nc, 'vpA'] * \ - (1 - msf.Tf / m.prop[nc, 'TC']) - + m.prop[nc, 'vpB'] * \ - (1 - msf.Tf / m.prop[nc, 'TC'])**1.5 - + m.prop[nc, 'vpC'] * \ - (1 - msf.Tf / m.prop[nc, 'TC'])**3 - + m.prop[nc, 'vpD'] * \ - (1 - msf.Tf / m.prop[nc, 'TC'])**6 + m.prop[nc, 'TC'] + / msf.Tf + * ( + m.prop[nc, 'vpA'] * (1 - msf.Tf / m.prop[nc, 'TC']) + + m.prop[nc, 'vpB'] * (1 - msf.Tf / m.prop[nc, 'TC']) ** 1.5 + + m.prop[nc, 'vpC'] * (1 - msf.Tf / m.prop[nc, 'TC']) ** 3 + + m.prop[nc, 'vpD'] * (1 - msf.Tf / m.prop[nc, 'TC']) ** 6 ) ) - msf.OBJ = Objective( - expr=1, - sense=minimize) + msf.OBJ = Objective(expr=1, sense=minimize) #### - SolverFactory('ipopt').solve(msf, - tee=False) + SolverFactory('ipopt').solve(msf, tee=False) m.yfi = {} for nc in msf.nc: m.yfi[nc] = value(msf.yf[nc]) - + m.q_init = value(msf.q) return m diff --git a/gdplib/kaibel/kaibel_solve_gdp.py b/gdplib/kaibel/kaibel_solve_gdp.py index 9c1592f..354bd50 100644 --- a/gdplib/kaibel/kaibel_solve_gdp.py +++ b/gdplib/kaibel/kaibel_solve_gdp.py @@ -1,11 +1,19 @@ """ Kaibel Column model: GDP formulation """ - from __future__ import division from math import copysign -from pyomo.environ import (Constraint, exp, minimize, NonNegativeReals, Objective, RangeSet, Set, Var) +from pyomo.environ import ( + Constraint, + exp, + minimize, + NonNegativeReals, + Objective, + RangeSet, + Set, + Var, +) from pyomo.gdp import Disjunct from gdplib.kaibel.kaibel_init import initialize_kaibel @@ -17,119 +25,102 @@ def build_model(): m = initialize_kaibel() - # Side feed init m = calc_side_feed_flash(m) - m.name = "GDP Kaibel Column" #### Calculated initial values - m.Treb = m.TB0 + 5 # Reboiler temperature in K - m.Tbot = m.TB0 # Bottom-most tray temperature in K - m.Ttop = m.TD0 # Top-most tray temperature in K - m.Tcon = m.TD0 - 5 # Condenser temperature in K + m.Treb = m.TB0 + 5 # Reboiler temperature in K + m.Tbot = m.TB0 # Bottom-most tray temperature in K + m.Ttop = m.TD0 # Top-most tray temperature in K + m.Tcon = m.TD0 - 5 # Condenser temperature in K - m.dv0 = {} # Initial vapor distributor value - m.dl0 = {} # Initial liquid distributor value + m.dv0 = {} # Initial vapor distributor value + m.dl0 = {} # Initial liquid distributor value m.dv0[2] = 0.516 m.dv0[3] = 1 - m.dv0[2] m.dl0[2] = 0.36 m.dl0[3] = 1 - m.dl0[2] - #### Calculated upper and lower bounds m.min_tray = m.Knmin * 0.8 # Lower bound on number of trays - m.Tlo = m.Tcon - 20 # Temperature lower bound - m.Tup = m.Treb + 20 # Temperature upper bound - - m.flow_max = 1e3 # Flowrates upper bound - m.Qmax = 60 # Heat loads upper bound + m.Tlo = m.Tcon - 20 # Temperature lower bound + m.Tup = m.Treb + 20 # Temperature upper bound + m.flow_max = 1e3 # Flowrates upper bound + m.Qmax = 60 # Heat loads upper bound #### Column tray details - m.num_trays = m.np # Trays per section - m.min_num_trays = 10 # Minimum number of trays per section - m.num_total = m.np * 3 # Total number of trays - m.feed_tray = 12 # Side feed tray - m.sideout1_tray = 8 # Side outlet 1 tray - m.sideout2_tray = 17 # Side outlet 2 tray - m.reb_tray = 1 # Reboiler tray - m.con_tray = m.num_trays # Condenser tray - - + m.num_trays = m.np # Trays per section + m.min_num_trays = 10 # Minimum number of trays per section + m.num_total = m.np * 3 # Total number of trays + m.feed_tray = 12 # Side feed tray + m.sideout1_tray = 8 # Side outlet 1 tray + m.sideout2_tray = 17 # Side outlet 2 tray + m.reb_tray = 1 # Reboiler tray + m.con_tray = m.num_trays # Condenser tray # ------------------------------------------------------------------ - + # Beginning of model - - # ------------------------------------------------------------------ + # ------------------------------------------------------------------ ## Sets - m.section = RangeSet(4, - doc="Column sections:1=top, 2=feed side, 3=prod side, 4=bot") + m.section = RangeSet( + 4, doc="Column sections:1=top, 2=feed side, 3=prod side, 4=bot" + ) m.section_main = Set(initialize=[1, 4]) - m.tray = RangeSet(m.np, - doc="Potential trays in each section") - m.tray_total = RangeSet(m.num_total, - doc="Total trays in the column") - m.tray_below_feed = RangeSet(m.feed_tray, - doc="Trays below feed") - m.tray_below_so1 = RangeSet(m.sideout1_tray, - doc="Trays below side outlet 1") - m.tray_below_so2 = RangeSet(m.sideout2_tray, - doc="Trays below side outlet 1") - - m.comp = RangeSet(4, - doc="Components") - m.dw = RangeSet(2, 3, - doc="Dividing wall sections") - m.cplv = RangeSet(2, - doc="Heat capacity: 1=liquid, 2=vapor") - m.so = RangeSet(2, - doc="Side product outlets") - m.bounds = RangeSet(2, - doc="Number of boundary condition values") - - m.candidate_trays_main = Set(initialize=m.tray - - [m.con_tray, m.reb_tray], - doc="Candidate trays for top and \ - bottom sections 1 and 4") - m.candidate_trays_feed = Set(initialize=m.tray - - [m.con_tray, m.feed_tray, m.reb_tray], - doc="Candidate trays for feed section 2") - m.candidate_trays_product = Set(initialize=m.tray - - [m.con_tray, m.sideout1_tray, - m.sideout2_tray, m.reb_tray], - doc="Candidate trays for product section 3") - - + m.tray = RangeSet(m.np, doc="Potential trays in each section") + m.tray_total = RangeSet(m.num_total, doc="Total trays in the column") + m.tray_below_feed = RangeSet(m.feed_tray, doc="Trays below feed") + m.tray_below_so1 = RangeSet(m.sideout1_tray, doc="Trays below side outlet 1") + m.tray_below_so2 = RangeSet(m.sideout2_tray, doc="Trays below side outlet 1") + + m.comp = RangeSet(4, doc="Components") + m.dw = RangeSet(2, 3, doc="Dividing wall sections") + m.cplv = RangeSet(2, doc="Heat capacity: 1=liquid, 2=vapor") + m.so = RangeSet(2, doc="Side product outlets") + m.bounds = RangeSet(2, doc="Number of boundary condition values") + + m.candidate_trays_main = Set( + initialize=m.tray - [m.con_tray, m.reb_tray], + doc="Candidate trays for top and \ + bottom sections 1 and 4", + ) + m.candidate_trays_feed = Set( + initialize=m.tray - [m.con_tray, m.feed_tray, m.reb_tray], + doc="Candidate trays for feed section 2", + ) + m.candidate_trays_product = Set( + initialize=m.tray - [m.con_tray, m.sideout1_tray, m.sideout2_tray, m.reb_tray], + doc="Candidate trays for product section 3", + ) + ## Calculation of initial values - m.dHvap = {} # Heat of vaporization - - m.P0 = {} # Initial pressure - m.T0 = {} # Initial temperature - m.L0 = {} # Initial individual liquid flowrate in mol/s - m.V0 = {} # Initial individual vapor flowrate - m.Vtotal0 = {} # Initial total vapor flowrate in mol/s - m.Ltotal0 = {} # Initial liquid flowrate in mol/s - m.x0 = {} # Initial liquid composition - m.y0 = {} # Initial vapor composition - m.actv0 = {} # Initial activity coefficients - m.cpdT0 = {} # Initial heat capacity for liquid and vapor phases - m.hl0 = {} # Initial liquid enthalpy in J/mol - m.hv0 = {} # Initial vapor enthalpy in J/mol - m.Pi = m.Preb # Initial given pressure value - m.Ti = {} # Initial known temperature values - - + m.dHvap = {} # Heat of vaporization + + m.P0 = {} # Initial pressure + m.T0 = {} # Initial temperature + m.L0 = {} # Initial individual liquid flowrate in mol/s + m.V0 = {} # Initial individual vapor flowrate + m.Vtotal0 = {} # Initial total vapor flowrate in mol/s + m.Ltotal0 = {} # Initial liquid flowrate in mol/s + m.x0 = {} # Initial liquid composition + m.y0 = {} # Initial vapor composition + m.actv0 = {} # Initial activity coefficients + m.cpdT0 = {} # Initial heat capacity for liquid and vapor phases + m.hl0 = {} # Initial liquid enthalpy in J/mol + m.hv0 = {} # Initial vapor enthalpy in J/mol + m.Pi = m.Preb # Initial given pressure value + m.Ti = {} # Initial known temperature values + for sec in m.section: for n_tray in m.tray: m.P0[sec, n_tray] = m.Pi - for sec in m.section: for n_tray in m.tray: for comp in m.comp: @@ -138,25 +129,17 @@ def build_model(): for sec in m.section: for n_tray in m.tray: - m.Ltotal0[sec, n_tray] = sum( - m.L0[sec, n_tray, comp] for comp in m.comp) - m.Vtotal0[sec, n_tray] = sum( - m.V0[sec, n_tray, comp] for comp in m.comp) + m.Ltotal0[sec, n_tray] = sum(m.L0[sec, n_tray, comp] for comp in m.comp) + m.Vtotal0[sec, n_tray] = sum(m.V0[sec, n_tray, comp] for comp in m.comp) - for n_tray in m.tray_total: if n_tray == m.reb_tray: m.Ti[n_tray] = m.Treb elif n_tray == m.num_total: m.Ti[n_tray] = m.Tcon else: - m.Ti[n_tray] = ( - m.Tbot - + (m.Ttop - m.Tbot) * \ - (n_tray - 2) / (m.num_total - 3) - ) + m.Ti[n_tray] = m.Tbot + (m.Ttop - m.Tbot) * (n_tray - 2) / (m.num_total - 3) - for n_tray in m.tray_total: if n_tray <= m.num_trays: m.T0[1, n_tray] = m.Ti[n_tray] @@ -164,7 +147,7 @@ def build_model(): m.T0[2, n_tray - m.num_trays] = m.Ti[n_tray] m.T0[3, n_tray - m.num_trays] = m.Ti[n_tray] elif n_tray >= m.num_trays * 2: - m.T0[4, n_tray - m.num_trays*2] = m.Ti[n_tray] + m.T0[4, n_tray - m.num_trays * 2] = m.Ti[n_tray] for sec in m.section: for n_tray in m.tray: @@ -172,338 +155,391 @@ def build_model(): m.x0[sec, n_tray, comp] = m.xfi[comp] m.actv0[sec, n_tray, comp] = 1 m.y0[sec, n_tray, comp] = m.xfi[comp] - - + ## Enthalpy boundary values - hlb = {} # Liquid enthalpy - hvb = {} # Vapor enthalpy - cpb = {} # Heact capacity - dHvapb = {} # Heat of vaporization - Tbounds = {} # Temperature bounds - kc = {} # Light and heavy key components + hlb = {} # Liquid enthalpy + hvb = {} # Vapor enthalpy + cpb = {} # Heact capacity + dHvapb = {} # Heat of vaporization + Tbounds = {} # Temperature bounds + kc = {} # Light and heavy key components Tbounds[1] = m.Tcon Tbounds[2] = m.Treb kc[1] = m.lc kc[2] = m.hc - for comp in m.comp: dHvapb[comp] = -( - m.Rgas * m.prop[comp, 'TC'] * ( - m.prop[comp, 'vpA'] * \ - (1 - m.Tref / m.prop[comp, 'TC']) - + m.prop[comp, 'vpB'] * \ - (1 - m.Tref / m.prop[comp, 'TC'])**1.5 - + m.prop[comp, 'vpC'] * \ - (1 - m.Tref / m.prop[comp, 'TC'])**3 - + m.prop[comp, 'vpD'] * \ - (1 - m.Tref / m.prop[comp, 'TC'])**6) - + m.Rgas * m.Tref * ( + m.Rgas + * m.prop[comp, 'TC'] + * ( + m.prop[comp, 'vpA'] * (1 - m.Tref / m.prop[comp, 'TC']) + + m.prop[comp, 'vpB'] * (1 - m.Tref / m.prop[comp, 'TC']) ** 1.5 + + m.prop[comp, 'vpC'] * (1 - m.Tref / m.prop[comp, 'TC']) ** 3 + + m.prop[comp, 'vpD'] * (1 - m.Tref / m.prop[comp, 'TC']) ** 6 + ) + + m.Rgas + * m.Tref + * ( m.prop[comp, 'vpA'] - + 1.5 * m.prop[comp, 'vpB'] * \ - (1 - m.Tref / m.prop[comp, 'TC'])**0.5 - + 3 * m.prop[comp, 'vpC'] * \ - (1 - m.Tref / m.prop[comp, 'TC'])**2 - + 6 * m.prop[comp, 'vpD'] * \ - (1 - m.Tref / m.prop[comp, 'TC'])**5 + + 1.5 * m.prop[comp, 'vpB'] * (1 - m.Tref / m.prop[comp, 'TC']) ** 0.5 + + 3 * m.prop[comp, 'vpC'] * (1 - m.Tref / m.prop[comp, 'TC']) ** 2 + + 6 * m.prop[comp, 'vpD'] * (1 - m.Tref / m.prop[comp, 'TC']) ** 5 ) ) - for b in m.bounds: for cp in m.cplv: cpb[b, cp] = m.cpc[cp] * ( - (Tbounds[b] - m.Tref) * \ - m.prop[kc[b], 'cpA', cp] - + (Tbounds[b]**2 - m.Tref**2) * \ - m.prop[kc[b], 'cpB', cp] * \ - m.cpc2['A', cp] / 2 - + (Tbounds[b]**3 - m.Tref**3) * \ - m.prop[kc[b], 'cpC', cp] * \ - m.cpc2['B', cp] / 3 - + (Tbounds[b]**4 - m.Tref**4) * \ - m.prop[kc[b], 'cpD', cp] / 4 + (Tbounds[b] - m.Tref) * m.prop[kc[b], 'cpA', cp] + + (Tbounds[b] ** 2 - m.Tref**2) + * m.prop[kc[b], 'cpB', cp] + * m.cpc2['A', cp] + / 2 + + (Tbounds[b] ** 3 - m.Tref**3) + * m.prop[kc[b], 'cpC', cp] + * m.cpc2['B', cp] + / 3 + + (Tbounds[b] ** 4 - m.Tref**4) * m.prop[kc[b], 'cpD', cp] / 4 ) - hlb[b] = ( - cpb[b, 1] - ) - hvb[b] = ( - cpb[b, 2] - + dHvapb[b] - ) + hlb[b] = cpb[b, 1] + hvb[b] = cpb[b, 2] + dHvapb[b] m.hllo = (1 - copysign(0.2, hlb[1])) * hlb[1] / m.Hscale m.hlup = (1 + copysign(0.2, hlb[2])) * hlb[2] / m.Hscale m.hvlo = (1 - copysign(0.2, hvb[1])) * hvb[1] / m.Hscale m.hvup = (1 + copysign(0.2, hvb[2])) * hvb[2] / m.Hscale - for comp in m.comp: m.dHvap[comp] = dHvapb[comp] / m.Hscale - for sec in m.section: for n_tray in m.tray: for comp in m.comp: for cp in m.cplv: m.cpdT0[sec, n_tray, comp, cp] = ( - m.cpc[cp] * ( - (m.T0[sec, n_tray] - m.Tref) * \ - m.prop[comp, 'cpA', cp] - + (m.T0[sec, n_tray]**2 - m.Tref**2) * \ - m.prop[comp, 'cpB', cp] * \ - m.cpc2['A', cp] / 2 - + (m.T0[sec, n_tray]**3 - m.Tref**3) * \ - m.prop[comp, 'cpC', cp] * \ - m.cpc2['B', cp] / 3 - + (m.T0[sec, n_tray]**4 - m.Tref**4) * \ - m.prop[comp, 'cpD', cp] / 4 - ) / m.Hscale + m.cpc[cp] + * ( + (m.T0[sec, n_tray] - m.Tref) * m.prop[comp, 'cpA', cp] + + (m.T0[sec, n_tray] ** 2 - m.Tref**2) + * m.prop[comp, 'cpB', cp] + * m.cpc2['A', cp] + / 2 + + (m.T0[sec, n_tray] ** 3 - m.Tref**3) + * m.prop[comp, 'cpC', cp] + * m.cpc2['B', cp] + / 3 + + (m.T0[sec, n_tray] ** 4 - m.Tref**4) + * m.prop[comp, 'cpD', cp] + / 4 + ) + / m.Hscale ) - for sec in m.section: for n_tray in m.tray: for comp in m.comp: - m.hl0[sec, n_tray, comp] = ( - m.cpdT0[sec, n_tray, comp, 1] - ) - m.hv0[sec, n_tray, comp] = ( - m.cpdT0[sec, n_tray, comp, 2] - + m.dHvap[comp] - ) + m.hl0[sec, n_tray, comp] = m.cpdT0[sec, n_tray, comp, 1] + m.hv0[sec, n_tray, comp] = m.cpdT0[sec, n_tray, comp, 2] + m.dHvap[comp] #### Side feed - m.cpdTf = {} # Heat capacity for side feed J/mol K - m.hlf = {} # Liquid enthalpy for side feed in J/mol - m.hvf = {} # Vapor enthalpy for side feed in J/mol - m.F0 = {} # Side feed flowrate per component in mol/s + m.cpdTf = {} # Heat capacity for side feed J/mol K + m.hlf = {} # Liquid enthalpy for side feed in J/mol + m.hvf = {} # Vapor enthalpy for side feed in J/mol + m.F0 = {} # Side feed flowrate per component in mol/s for comp in m.comp: for cp in m.cplv: m.cpdTf[comp, cp] = ( - m.cpc[cp]*( - (m.Tf - m.Tref) * \ - m.prop[comp, 'cpA', cp] - + (m.Tf**2 - m.Tref**2) * \ - m.prop[comp, 'cpB', cp] * \ - m.cpc2['A', cp] / 2 - + (m.Tf**3 - m.Tref**3) * \ - m.prop[comp, 'cpC', cp] * \ - m.cpc2['B', cp] / 3 - + (m.Tf**4 - m.Tref**4) * \ - m.prop[comp, 'cpD', cp] / 4 - ) / m.Hscale + m.cpc[cp] + * ( + (m.Tf - m.Tref) * m.prop[comp, 'cpA', cp] + + (m.Tf**2 - m.Tref**2) + * m.prop[comp, 'cpB', cp] + * m.cpc2['A', cp] + / 2 + + (m.Tf**3 - m.Tref**3) + * m.prop[comp, 'cpC', cp] + * m.cpc2['B', cp] + / 3 + + (m.Tf**4 - m.Tref**4) * m.prop[comp, 'cpD', cp] / 4 + ) + / m.Hscale ) - + for comp in m.comp: m.F0[comp] = m.xfi[comp] * m.Fi - m.hlf[comp] = ( - m.cpdTf[comp, 1] - ) - m.hvf[comp] = ( - m.cpdTf[comp, 2] - + m.dHvap[comp] - ) - - m.P = Var(m.section, m.tray, - doc="Pressure at each potential tray in bars", - domain=NonNegativeReals, - bounds=(m.Pcon, m.Preb), - initialize=m.P0) - m.T = Var(m.section, m.tray, - doc="Temperature at each potential tray in K", - domain=NonNegativeReals, - bounds=(m.Tlo, m.Tup), - initialize=m.T0) - - m.x = Var(m.section, m.tray, m.comp, - doc="Liquid composition", - domain=NonNegativeReals, - bounds=(0, 1), - initialize=m.x0) - m.y = Var(m.section, m.tray, m.comp, - doc="Vapor composition", - domain=NonNegativeReals, - bounds=(0, 1), - initialize=m.y0) - - m.dl = Var(m.dw, - doc="Liquid distributor", - bounds=(0.2, 0.8), - initialize=m.dl0) - m.dv = Var(m.dw, - doc="Vapor distributor", - bounds=(0, 1), - domain=NonNegativeReals, - initialize=m.dv0) - - m.V = Var(m.section, m.tray, m.comp, - doc="Vapor flowrate in mol/s", - domain=NonNegativeReals, - bounds=(0, m.flow_max), - initialize=m.V0) - m.L = Var(m.section, m.tray, m.comp, - doc="Liquid flowrate in mol/s", - domain=NonNegativeReals, - bounds=(0, m.flow_max), - initialize=m.L0) - m.Vtotal = Var(m.section, m.tray, - doc="Total vapor flowrate in mol/s", - domain=NonNegativeReals, - bounds=(0, m.flow_max), - initialize=m.Vtotal0) - m.Ltotal = Var(m.section, m.tray, - doc="Total liquid flowrate in mol/s", - domain=NonNegativeReals, - bounds=(0, m.flow_max), - initialize=m.Ltotal0) - - m.D = Var(m.comp, - doc="Distillate flowrate in mol/s", - domain=NonNegativeReals, - bounds=(0, m.flow_max), - initialize=m.Ddes) - m.B = Var(m.comp, - doc="Bottoms flowrate in mol/s", - domain=NonNegativeReals, - bounds=(0, m.flow_max), - initialize=m.Bdes) - m.S = Var(m.so, m.comp, - doc="Product 2 and 3 flowrates in mol/s", - domain=NonNegativeReals, - bounds=(0, m.flow_max), - initialize=m.Sdes) - m.Dtotal = Var(doc="Distillate flowrate in mol/s", - domain=NonNegativeReals, - bounds=(0, m.flow_max), - initialize=m.Ddes) - m.Btotal = Var(doc="Bottoms flowrate in mol/s", - domain=NonNegativeReals, - bounds=(0, m.flow_max), - initialize=m.Bdes) - m.Stotal = Var(m.so, - doc="Total product 2 and 3 side flowrate in mol/s", - domain=NonNegativeReals, - bounds=(0, m.flow_max), - initialize=m.Sdes) - - m.hl = Var(m.section, m.tray, m.comp, - doc='Liquid enthalpy in J/mol', - bounds=(m.hllo, m.hlup), - initialize=m.hl0) - m.hv = Var(m.section, m.tray, m.comp, - doc='Vapor enthalpy in J/mol', - bounds=(m.hvlo, m.hvup), - initialize=m.hv0) - m.Qreb = Var(doc="Reboiler heat duty in J/s", - domain=NonNegativeReals, - bounds=(0, m.Qmax), - initialize=1) - m.Qcon = Var(doc="Condenser heat duty in J/s", - domain=NonNegativeReals, - bounds=(0, m.Qmax), - initialize=1) - - m.rr = Var(doc="Internal reflux ratio", - domain=NonNegativeReals, - bounds=(0.7, 1), - initialize=m.rr0) - m.bu = Var(doc="Boilup rate", - domain=NonNegativeReals, - bounds=(0.7, 1), - initialize=m.bu0) - - m.F = Var(m.comp, - doc="Side feed flowrate in mol/s", - domain=NonNegativeReals, - bounds=(0, 50), - initialize=m.F0) - m.q = Var(doc="Vapor fraction in side feed", - domain=NonNegativeReals, - bounds=(0, 1), - initialize=1) - - m.actv = Var(m.section, m.tray, m.comp, - doc="Liquid activity coefficient", - domain=NonNegativeReals, - bounds=(0, 10), - initialize=m.actv0) - - m.errx = Var(m.section, m.tray, - bounds=(-1e-3, 1e-3), - initialize=0) - m.erry = Var(m.section, m.tray, - bounds=(-1e-3, 1e-3), initialize=0) - m.slack = Var(m.section, m.tray, m.comp, - doc="Slack variable", - bounds=(-1e-8, 1e-8), - initialize=0) - - m.tray_exists = Disjunct(m.section, m.tray, - rule=_build_tray_equations) - m.tray_absent = Disjunct(m.section, m.tray, - rule=_build_pass_through_eqns) - - - @m.Disjunction(m.section, m.tray, - doc="Disjunction between whether each tray exists or not") + m.hlf[comp] = m.cpdTf[comp, 1] + m.hvf[comp] = m.cpdTf[comp, 2] + m.dHvap[comp] + + m.P = Var( + m.section, + m.tray, + doc="Pressure at each potential tray in bars", + domain=NonNegativeReals, + bounds=(m.Pcon, m.Preb), + initialize=m.P0, + ) + m.T = Var( + m.section, + m.tray, + doc="Temperature at each potential tray in K", + domain=NonNegativeReals, + bounds=(m.Tlo, m.Tup), + initialize=m.T0, + ) + + m.x = Var( + m.section, + m.tray, + m.comp, + doc="Liquid composition", + domain=NonNegativeReals, + bounds=(0, 1), + initialize=m.x0, + ) + m.y = Var( + m.section, + m.tray, + m.comp, + doc="Vapor composition", + domain=NonNegativeReals, + bounds=(0, 1), + initialize=m.y0, + ) + + m.dl = Var(m.dw, doc="Liquid distributor", bounds=(0.2, 0.8), initialize=m.dl0) + m.dv = Var( + m.dw, + doc="Vapor distributor", + bounds=(0, 1), + domain=NonNegativeReals, + initialize=m.dv0, + ) + + m.V = Var( + m.section, + m.tray, + m.comp, + doc="Vapor flowrate in mol/s", + domain=NonNegativeReals, + bounds=(0, m.flow_max), + initialize=m.V0, + ) + m.L = Var( + m.section, + m.tray, + m.comp, + doc="Liquid flowrate in mol/s", + domain=NonNegativeReals, + bounds=(0, m.flow_max), + initialize=m.L0, + ) + m.Vtotal = Var( + m.section, + m.tray, + doc="Total vapor flowrate in mol/s", + domain=NonNegativeReals, + bounds=(0, m.flow_max), + initialize=m.Vtotal0, + ) + m.Ltotal = Var( + m.section, + m.tray, + doc="Total liquid flowrate in mol/s", + domain=NonNegativeReals, + bounds=(0, m.flow_max), + initialize=m.Ltotal0, + ) + + m.D = Var( + m.comp, + doc="Distillate flowrate in mol/s", + domain=NonNegativeReals, + bounds=(0, m.flow_max), + initialize=m.Ddes, + ) + m.B = Var( + m.comp, + doc="Bottoms flowrate in mol/s", + domain=NonNegativeReals, + bounds=(0, m.flow_max), + initialize=m.Bdes, + ) + m.S = Var( + m.so, + m.comp, + doc="Product 2 and 3 flowrates in mol/s", + domain=NonNegativeReals, + bounds=(0, m.flow_max), + initialize=m.Sdes, + ) + m.Dtotal = Var( + doc="Distillate flowrate in mol/s", + domain=NonNegativeReals, + bounds=(0, m.flow_max), + initialize=m.Ddes, + ) + m.Btotal = Var( + doc="Bottoms flowrate in mol/s", + domain=NonNegativeReals, + bounds=(0, m.flow_max), + initialize=m.Bdes, + ) + m.Stotal = Var( + m.so, + doc="Total product 2 and 3 side flowrate in mol/s", + domain=NonNegativeReals, + bounds=(0, m.flow_max), + initialize=m.Sdes, + ) + + m.hl = Var( + m.section, + m.tray, + m.comp, + doc='Liquid enthalpy in J/mol', + bounds=(m.hllo, m.hlup), + initialize=m.hl0, + ) + m.hv = Var( + m.section, + m.tray, + m.comp, + doc='Vapor enthalpy in J/mol', + bounds=(m.hvlo, m.hvup), + initialize=m.hv0, + ) + m.Qreb = Var( + doc="Reboiler heat duty in J/s", + domain=NonNegativeReals, + bounds=(0, m.Qmax), + initialize=1, + ) + m.Qcon = Var( + doc="Condenser heat duty in J/s", + domain=NonNegativeReals, + bounds=(0, m.Qmax), + initialize=1, + ) + + m.rr = Var( + doc="Internal reflux ratio", + domain=NonNegativeReals, + bounds=(0.7, 1), + initialize=m.rr0, + ) + m.bu = Var( + doc="Boilup rate", domain=NonNegativeReals, bounds=(0.7, 1), initialize=m.bu0 + ) + + m.F = Var( + m.comp, + doc="Side feed flowrate in mol/s", + domain=NonNegativeReals, + bounds=(0, 50), + initialize=m.F0, + ) + m.q = Var( + doc="Vapor fraction in side feed", + domain=NonNegativeReals, + bounds=(0, 1), + initialize=1, + ) + + m.actv = Var( + m.section, + m.tray, + m.comp, + doc="Liquid activity coefficient", + domain=NonNegativeReals, + bounds=(0, 10), + initialize=m.actv0, + ) + + m.errx = Var(m.section, m.tray, bounds=(-1e-3, 1e-3), initialize=0) + m.erry = Var(m.section, m.tray, bounds=(-1e-3, 1e-3), initialize=0) + m.slack = Var( + m.section, + m.tray, + m.comp, + doc="Slack variable", + bounds=(-1e-8, 1e-8), + initialize=0, + ) + + m.tray_exists = Disjunct(m.section, m.tray, rule=_build_tray_equations) + m.tray_absent = Disjunct(m.section, m.tray, rule=_build_pass_through_eqns) + + @m.Disjunction( + m.section, m.tray, doc="Disjunction between whether each tray exists or not" + ) def tray_exists_or_not(m, sec, n_tray): return [m.tray_exists[sec, n_tray], m.tray_absent[sec, n_tray]] @m.Constraint(m.section_main) def minimum_trays_main(m, sec): - return sum(m.tray_exists[sec, n_tray].binary_indicator_var - for n_tray in m.candidate_trays_main) + 1 >= m.min_num_trays + return ( + sum( + m.tray_exists[sec, n_tray].binary_indicator_var + for n_tray in m.candidate_trays_main + ) + + 1 + >= m.min_num_trays + ) @m.Constraint() def minimum_trays_feed(m): - return sum(m.tray_exists[2, n_tray].binary_indicator_var - for n_tray in m.candidate_trays_feed) + 1 >= m.min_num_trays + return ( + sum( + m.tray_exists[2, n_tray].binary_indicator_var + for n_tray in m.candidate_trays_feed + ) + + 1 + >= m.min_num_trays + ) @m.Constraint() def minimum_trays_product(m): - return sum(m.tray_exists[3, n_tray].binary_indicator_var - for n_tray in m.candidate_trays_product) + 1 >= m.min_num_trays + return ( + sum( + m.tray_exists[3, n_tray].binary_indicator_var + for n_tray in m.candidate_trays_product + ) + + 1 + >= m.min_num_trays + ) - ## Fixed trays - enforce_tray_exists(m, 1, 1) # reboiler - enforce_tray_exists(m, 1, m.num_trays) # vapor distributor - enforce_tray_exists(m, 2, 1) # dividing wall starting tray - enforce_tray_exists(m, 2, m.feed_tray) # feed tray - enforce_tray_exists(m, 2, m.num_trays) # dividing wall ending tray - enforce_tray_exists(m, 3, 1) # dividing wall starting tray - enforce_tray_exists(m, 3, m.sideout1_tray)# side outlet 1 for product 3 - enforce_tray_exists(m, 3, m.sideout2_tray)# side outlet 2 for product 2 - enforce_tray_exists(m, 3, m.num_trays) # dividing wall ending tray - enforce_tray_exists(m, 4, 1) # liquid distributor - enforce_tray_exists(m, 4, m.num_trays) # condenser - - - + enforce_tray_exists(m, 1, 1) # reboiler + enforce_tray_exists(m, 1, m.num_trays) # vapor distributor + enforce_tray_exists(m, 2, 1) # dividing wall starting tray + enforce_tray_exists(m, 2, m.feed_tray) # feed tray + enforce_tray_exists(m, 2, m.num_trays) # dividing wall ending tray + enforce_tray_exists(m, 3, 1) # dividing wall starting tray + enforce_tray_exists(m, 3, m.sideout1_tray) # side outlet 1 for product 3 + enforce_tray_exists(m, 3, m.sideout2_tray) # side outlet 2 for product 2 + enforce_tray_exists(m, 3, m.num_trays) # dividing wall ending tray + enforce_tray_exists(m, 4, 1) # liquid distributor + enforce_tray_exists(m, 4, m.num_trays) # condenser #### Global constraints @m.Constraint(m.dw, m.tray, doc="Monotonic temperature") def monotonic_temperature(m, sec, n_tray): return m.T[sec, n_tray] <= m.T[1, m.num_trays] - @m.Constraint(doc="Liquid distributor") def liquid_distributor(m): return sum(m.dl[sec] for sec in m.dw) - 1 == 0 - @m.Constraint(doc="Vapor distributor") def vapor_distributor(m): return sum(m.dv[sec] for sec in m.dw) - 1 == 0 - @m.Constraint(doc="Reboiler composition specification") def heavy_product(m): return m.x[1, m.reb_tray, m.hc] >= m.xspec_hc - @m.Constraint(doc="Condenser composition specification") def light_product(m): return m.x[4, m.con_tray, m.lc] >= m.xspec_lc @@ -512,143 +548,146 @@ def light_product(m): def intermediate1_product(m): return m.x[3, m.sideout1_tray, 3] >= m.xspec_inter3 - @m.Constraint(doc="Side outlet 2 final liquid composition") def intermediate2_product(m): return m.x[3, m.sideout2_tray, 2] >= m.xspec_inter2 - @m.Constraint(doc="Reboiler flowrate") def _heavy_product_flow(m): - return m.Btotal >= m.Bdes + return m.Btotal >= m.Bdes - @m.Constraint(doc="Condenser flowrate") def _light_product_flow(m): - return m.Dtotal >= m.Ddes + return m.Dtotal >= m.Ddes - @m.Constraint(m.so, doc="Intermediate flowrate") def _intermediate_product_flow(m, so): - return m.Stotal[so] >= m.Sdes + return m.Stotal[so] >= m.Sdes - @m.Constraint(doc="Internal boilup ratio, V/L") def _internal_boilup_ratio(m): return m.bu * m.Ltotal[1, m.reb_tray + 1] == m.Vtotal[1, m.reb_tray] - @m.Constraint(doc="Internal reflux ratio, L/V") def internal_reflux_ratio(m): return m.rr * m.Vtotal[4, m.con_tray - 1] == m.Ltotal[4, m.con_tray] - @m.Constraint(doc="External boilup ratio relation with bottoms") def _external_boilup_ratio(m): - return m.Btotal == (1 - m.bu) * m.Ltotal[1, m.reb_tray + 1] - + return m.Btotal == (1 - m.bu) * m.Ltotal[1, m.reb_tray + 1] @m.Constraint(doc="External reflux ratio relation with distillate") def _external_reflux_ratio(m): - return m.Dtotal == (1 - m.rr) * m.Vtotal[4, m.con_tray - 1] + return m.Dtotal == (1 - m.rr) * m.Vtotal[4, m.con_tray - 1] - @m.Constraint(m.section, m.tray, doc="Total vapor flowrate") def _total_vapor_flowrate(m, sec, n_tray): - return sum(m.V[sec, n_tray, comp] for comp in m.comp) == m.Vtotal[sec, n_tray] - + return sum(m.V[sec, n_tray, comp] for comp in m.comp) == m.Vtotal[sec, n_tray] @m.Constraint(m.section, m.tray, doc="Total liquid flowrate") def _total_liquid_flowrate(m, sec, n_tray): - return sum(m.L[sec, n_tray, comp] for comp in m.comp) == m.Ltotal[sec, n_tray] - + return sum(m.L[sec, n_tray, comp] for comp in m.comp) == m.Ltotal[sec, n_tray] @m.Constraint(m.comp, doc="Bottoms and liquid relation") def bottoms_equality(m, comp): return m.B[comp] == m.L[1, m.reb_tray, comp] - @m.Constraint(m.comp) def condenser_total(m, comp): - return m.V[4, m.con_tray, comp] == 0 + return m.V[4, m.con_tray, comp] == 0 - @m.Constraint() def total_bottoms_product(m): return sum(m.B[comp] for comp in m.comp) == m.Btotal - @m.Constraint() def total_distillate_product(m): return sum(m.D[comp] for comp in m.comp) == m.Dtotal - @m.Constraint(m.so) def total_side_product(m, so): return sum(m.S[so, comp] for comp in m.comp) == m.Stotal[so] - - - m.obj = Objective( - expr= (m.Qcon + m.Qreb) * m.Hscale + 1e3 * ( + expr=(m.Qcon + m.Qreb) * m.Hscale + + 1e3 + * ( sum( - sum(m.tray_exists[sec, n_tray].binary_indicator_var - for n_tray in m.candidate_trays_main) - for sec in m.section_main) - + sum(m.tray_exists[2, n_tray].binary_indicator_var - for n_tray in m.candidate_trays_feed) - + sum(m.tray_exists[3, n_tray].binary_indicator_var - for n_tray in m.candidate_trays_product) - + 1), - sense=minimize) - - - - + sum( + m.tray_exists[sec, n_tray].binary_indicator_var + for n_tray in m.candidate_trays_main + ) + for sec in m.section_main + ) + + sum( + m.tray_exists[2, n_tray].binary_indicator_var + for n_tray in m.candidate_trays_feed + ) + + sum( + m.tray_exists[3, n_tray].binary_indicator_var + for n_tray in m.candidate_trays_product + ) + + 1 + ), + sense=minimize, + ) @m.Constraint(m.section_main, m.candidate_trays_main) def _logic_proposition_main(m, sec, n_tray): if n_tray > m.reb_tray and (n_tray + 1) < m.num_trays: - return m.tray_exists[sec, n_tray].binary_indicator_var <= m.tray_exists[sec, n_tray + 1].binary_indicator_var + return ( + m.tray_exists[sec, n_tray].binary_indicator_var + <= m.tray_exists[sec, n_tray + 1].binary_indicator_var + ) else: return Constraint.NoConstraint - @m.Constraint(m.candidate_trays_feed) def _logic_proposition_feed(m, n_tray): if n_tray > m.reb_tray and (n_tray + 1) < m.feed_tray: - return m.tray_exists[2, n_tray].binary_indicator_var <= m.tray_exists[2, n_tray + 1].binary_indicator_var + return ( + m.tray_exists[2, n_tray].binary_indicator_var + <= m.tray_exists[2, n_tray + 1].binary_indicator_var + ) elif n_tray > m.feed_tray and (n_tray + 1) < m.con_tray: - return m.tray_exists[2, n_tray + 1].binary_indicator_var <= m.tray_exists[2, n_tray].binary_indicator_var + return ( + m.tray_exists[2, n_tray + 1].binary_indicator_var + <= m.tray_exists[2, n_tray].binary_indicator_var + ) else: return Constraint.NoConstraint - @m.Constraint(m.candidate_trays_product) def _logic_proposition_section3(m, n_tray): if n_tray > 1 and (n_tray + 1) < m.num_trays: - return m.tray_exists[3, n_tray].binary_indicator_var <= m.tray_exists[3, n_tray + 1].binary_indicator_var + return ( + m.tray_exists[3, n_tray].binary_indicator_var + <= m.tray_exists[3, n_tray + 1].binary_indicator_var + ) else: return Constraint.NoConstraint - @m.Constraint(m.tray) def equality_feed_product_side(m, n_tray): - return m.tray_exists[2, n_tray].binary_indicator_var == m.tray_exists[3, n_tray].binary_indicator_var - + return ( + m.tray_exists[2, n_tray].binary_indicator_var + == m.tray_exists[3, n_tray].binary_indicator_var + ) @m.Constraint() def _existent_minimum_numbertrays(m): return sum( - sum(m.tray_exists[sec, n_tray].binary_indicator_var - for n_tray in m.tray) for sec in m.section) - sum(m.tray_exists[3, n_tray].binary_indicator_var for n_tray in m.tray) >= int(m.min_tray) - + sum(m.tray_exists[sec, n_tray].binary_indicator_var for n_tray in m.tray) + for sec in m.section + ) - sum( + m.tray_exists[3, n_tray].binary_indicator_var for n_tray in m.tray + ) >= int( + m.min_tray + ) return m - def enforce_tray_exists(m, sec, n_tray): m.tray_exists[sec, n_tray].indicator_var.fix(True) m.tray_absent[sec, n_tray].deactivate() @@ -659,618 +698,652 @@ def _build_tray_equations(m, sec, n_tray): 1: _build_bottom_equations, 2: _build_feed_side_equations, 3: _build_product_side_equations, - 4: _build_top_equations + 4: _build_top_equations, } build_function[sec](m, n_tray) - def _build_bottom_equations(disj, n_tray): m = disj.model() - @disj.Constraint(m.comp, - doc="Bottom section 1 mass per component balances") + @disj.Constraint(m.comp, doc="Bottom section 1 mass per component balances") def _bottom_mass_percomponent_balances(disj, comp): - return ( - (m.L[1, n_tray + 1, comp] - if n_tray < m.num_trays else 0) - + (m.L[2, 1, comp] - if n_tray == m.num_trays else 0) - + (m.L[3, 1, comp] - if n_tray == m.num_trays else 0) - - (m.L[1, n_tray, comp] - if n_tray > m.reb_tray else 0) - + (m.V[1, n_tray - 1, comp] - if n_tray > m.reb_tray else 0) - - (m.V[1, n_tray, comp] * m.dv[2] - if n_tray == m.num_trays else 0) - - (m.V[1, n_tray, comp] * m.dv[3] - if n_tray == m.num_trays else 0) - - (m.V[1, n_tray, comp] - if n_tray < m.num_trays else 0) - - (m.B[comp] - if n_tray == m.reb_tray else 0) - == m.slack[1, n_tray, comp] - ) - + return (m.L[1, n_tray + 1, comp] if n_tray < m.num_trays else 0) + ( + m.L[2, 1, comp] if n_tray == m.num_trays else 0 + ) + (m.L[3, 1, comp] if n_tray == m.num_trays else 0) - ( + m.L[1, n_tray, comp] if n_tray > m.reb_tray else 0 + ) + ( + m.V[1, n_tray - 1, comp] if n_tray > m.reb_tray else 0 + ) - ( + m.V[1, n_tray, comp] * m.dv[2] if n_tray == m.num_trays else 0 + ) - ( + m.V[1, n_tray, comp] * m.dv[3] if n_tray == m.num_trays else 0 + ) - ( + m.V[1, n_tray, comp] if n_tray < m.num_trays else 0 + ) - ( + m.B[comp] if n_tray == m.reb_tray else 0 + ) == m.slack[ + 1, n_tray, comp + ] + @disj.Constraint(doc="Bottom section 1 energy balances") def _bottom_energy_balances(disj): return ( sum( - (m.L[1, n_tray + 1, comp] * m.hl[1, n_tray + 1, comp] - if n_tray < m.num_trays else 0) - + (m.L[2, 1, comp] * m.hl[2, 1, comp] - if n_tray == m.num_trays else 0) - + (m.L[3, 1, comp] * m.hl[3, 1, comp] - if n_tray == m.num_trays else 0) - - (m.L[1, n_tray, comp] * m.hl[1, n_tray, comp] - if n_tray > m.reb_tray else 0) - + (m.V[1, n_tray - 1, comp] * m.hv[1, n_tray - 1, comp] - if n_tray > m.reb_tray else 0) - - (m.V[1, n_tray, comp] * m.dv[2] * m.hv[1, n_tray, comp] - if n_tray == m.num_trays else 0) - - (m.V[1, n_tray, comp] * m.dv[3] * m.hv[1, n_tray, comp] - if n_tray == m.num_trays else 0) - - (m.V[1, n_tray, comp] * m.hv[1, n_tray, comp] - if n_tray < m.num_trays else 0) - - (m.B[comp] * m.hl[1, n_tray, comp] - if n_tray == m.reb_tray else 0) - for comp in m.comp) * m.Qscale - + (m.Qreb if n_tray == m.reb_tray else 0) - ==0 + ( + m.L[1, n_tray + 1, comp] * m.hl[1, n_tray + 1, comp] + if n_tray < m.num_trays + else 0 + ) + + (m.L[2, 1, comp] * m.hl[2, 1, comp] if n_tray == m.num_trays else 0) + + (m.L[3, 1, comp] * m.hl[3, 1, comp] if n_tray == m.num_trays else 0) + - ( + m.L[1, n_tray, comp] * m.hl[1, n_tray, comp] + if n_tray > m.reb_tray + else 0 + ) + + ( + m.V[1, n_tray - 1, comp] * m.hv[1, n_tray - 1, comp] + if n_tray > m.reb_tray + else 0 + ) + - ( + m.V[1, n_tray, comp] * m.dv[2] * m.hv[1, n_tray, comp] + if n_tray == m.num_trays + else 0 + ) + - ( + m.V[1, n_tray, comp] * m.dv[3] * m.hv[1, n_tray, comp] + if n_tray == m.num_trays + else 0 + ) + - ( + m.V[1, n_tray, comp] * m.hv[1, n_tray, comp] + if n_tray < m.num_trays + else 0 + ) + - (m.B[comp] * m.hl[1, n_tray, comp] if n_tray == m.reb_tray else 0) + for comp in m.comp + ) + * m.Qscale + + (m.Qreb if n_tray == m.reb_tray else 0) + == 0 ) - - @disj.Constraint(m.comp, - doc="Bottom section 1 liquid flowrate per component") + @disj.Constraint(m.comp, doc="Bottom section 1 liquid flowrate per component") def _bottom_liquid_percomponent(disj, comp): return m.L[1, n_tray, comp] == m.Ltotal[1, n_tray] * m.x[1, n_tray, comp] - - @disj.Constraint(m.comp, - doc="Bottom section 1 vapor flowrate per component") + @disj.Constraint(m.comp, doc="Bottom section 1 vapor flowrate per component") def _bottom_vapor_percomponent(disj, comp): - return m.V[1, n_tray, comp] == m.Vtotal[1, n_tray] * m.y[1, n_tray, comp] - + return m.V[1, n_tray, comp] == m.Vtotal[1, n_tray] * m.y[1, n_tray, comp] @disj.Constraint(doc="Bottom section 1 liquid composition equilibrium summation") def bottom_liquid_composition_summation(disj): return sum(m.x[1, n_tray, comp] for comp in m.comp) - 1 == m.errx[1, n_tray] - @disj.Constraint(doc="Bottom section 1 vapor composition equilibrium summation") def bottom_vapor_composition_summation(disj): return sum(m.y[1, n_tray, comp] for comp in m.comp) - 1 == m.erry[1, n_tray] - - @disj.Constraint(m.comp, - doc="Bottom section 1 vapor composition") + @disj.Constraint(m.comp, doc="Bottom section 1 vapor composition") def bottom_vapor_composition(disj, comp): - return m.y[1, n_tray, comp] == m.x[1, n_tray, comp] * ( - m.actv[1, n_tray, comp] * ( - m.prop[comp, 'PC'] * exp( - m.prop[comp, 'TC'] / m.T[1, n_tray] * ( - m.prop[comp, 'vpA'] * \ - (1 - m.T[1, n_tray] / m.prop[comp, 'TC']) - + m.prop[comp, 'vpB'] * \ - (1 - m.T[1, n_tray]/m.prop[comp, 'TC'])**1.5 - + m.prop[comp, 'vpC'] * \ - (1 - m.T[1, n_tray]/m.prop[comp, 'TC'])**3 - + m.prop[comp, 'vpD'] * \ - (1 - m.T[1, n_tray]/m.prop[comp, 'TC'])**6 + return ( + m.y[1, n_tray, comp] + == m.x[1, n_tray, comp] + * ( + m.actv[1, n_tray, comp] + * ( + m.prop[comp, 'PC'] + * exp( + m.prop[comp, 'TC'] + / m.T[1, n_tray] + * ( + m.prop[comp, 'vpA'] + * (1 - m.T[1, n_tray] / m.prop[comp, 'TC']) + + m.prop[comp, 'vpB'] + * (1 - m.T[1, n_tray] / m.prop[comp, 'TC']) ** 1.5 + + m.prop[comp, 'vpC'] + * (1 - m.T[1, n_tray] / m.prop[comp, 'TC']) ** 3 + + m.prop[comp, 'vpD'] + * (1 - m.T[1, n_tray] / m.prop[comp, 'TC']) ** 6 + ) ) ) ) - ) / m.P[1, n_tray] - + / m.P[1, n_tray] + ) - - @disj.Constraint(m.comp, - doc="Bottom section 1 liquid enthalpy") + @disj.Constraint(m.comp, doc="Bottom section 1 liquid enthalpy") def bottom_liquid_enthalpy(disj, comp): return m.hl[1, n_tray, comp] == ( - m.cpc[1] * ( - (m.T[1, n_tray] - m.Tref) * \ - m.prop[comp, 'cpA', 1] - + (m.T[1, n_tray]**2 - m.Tref**2) * \ - m.prop[comp, 'cpB', 1] * m.cpc2['A', 1] / 2 - + (m.T[1, n_tray]**3 - m.Tref**3) * \ - m.prop[comp, 'cpC', 1] * m.cpc2['B', 1] / 3 - + (m.T[1, n_tray]**4 - m.Tref**4) * \ - m.prop[comp, 'cpD', 1] / 4 - ) / m.Hscale - ) - - - @disj.Constraint(m.comp, - doc="Bottom section 1 vapor enthalpy") + m.cpc[1] + * ( + (m.T[1, n_tray] - m.Tref) * m.prop[comp, 'cpA', 1] + + (m.T[1, n_tray] ** 2 - m.Tref**2) + * m.prop[comp, 'cpB', 1] + * m.cpc2['A', 1] + / 2 + + (m.T[1, n_tray] ** 3 - m.Tref**3) + * m.prop[comp, 'cpC', 1] + * m.cpc2['B', 1] + / 3 + + (m.T[1, n_tray] ** 4 - m.Tref**4) * m.prop[comp, 'cpD', 1] / 4 + ) + / m.Hscale + ) + + @disj.Constraint(m.comp, doc="Bottom section 1 vapor enthalpy") def bottom_vapor_enthalpy(disj, comp): return m.hv[1, n_tray, comp] == ( - m.cpc[2] * ( - (m.T[1, n_tray] - m.Tref) * \ - m.prop[comp, 'cpA', 2] - + (m.T[1, n_tray]**2 - m.Tref**2) * \ - m.prop[comp, 'cpB', 2] * m.cpc2['A', 2] / 2 - + (m.T[1, n_tray]**3 - m.Tref**3) * \ - m.prop[comp, 'cpC', 2] * m.cpc2['B', 2] / 3 - + (m.T[1, n_tray]**4 - m.Tref**4) * \ - m.prop[comp, 'cpD', 2] / 4 - ) / m.Hscale + m.cpc[2] + * ( + (m.T[1, n_tray] - m.Tref) * m.prop[comp, 'cpA', 2] + + (m.T[1, n_tray] ** 2 - m.Tref**2) + * m.prop[comp, 'cpB', 2] + * m.cpc2['A', 2] + / 2 + + (m.T[1, n_tray] ** 3 - m.Tref**3) + * m.prop[comp, 'cpC', 2] + * m.cpc2['B', 2] + / 3 + + (m.T[1, n_tray] ** 4 - m.Tref**4) * m.prop[comp, 'cpD', 2] / 4 + ) + / m.Hscale + m.dHvap[comp] - ) + ) - @disj.Constraint(m.comp, - doc="Bottom section 1 liquid activity coefficient") + @disj.Constraint(m.comp, doc="Bottom section 1 liquid activity coefficient") def bottom_activity_coefficient(disj, comp): - return m.actv[1, n_tray, comp] == 1 - + return m.actv[1, n_tray, comp] == 1 - - def _build_feed_side_equations(disj, n_tray): m = disj.model() - @disj.Constraint(m.comp, - doc="Feed section 2 mass per component balances") + @disj.Constraint(m.comp, doc="Feed section 2 mass per component balances") def _feedside_masspercomponent_balances(disj, comp): - return ( - (m.L[2, n_tray + 1, comp] - if n_tray < m.num_trays else 0) - + (m.L[4, 1, comp] * m.dl[2] - if n_tray == m.num_trays else 0) - - m.L[2, n_tray, comp] - + (m.V[1, m.num_trays, comp] * m.dv[2] - if n_tray == 1 else 0) - + (m.V[2, n_tray - 1, comp] - if n_tray > 1 else 0) - - m.V[2, n_tray, comp] - + (m.F[comp] - if n_tray == m.feed_tray else 0) - == 0 - ) - + return (m.L[2, n_tray + 1, comp] if n_tray < m.num_trays else 0) + ( + m.L[4, 1, comp] * m.dl[2] if n_tray == m.num_trays else 0 + ) - m.L[2, n_tray, comp] + ( + m.V[1, m.num_trays, comp] * m.dv[2] if n_tray == 1 else 0 + ) + ( + m.V[2, n_tray - 1, comp] if n_tray > 1 else 0 + ) - m.V[ + 2, n_tray, comp + ] + ( + m.F[comp] if n_tray == m.feed_tray else 0 + ) == 0 @disj.Constraint(doc="Feed section 2 energy balances") def _feedside_energy_balances(disj): return ( sum( - (m.L[2, n_tray + 1, comp] * m.hl[2, n_tray + 1, comp] - if n_tray < m.num_trays else 0) - + (m.L[4, 1, comp] * m.dl[2] * m.hl[4, 1, comp] - if n_tray == m.num_trays else 0) + ( + m.L[2, n_tray + 1, comp] * m.hl[2, n_tray + 1, comp] + if n_tray < m.num_trays + else 0 + ) + + ( + m.L[4, 1, comp] * m.dl[2] * m.hl[4, 1, comp] + if n_tray == m.num_trays + else 0 + ) - m.L[2, n_tray, comp] * m.hl[2, n_tray, comp] - + (m.V[1, m.num_trays, comp] * m.dv[2] * m.hv[1, m.num_trays, comp] - if n_tray == 1 else 0) - + (m.V[2, n_tray - 1, comp] * m.hv[2, n_tray - 1, comp] - if n_tray > 1 else 0) + + ( + m.V[1, m.num_trays, comp] * m.dv[2] * m.hv[1, m.num_trays, comp] + if n_tray == 1 + else 0 + ) + + ( + m.V[2, n_tray - 1, comp] * m.hv[2, n_tray - 1, comp] + if n_tray > 1 + else 0 + ) - m.V[2, n_tray, comp] * m.hv[2, n_tray, comp] - for comp in m.comp) * m.Qscale + for comp in m.comp + ) + * m.Qscale + sum( - (m.F[comp] * (m.hlf[comp] * (1 - m.q) + m.hvf[comp] * m.q) - if n_tray == m.feed_tray else 0) - for comp in m.comp) * m.Qscale - ==0 + ( + m.F[comp] * (m.hlf[comp] * (1 - m.q) + m.hvf[comp] * m.q) + if n_tray == m.feed_tray + else 0 + ) + for comp in m.comp + ) + * m.Qscale + == 0 ) - - @disj.Constraint(m.comp, - doc="Feed section 2 liquid flowrate per component") + @disj.Constraint(m.comp, doc="Feed section 2 liquid flowrate per component") def _feedside_liquid_percomponent(disj, comp): return m.L[2, n_tray, comp] == m.Ltotal[2, n_tray] * m.x[2, n_tray, comp] - - @disj.Constraint(m.comp, - doc="Feed section 2 vapor flowrate per component") + @disj.Constraint(m.comp, doc="Feed section 2 vapor flowrate per component") def _feedside_vapor_percomponent(disj, comp): - return m.V[2, n_tray, comp] == m.Vtotal[2, n_tray] * m.y[2, n_tray, comp] - + return m.V[2, n_tray, comp] == m.Vtotal[2, n_tray] * m.y[2, n_tray, comp] @disj.Constraint(doc="Feed section 2 liquid composition equilibrium summation") def feedside_liquid_composition_summation(disj): return sum(m.x[2, n_tray, comp] for comp in m.comp) - 1 == m.errx[2, n_tray] - @disj.Constraint(doc="Feed section 2 vapor composition equilibrium summation") def feedside_vapor_composition_summation(disj): return sum(m.y[2, n_tray, comp] for comp in m.comp) - 1 == m.erry[2, n_tray] - - @disj.Constraint(m.comp, - doc="Feed section 2 vapor composition") + @disj.Constraint(m.comp, doc="Feed section 2 vapor composition") def feedside_vapor_composition(disj, comp): - return m.y[2, n_tray, comp] == m.x[2, n_tray, comp] * ( - m.actv[2, n_tray, comp] * ( - m.prop[comp, 'PC'] * exp( - m.prop[comp, 'TC'] / m.T[2, n_tray] * ( - m.prop[comp, 'vpA'] * \ - (1 - m.T[2, n_tray] / m.prop[comp, 'TC']) - + m.prop[comp, 'vpB'] * \ - (1 - m.T[2, n_tray] / m.prop[comp, 'TC'])**1.5 - + m.prop[comp, 'vpC'] * \ - (1 - m.T[2, n_tray] / m.prop[comp, 'TC'])**3 - + m.prop[comp, 'vpD'] * \ - (1 - m.T[2, n_tray] / m.prop[comp, 'TC'])**6 + return ( + m.y[2, n_tray, comp] + == m.x[2, n_tray, comp] + * ( + m.actv[2, n_tray, comp] + * ( + m.prop[comp, 'PC'] + * exp( + m.prop[comp, 'TC'] + / m.T[2, n_tray] + * ( + m.prop[comp, 'vpA'] + * (1 - m.T[2, n_tray] / m.prop[comp, 'TC']) + + m.prop[comp, 'vpB'] + * (1 - m.T[2, n_tray] / m.prop[comp, 'TC']) ** 1.5 + + m.prop[comp, 'vpC'] + * (1 - m.T[2, n_tray] / m.prop[comp, 'TC']) ** 3 + + m.prop[comp, 'vpD'] + * (1 - m.T[2, n_tray] / m.prop[comp, 'TC']) ** 6 + ) ) ) ) - ) / m.P[2, n_tray] - - + / m.P[2, n_tray] + ) - @disj.Constraint(m.comp, - doc="Feed section 2 liquid enthalpy") + @disj.Constraint(m.comp, doc="Feed section 2 liquid enthalpy") def feedside_liquid_enthalpy(disj, comp): return m.hl[2, n_tray, comp] == ( - m.cpc[1] * ( - (m.T[2, n_tray] - m.Tref) * \ - m.prop[comp, 'cpA', 1] - + (m.T[2, n_tray]**2 - m.Tref**2) * \ - m.prop[comp, 'cpB', 1] * m.cpc2['A', 1] / 2 - + (m.T[2, n_tray]**3 - m.Tref**3) * \ - m.prop[comp, 'cpC', 1] * m.cpc2['B', 1] / 3 - + (m.T[2, n_tray]**4 - m.Tref**4) * \ - m.prop[comp, 'cpD', 1] / 4 - ) / m.Hscale - ) - - - @disj.Constraint(m.comp, - doc="Feed section 2 vapor enthalpy") + m.cpc[1] + * ( + (m.T[2, n_tray] - m.Tref) * m.prop[comp, 'cpA', 1] + + (m.T[2, n_tray] ** 2 - m.Tref**2) + * m.prop[comp, 'cpB', 1] + * m.cpc2['A', 1] + / 2 + + (m.T[2, n_tray] ** 3 - m.Tref**3) + * m.prop[comp, 'cpC', 1] + * m.cpc2['B', 1] + / 3 + + (m.T[2, n_tray] ** 4 - m.Tref**4) * m.prop[comp, 'cpD', 1] / 4 + ) + / m.Hscale + ) + + @disj.Constraint(m.comp, doc="Feed section 2 vapor enthalpy") def feedside_vapor_enthalpy(disj, comp): return m.hv[2, n_tray, comp] == ( - m.cpc[2] * ( - (m.T[2, n_tray] - m.Tref) * \ - m.prop[comp, 'cpA', 2] - + (m.T[2, n_tray]**2 - m.Tref**2) * \ - m.prop[comp, 'cpB', 2] * m.cpc2['A', 2] / 2 - + (m.T[2, n_tray]**3 - m.Tref**3) * \ - m.prop[comp, 'cpC', 2] * m.cpc2['B', 2] / 3 - + (m.T[2, n_tray]**4 - m.Tref**4) * \ - m.prop[comp, 'cpD', 2] / 4 - ) / m.Hscale + m.cpc[2] + * ( + (m.T[2, n_tray] - m.Tref) * m.prop[comp, 'cpA', 2] + + (m.T[2, n_tray] ** 2 - m.Tref**2) + * m.prop[comp, 'cpB', 2] + * m.cpc2['A', 2] + / 2 + + (m.T[2, n_tray] ** 3 - m.Tref**3) + * m.prop[comp, 'cpC', 2] + * m.cpc2['B', 2] + / 3 + + (m.T[2, n_tray] ** 4 - m.Tref**4) * m.prop[comp, 'cpD', 2] / 4 + ) + / m.Hscale + m.dHvap[comp] - ) + ) - - @disj.Constraint(m.comp, - doc="Feed section 2 liquid activity coefficient") + @disj.Constraint(m.comp, doc="Feed section 2 liquid activity coefficient") def feedside_activity_coefficient(disj, comp): - return m.actv[2, n_tray, comp] == 1 + return m.actv[2, n_tray, comp] == 1 - - - def _build_product_side_equations(disj, n_tray): m = disj.model() - @disj.Constraint(m.comp, - doc="Product section 3 mass per component balances") + @disj.Constraint(m.comp, doc="Product section 3 mass per component balances") def _productside_masspercomponent_balances(disj, comp): - return ( - (m.L[3, n_tray + 1, comp] - if n_tray < m.num_trays else 0) - + (m.L[4, 1, comp] * m.dl[3] - if n_tray == m.num_trays else 0) - - m.L[3, n_tray, comp] - + (m.V[1, m.num_trays, comp] * m.dv[3] - if n_tray == 1 else 0) - + (m.V[3, n_tray - 1, comp] - if n_tray > 1 else 0) - - m.V[3, n_tray, comp] - - (m.S[1, comp] - if n_tray == m.sideout1_tray else 0) - - (m.S[2, comp] - if n_tray == m.sideout2_tray else 0) - ==0 - ) + return (m.L[3, n_tray + 1, comp] if n_tray < m.num_trays else 0) + ( + m.L[4, 1, comp] * m.dl[3] if n_tray == m.num_trays else 0 + ) - m.L[3, n_tray, comp] + ( + m.V[1, m.num_trays, comp] * m.dv[3] if n_tray == 1 else 0 + ) + ( + m.V[3, n_tray - 1, comp] if n_tray > 1 else 0 + ) - m.V[ + 3, n_tray, comp + ] - ( + m.S[1, comp] if n_tray == m.sideout1_tray else 0 + ) - ( + m.S[2, comp] if n_tray == m.sideout2_tray else 0 + ) == 0 - @disj.Constraint(doc="Product section 3 energy balances") def _productside_energy_balances(disj): return ( sum( - (m.L[3, n_tray + 1, comp] * m.hl[3, n_tray + 1, comp] - if n_tray < m.num_trays else 0) - + (m.L[4, 1, comp] * m.dl[3] * m.hl[4, 1, comp] - if n_tray == m.num_trays else 0) + ( + m.L[3, n_tray + 1, comp] * m.hl[3, n_tray + 1, comp] + if n_tray < m.num_trays + else 0 + ) + + ( + m.L[4, 1, comp] * m.dl[3] * m.hl[4, 1, comp] + if n_tray == m.num_trays + else 0 + ) - m.L[3, n_tray, comp] * m.hl[3, n_tray, comp] - + (m.V[1, m.num_trays, comp] * m.dv[3] * m.hv[1, m.num_trays, comp] - if n_tray == 1 else 0) - + (m.V[3, n_tray - 1, comp] * m.hv[3, n_tray - 1, comp] - if n_tray > 1 else 0) + + ( + m.V[1, m.num_trays, comp] * m.dv[3] * m.hv[1, m.num_trays, comp] + if n_tray == 1 + else 0 + ) + + ( + m.V[3, n_tray - 1, comp] * m.hv[3, n_tray - 1, comp] + if n_tray > 1 + else 0 + ) - m.V[3, n_tray, comp] * m.hv[3, n_tray, comp] - - (m.S[1, comp] * m.hl[3, n_tray, comp] - if n_tray == m.sideout1_tray else 0) - - (m.S[2, comp] * m.hl[3, n_tray, comp] - if n_tray == m.sideout2_tray else 0) - for comp in m.comp) * m.Qscale - ==0 + - ( + m.S[1, comp] * m.hl[3, n_tray, comp] + if n_tray == m.sideout1_tray + else 0 + ) + - ( + m.S[2, comp] * m.hl[3, n_tray, comp] + if n_tray == m.sideout2_tray + else 0 + ) + for comp in m.comp + ) + * m.Qscale + == 0 ) - - @disj.Constraint(m.comp, - doc="Product section 3 liquid flowrate per component") + @disj.Constraint(m.comp, doc="Product section 3 liquid flowrate per component") def _productside_liquid_percomponent(disj, comp): return m.L[3, n_tray, comp] == m.Ltotal[3, n_tray] * m.x[3, n_tray, comp] - - @disj.Constraint(m.comp, - doc="Product section 3 vapor flowrate per component") + @disj.Constraint(m.comp, doc="Product section 3 vapor flowrate per component") def _productside_vapor_percomponent(disj, comp): - return m.V[3, n_tray, comp] == m.Vtotal[3, n_tray] * m.y[3, n_tray, comp] - + return m.V[3, n_tray, comp] == m.Vtotal[3, n_tray] * m.y[3, n_tray, comp] @disj.Constraint(doc="Product section 3 liquid composition equilibrium summation") def productside_liquid_composition_summation(disj): return sum(m.x[3, n_tray, comp] for comp in m.comp) - 1 == m.errx[3, n_tray] - @disj.Constraint(doc="Product section 3 vapor composition equilibrium summation") def productside_vapor_composition_summation(disj): return sum(m.y[3, n_tray, comp] for comp in m.comp) - 1 == m.erry[3, n_tray] - - @disj.Constraint(m.comp, - doc="Product section 3 vapor composition") + @disj.Constraint(m.comp, doc="Product section 3 vapor composition") def productside_vapor_composition(disj, comp): - return m.y[3, n_tray, comp] == m.x[3, n_tray, comp] * ( - m.actv[3, n_tray, comp] * ( - m.prop[comp, 'PC'] * exp( - m.prop[comp, 'TC'] / m.T[3, n_tray] * ( - m.prop[comp, 'vpA'] * \ - (1 - m.T[3, n_tray]/m.prop[comp, 'TC']) - + m.prop[comp, 'vpB'] * \ - (1 - m.T[3, n_tray]/m.prop[comp, 'TC'])**1.5 - + m.prop[comp, 'vpC'] * \ - (1 - m.T[3, n_tray]/m.prop[comp, 'TC'])**3 - + m.prop[comp, 'vpD'] * \ - (1 - m.T[3, n_tray]/m.prop[comp, 'TC'])**6 + return ( + m.y[3, n_tray, comp] + == m.x[3, n_tray, comp] + * ( + m.actv[3, n_tray, comp] + * ( + m.prop[comp, 'PC'] + * exp( + m.prop[comp, 'TC'] + / m.T[3, n_tray] + * ( + m.prop[comp, 'vpA'] + * (1 - m.T[3, n_tray] / m.prop[comp, 'TC']) + + m.prop[comp, 'vpB'] + * (1 - m.T[3, n_tray] / m.prop[comp, 'TC']) ** 1.5 + + m.prop[comp, 'vpC'] + * (1 - m.T[3, n_tray] / m.prop[comp, 'TC']) ** 3 + + m.prop[comp, 'vpD'] + * (1 - m.T[3, n_tray] / m.prop[comp, 'TC']) ** 6 + ) ) ) ) - ) / m.P[3, n_tray] - - + / m.P[3, n_tray] + ) - @disj.Constraint(m.comp, - doc="Product section 3 liquid enthalpy") + @disj.Constraint(m.comp, doc="Product section 3 liquid enthalpy") def productside_liquid_enthalpy(disj, comp): return m.hl[3, n_tray, comp] == ( - m.cpc[1] * ( - (m.T[3, n_tray] - m.Tref) * \ - m.prop[comp, 'cpA', 1] - + (m.T[3, n_tray]**2 - m.Tref**2) * \ - m.prop[comp, 'cpB', 1] * m.cpc2['A', 1] / 2 - + (m.T[3, n_tray]**3 - m.Tref**3) * \ - m.prop[comp, 'cpC', 1] * m.cpc2['B', 1] / 3 - + (m.T[3, n_tray]**4 - m.Tref**4) * \ - m.prop[comp, 'cpD', 1] / 4 - ) / m.Hscale - ) - - - @disj.Constraint(m.comp, - doc="Product section 3 vapor enthalpy") + m.cpc[1] + * ( + (m.T[3, n_tray] - m.Tref) * m.prop[comp, 'cpA', 1] + + (m.T[3, n_tray] ** 2 - m.Tref**2) + * m.prop[comp, 'cpB', 1] + * m.cpc2['A', 1] + / 2 + + (m.T[3, n_tray] ** 3 - m.Tref**3) + * m.prop[comp, 'cpC', 1] + * m.cpc2['B', 1] + / 3 + + (m.T[3, n_tray] ** 4 - m.Tref**4) * m.prop[comp, 'cpD', 1] / 4 + ) + / m.Hscale + ) + + @disj.Constraint(m.comp, doc="Product section 3 vapor enthalpy") def productside_vapor_enthalpy(disj, comp): return m.hv[3, n_tray, comp] == ( - m.cpc[2] * ( - (m.T[3, n_tray] - m.Tref) * \ - m.prop[comp, 'cpA', 2] - + (m.T[3, n_tray]**2 - m.Tref**2) * \ - m.prop[comp, 'cpB', 2] * m.cpc2['A', 2] / 2 - + (m.T[3, n_tray]**3 - m.Tref**3) * \ - m.prop[comp, 'cpC', 2] * m.cpc2['B', 2] / 3 - + (m.T[3, n_tray]**4 - m.Tref**4) * \ - m.prop[comp, 'cpD', 2] / 4 - ) / m.Hscale + m.cpc[2] + * ( + (m.T[3, n_tray] - m.Tref) * m.prop[comp, 'cpA', 2] + + (m.T[3, n_tray] ** 2 - m.Tref**2) + * m.prop[comp, 'cpB', 2] + * m.cpc2['A', 2] + / 2 + + (m.T[3, n_tray] ** 3 - m.Tref**3) + * m.prop[comp, 'cpC', 2] + * m.cpc2['B', 2] + / 3 + + (m.T[3, n_tray] ** 4 - m.Tref**4) * m.prop[comp, 'cpD', 2] / 4 + ) + / m.Hscale + m.dHvap[comp] - ) - + ) - @disj.Constraint(m.comp, - doc="Product section 3 liquid activity coefficient") + @disj.Constraint(m.comp, doc="Product section 3 liquid activity coefficient") def productside_activity_coefficient(disj, comp): - return m.actv[3, n_tray, comp] == 1 - + return m.actv[3, n_tray, comp] == 1 - - def _build_top_equations(disj, n_tray): m = disj.model() - @disj.Constraint(m.comp, - doc="Top section 4 mass per component balances") + @disj.Constraint(m.comp, doc="Top section 4 mass per component balances") def _top_mass_percomponent_balances(disj, comp): - return ( - (m.L[4, n_tray + 1, comp] - if n_tray < m.con_tray else 0) - - (m.L[4, n_tray, comp] * m.dl[2] - if n_tray == 1 else 0) - - (m.L[4, n_tray, comp] * m.dl[3] - if n_tray == 1 else 0) - - (m.L[4, n_tray, comp] - if n_tray > 1 else 0) - + (m.V[2, m.num_trays, comp] - if n_tray == 1 else 0) - + (m.V[3, m.num_trays, comp] - if n_tray == 1 else 0) - + (m.V[4, n_tray - 1, comp] - if n_tray > 1 else 0) - - (m.V[4, n_tray, comp] - if n_tray < m.con_tray else 0) - - (m.D[comp] - if n_tray == m.con_tray else 0) - ==0 - ) - + return (m.L[4, n_tray + 1, comp] if n_tray < m.con_tray else 0) - ( + m.L[4, n_tray, comp] * m.dl[2] if n_tray == 1 else 0 + ) - (m.L[4, n_tray, comp] * m.dl[3] if n_tray == 1 else 0) - ( + m.L[4, n_tray, comp] if n_tray > 1 else 0 + ) + ( + m.V[2, m.num_trays, comp] if n_tray == 1 else 0 + ) + ( + m.V[3, m.num_trays, comp] if n_tray == 1 else 0 + ) + ( + m.V[4, n_tray - 1, comp] if n_tray > 1 else 0 + ) - ( + m.V[4, n_tray, comp] if n_tray < m.con_tray else 0 + ) - ( + m.D[comp] if n_tray == m.con_tray else 0 + ) == 0 @disj.Constraint(doc="Top scetion 4 energy balances") def _top_energy_balances(disj): return ( sum( - (m.L[4, n_tray + 1, comp] * m.hl[4, n_tray + 1, comp] - if n_tray < m.con_tray else 0) - - (m.L[4, n_tray, comp] * m.dl[2] * m.hl[4, n_tray, comp] - if n_tray == 1 else 0) - - (m.L[4, n_tray, comp] * m.dl[3] * m.hl[4, n_tray, comp] - if n_tray == 1 else 0) - - (m.L[4, n_tray, comp] * m.hl[4, n_tray, comp] - if n_tray > 1 else 0) - + (m.V[2, m.num_trays, comp] * m.hv[2, m.num_trays, comp] - if n_tray == 1 else 0) - + (m.V[3, m.num_trays, comp] * m.hv[3, m.num_trays, comp] - if n_tray == 1 else 0) - + (m.V[4, n_tray - 1, comp] * m.hv[4, n_tray - 1, comp] - if n_tray > 1 else 0) - - (m.V[4, n_tray, comp] * m.hv[4, n_tray, comp] - if n_tray < m.con_tray else 0) - - (m.D[comp] * m.hl[4, n_tray, comp] - if n_tray == m.con_tray else 0) - for comp in m.comp) * m.Qscale + ( + m.L[4, n_tray + 1, comp] * m.hl[4, n_tray + 1, comp] + if n_tray < m.con_tray + else 0 + ) + - ( + m.L[4, n_tray, comp] * m.dl[2] * m.hl[4, n_tray, comp] + if n_tray == 1 + else 0 + ) + - ( + m.L[4, n_tray, comp] * m.dl[3] * m.hl[4, n_tray, comp] + if n_tray == 1 + else 0 + ) + - (m.L[4, n_tray, comp] * m.hl[4, n_tray, comp] if n_tray > 1 else 0) + + ( + m.V[2, m.num_trays, comp] * m.hv[2, m.num_trays, comp] + if n_tray == 1 + else 0 + ) + + ( + m.V[3, m.num_trays, comp] * m.hv[3, m.num_trays, comp] + if n_tray == 1 + else 0 + ) + + ( + m.V[4, n_tray - 1, comp] * m.hv[4, n_tray - 1, comp] + if n_tray > 1 + else 0 + ) + - ( + m.V[4, n_tray, comp] * m.hv[4, n_tray, comp] + if n_tray < m.con_tray + else 0 + ) + - (m.D[comp] * m.hl[4, n_tray, comp] if n_tray == m.con_tray else 0) + for comp in m.comp + ) + * m.Qscale - (m.Qcon if n_tray == m.con_tray else 0) - ==0 + == 0 ) - - @disj.Constraint(m.comp, - doc="Top section 4 liquid flowrate per component") + @disj.Constraint(m.comp, doc="Top section 4 liquid flowrate per component") def _top_liquid_percomponent(disj, comp): return m.L[4, n_tray, comp] == m.Ltotal[4, n_tray] * m.x[4, n_tray, comp] - - @disj.Constraint(m.comp, - doc="Top section 4 vapor flowrate per component") + @disj.Constraint(m.comp, doc="Top section 4 vapor flowrate per component") def _top_vapor_percomponent(disj, comp): - return m.V[4, n_tray, comp] == m.Vtotal[4, n_tray] * m.y[4, n_tray, comp] - + return m.V[4, n_tray, comp] == m.Vtotal[4, n_tray] * m.y[4, n_tray, comp] @disj.Constraint(doc="Top section 4 liquid composition equilibrium summation") def top_liquid_composition_summation(disj): return sum(m.x[4, n_tray, comp] for comp in m.comp) - 1 == m.errx[4, n_tray] - @disj.Constraint(doc="Top section 4 vapor composition equilibrium summation") def top_vapor_composition_summation(disj): return sum(m.y[4, n_tray, comp] for comp in m.comp) - 1 == m.erry[4, n_tray] - - @disj.Constraint(m.comp, - doc="Top scetion 4 vapor composition") + @disj.Constraint(m.comp, doc="Top scetion 4 vapor composition") def top_vapor_composition(disj, comp): - return m.y[4, n_tray, comp] == m.x[4, n_tray, comp] * ( - m.actv[4, n_tray, comp] * ( - m.prop[comp, 'PC'] * exp( - m.prop[comp, 'TC'] / m.T[4, n_tray] * ( - m.prop[comp, 'vpA'] * \ - (1 - m.T[4, n_tray]/m.prop[comp, 'TC']) - + m.prop[comp, 'vpB'] * \ - (1 - m.T[4, n_tray]/m.prop[comp, 'TC'])**1.5 - + m.prop[comp, 'vpC'] * \ - (1 - m.T[4, n_tray]/m.prop[comp, 'TC'])**3 - + m.prop[comp, 'vpD'] * \ - (1 - m.T[4, n_tray]/m.prop[comp, 'TC'])**6 + return ( + m.y[4, n_tray, comp] + == m.x[4, n_tray, comp] + * ( + m.actv[4, n_tray, comp] + * ( + m.prop[comp, 'PC'] + * exp( + m.prop[comp, 'TC'] + / m.T[4, n_tray] + * ( + m.prop[comp, 'vpA'] + * (1 - m.T[4, n_tray] / m.prop[comp, 'TC']) + + m.prop[comp, 'vpB'] + * (1 - m.T[4, n_tray] / m.prop[comp, 'TC']) ** 1.5 + + m.prop[comp, 'vpC'] + * (1 - m.T[4, n_tray] / m.prop[comp, 'TC']) ** 3 + + m.prop[comp, 'vpD'] + * (1 - m.T[4, n_tray] / m.prop[comp, 'TC']) ** 6 + ) ) ) ) - ) / m.P[4, n_tray] - - + / m.P[4, n_tray] + ) - @disj.Constraint(m.comp, - doc="Top section 4 liquid enthalpy") + @disj.Constraint(m.comp, doc="Top section 4 liquid enthalpy") def top_liquid_enthalpy(disj, comp): return m.hl[4, n_tray, comp] == ( - m.cpc[1] * ( - (m.T[4, n_tray] - m.Tref) * \ - m.prop[comp, 'cpA', 1] - + (m.T[4, n_tray]**2 - m.Tref**2) * \ - m.prop[comp, 'cpB', 1] * m.cpc2['A', 1] / 2 - + (m.T[4, n_tray]**3 - m.Tref**3) * \ - m.prop[comp, 'cpC', 1] * m.cpc2['B', 1] / 3 - + (m.T[4, n_tray]**4 - m.Tref**4) * \ - m.prop[comp, 'cpD', 1] / 4 - ) / m.Hscale - ) - - - @disj.Constraint(m.comp, - doc="Top section 4 vapor enthalpy") + m.cpc[1] + * ( + (m.T[4, n_tray] - m.Tref) * m.prop[comp, 'cpA', 1] + + (m.T[4, n_tray] ** 2 - m.Tref**2) + * m.prop[comp, 'cpB', 1] + * m.cpc2['A', 1] + / 2 + + (m.T[4, n_tray] ** 3 - m.Tref**3) + * m.prop[comp, 'cpC', 1] + * m.cpc2['B', 1] + / 3 + + (m.T[4, n_tray] ** 4 - m.Tref**4) * m.prop[comp, 'cpD', 1] / 4 + ) + / m.Hscale + ) + + @disj.Constraint(m.comp, doc="Top section 4 vapor enthalpy") def top_vapor_enthalpy(disj, comp): return m.hv[4, n_tray, comp] == ( - m.cpc[2] * ( - (m.T[4, n_tray] - m.Tref) * \ - m.prop[comp, 'cpA', 2] - + (m.T[4, n_tray]**2 - m.Tref**2) * \ - m.prop[comp, 'cpB', 2] * m.cpc2['A', 2] / 2 - + (m.T[4, n_tray]**3 - m.Tref**3) * \ - m.prop[comp, 'cpC', 2] * m.cpc2['B', 2] / 3 - + (m.T[4, n_tray]**4 - m.Tref**4) * \ - m.prop[comp, 'cpD', 2] / 4 - ) / m.Hscale + m.cpc[2] + * ( + (m.T[4, n_tray] - m.Tref) * m.prop[comp, 'cpA', 2] + + (m.T[4, n_tray] ** 2 - m.Tref**2) + * m.prop[comp, 'cpB', 2] + * m.cpc2['A', 2] + / 2 + + (m.T[4, n_tray] ** 3 - m.Tref**3) + * m.prop[comp, 'cpC', 2] + * m.cpc2['B', 2] + / 3 + + (m.T[4, n_tray] ** 4 - m.Tref**4) * m.prop[comp, 'cpD', 2] / 4 + ) + / m.Hscale + m.dHvap[comp] - ) + ) - - @disj.Constraint(m.comp, - doc="Top section 4 liquid activity coefficient") + @disj.Constraint(m.comp, doc="Top section 4 liquid activity coefficient") def top_activity_coefficient(disj, comp): - return m.actv[4, n_tray, comp] == 1 - + return m.actv[4, n_tray, comp] == 1 - def _build_pass_through_eqns(disj, sec, n_tray): m = disj.model() if n_tray == 1 or n_tray == m.num_trays: - return - - @disj.Constraint(m.comp, - doc="Pass through liquid flowrate") + return + + @disj.Constraint(m.comp, doc="Pass through liquid flowrate") def pass_through_liquid_flowrate(disj, comp): return m.L[sec, n_tray, comp] == m.L[sec, n_tray + 1, comp] - - @disj.Constraint(m.comp, - doc="Pass through vapor flowrate") + @disj.Constraint(m.comp, doc="Pass through vapor flowrate") def pass_through_vapor_flowrate(disj, comp): return m.V[sec, n_tray, comp] == m.V[sec, n_tray - 1, comp] - - @disj.Constraint(m.comp, - doc="Pass through liquid composition") + @disj.Constraint(m.comp, doc="Pass through liquid composition") def pass_through_liquid_composition(disj, comp): return m.x[sec, n_tray, comp] == m.x[sec, n_tray + 1, comp] - - @disj.Constraint(m.comp, - doc="Pass through vapor composition") + @disj.Constraint(m.comp, doc="Pass through vapor composition") def pass_through_vapor_composition(disj, comp): return m.y[sec, n_tray, comp] == m.y[sec, n_tray + 1, comp] - - @disj.Constraint(m.comp, - doc="Pass through liquid enthalpy") + @disj.Constraint(m.comp, doc="Pass through liquid enthalpy") def pass_through_liquid_enthalpy(disj, comp): return m.hl[sec, n_tray, comp] == m.hl[sec, n_tray + 1, comp] - - @disj.Constraint(m.comp, - doc="Pass through vapor enthalpy") + @disj.Constraint(m.comp, doc="Pass through vapor enthalpy") def pass_through_vapor_enthalpy(disj, comp): return m.hv[sec, n_tray, comp] == m.hv[sec, n_tray - 1, comp] - @disj.Constraint(doc="Pass through temperature") def pass_through_temperature(disj): return m.T[sec, n_tray] == m.T[sec, n_tray - 1] - - - + if __name__ == "__main__": model = build_model() - diff --git a/gdplib/kaibel/main_gdp.py b/gdplib/kaibel/main_gdp.py index 8c8b32b..5b130eb 100644 --- a/gdplib/kaibel/main_gdp.py +++ b/gdplib/kaibel/main_gdp.py @@ -57,7 +57,7 @@ def main(): for sec in m.section: for n_tray in m.tray: m.P[sec, n_tray].fix(m.Preb) - + ## Initial values for the tray existence or absence for n_tray in m.candidate_trays_main: for sec in m.section_main: @@ -70,53 +70,38 @@ def main(): m.tray_exists[3, n_tray].indicator_var.set_value(1) m.tray_absent[3, n_tray].indicator_var.set_value(0) - - intro_message(m) - - results = SolverFactory('gdpopt').solve(m, - strategy='LOA', - tee=True, - time_limit = 3600, - mip_solver='gams', - mip_solver_args=dict(solver='cplex') - ) - - m.calc_nt = ( - sum( - sum(m.tray_exists[sec, n_tray].indicator_var.value - for n_tray in m.tray) - for sec in m.section) - - sum(m.tray_exists[3, n_tray].indicator_var.value - for n_tray in m.tray) + results = SolverFactory('gdpopt').solve( + m, + strategy='LOA', + tee=True, + time_limit=3600, + mip_solver='gams', + mip_solver_args=dict(solver='cplex'), ) + + m.calc_nt = sum( + sum(m.tray_exists[sec, n_tray].indicator_var.value for n_tray in m.tray) + for sec in m.section + ) - sum(m.tray_exists[3, n_tray].indicator_var.value for n_tray in m.tray) m.dw_start = ( - sum(m.tray_exists[1, n_tray].indicator_var.value - for n_tray in m.tray) - + 1 - ) - m.dw_end = ( - sum(m.tray_exists[1, n_tray].indicator_var.value - for n_tray in m.tray) - + sum(m.tray_exists[2, n_tray].indicator_var.value - for n_tray in m.tray) + sum(m.tray_exists[1, n_tray].indicator_var.value for n_tray in m.tray) + 1 ) - + m.dw_end = sum( + m.tray_exists[1, n_tray].indicator_var.value for n_tray in m.tray + ) + sum(m.tray_exists[2, n_tray].indicator_var.value for n_tray in m.tray) display_results(m) - - print(' ', results) + print(' ', results) print(' Solver Status: ', results.solver.status) print(' Termination condition: ', results.solver.termination_condition) - - - def intro_message(m): - print(""" + print( + """ If you use this model and/or initialization strategy, you may cite the following: Rawlings, ES; Chen, Q; Grossmann, IE; Caballero, JA. Kaibel Column: Modeling, @@ -125,7 +110,8 @@ def intro_message(m): DOI: https://doi.org/10.1016/j.compchemeng.2019.03.006 - """) + """ + ) def display_results(m): @@ -141,71 +127,62 @@ def display_results(m): print('Dividing_wall_start: %s' % value(m.dw_start)) print('Dividing_wall_end: %s' % value(m.dw_end)) print(' ') - print('Qreb: {: >3.0f}kW B_1: {: > 2.0f} B_2: {: >2.0f} B_3: {: >2.0f} B_4: {: >2.0f} Btotal: {: >2.0f}' - .format(value(m.Qreb / m.Qscale ), - value(m.B[1]), - value(m.B[2]), - value(m.B[3]), - value(m.B[4]), - value(m.Btotal) - ) + print( + 'Qreb: {: >3.0f}kW B_1: {: > 2.0f} B_2: {: >2.0f} B_3: {: >2.0f} B_4: {: >2.0f} Btotal: {: >2.0f}'.format( + value(m.Qreb / m.Qscale), + value(m.B[1]), + value(m.B[2]), + value(m.B[3]), + value(m.B[4]), + value(m.Btotal), + ) ) - print('Qcon: {: >2.0f}kW D_1: {: >2.0f} D_2: {: >2.0f} D_3: {: >2.0f} D_4: {: >2.0f} Dtotal: {: >2.0f}' - .format(value(m.Qcon / m.Qscale), - value(m.D[1]), - value(m.D[2]), - value(m.D[3]), - value(m.D[4]), - value(m.Dtotal) - ) + print( + 'Qcon: {: >2.0f}kW D_1: {: >2.0f} D_2: {: >2.0f} D_3: {: >2.0f} D_4: {: >2.0f} Dtotal: {: >2.0f}'.format( + value(m.Qcon / m.Qscale), + value(m.D[1]), + value(m.D[2]), + value(m.D[3]), + value(m.D[4]), + value(m.Dtotal), + ) ) print(' ') - print('Reflux: {: >3.4f}' - .format(value(m.rr) - ) - ) - print('Reboil: {: >3.4f} ' - .format(value(m.bu) - ) - ) + print('Reflux: {: >3.4f}'.format(value(m.rr))) + print('Reboil: {: >3.4f} '.format(value(m.bu))) print(' ') print('Flowrates[mol/s]') - print('F_1: {: > 3.0f} F_2: {: >2.0f} F_3: {: >2.0f} F_4: {: >2.0f} Ftotal: {: >2.0f}' - .format(value(m.F[1]), - value(m.F[2]), - value(m.F[3]), - value(m.F[4]), - sum(value(m.F[comp]) for comp in m.comp) - ) + print( + 'F_1: {: > 3.0f} F_2: {: >2.0f} F_3: {: >2.0f} F_4: {: >2.0f} Ftotal: {: >2.0f}'.format( + value(m.F[1]), + value(m.F[2]), + value(m.F[3]), + value(m.F[4]), + sum(value(m.F[comp]) for comp in m.comp), + ) ) - print('S1_1: {: > 1.0f} S1_2: {: >2.0f} S1_3: {: >2.0f} S1_4: {: >2.0f} S1total: {: >2.0f}' - .format(value(m.S[1, 1]), - value(m.S[1, 2]), - value(m.S[1, 3]), - value(m.S[1, 4]), - sum(value(m.S[1, comp]) for comp in m.comp) - ) + print( + 'S1_1: {: > 1.0f} S1_2: {: >2.0f} S1_3: {: >2.0f} S1_4: {: >2.0f} S1total: {: >2.0f}'.format( + value(m.S[1, 1]), + value(m.S[1, 2]), + value(m.S[1, 3]), + value(m.S[1, 4]), + sum(value(m.S[1, comp]) for comp in m.comp), + ) ) - print('S2_1: {: > 1.0f} S2_2: {: >2.0f} S2_3: {: >2.0f} S2_4: {: >2.0f} S2total: {: >2.0f}' - .format(value(m.S[2, 1]), - value(m.S[2, 2]), - value(m.S[2, 3]), - value(m.S[2, 4]), - sum(value(m.S[2, comp]) for comp in m.comp) - ) + print( + 'S2_1: {: > 1.0f} S2_2: {: >2.0f} S2_3: {: >2.0f} S2_4: {: >2.0f} S2total: {: >2.0f}'.format( + value(m.S[2, 1]), + value(m.S[2, 2]), + value(m.S[2, 3]), + value(m.S[2, 4]), + sum(value(m.S[2, comp]) for comp in m.comp), + ) ) print(' ') print('Distributors:') - print('dl[2]: {: >3.4f} dl[3]: {: >3.4f}' - .format(value(m.dl[2]), - value(m.dl[3]) - ) - ) - print('dv[2]: {: >3.4f} dv[3]: {: >3.4f}' - .format(value(m.dv[2]), - value(m.dv[3]) - ) - ) + print('dl[2]: {: >3.4f} dl[3]: {: >3.4f}'.format(value(m.dl[2]), value(m.dl[3]))) + print('dv[2]: {: >3.4f} dv[3]: {: >3.4f}'.format(value(m.dv[2]), value(m.dv[3]))) print(' ') print(' ') print(' ') @@ -215,15 +192,21 @@ def display_results(m): print(' Tray Bottom Feed ') print('__________________________________________') for t in reversed(list(m.tray)): - print('[{: >2.0f}] {: >9.0g} {: >18.0g} F:{: >3.0f} ' - .format(t, - fabs(value(m.tray_exists[1, t].indicator_var)) - if t in m.candidate_trays_main else 1, - fabs(value(m.tray_exists[2, t].indicator_var)) - if t in m.candidate_trays_feed else 1, - sum(value(m.F[comp]) for comp in m.comp) - if t == m.feed_tray else 0, - ) + print( + '[{: >2.0f}] {: >9.0g} {: >18.0g} F:{: >3.0f} '.format( + t, + ( + fabs(value(m.tray_exists[1, t].indicator_var)) + if t in m.candidate_trays_main + else 1 + ), + ( + fabs(value(m.tray_exists[2, t].indicator_var)) + if t in m.candidate_trays_feed + else 1 + ), + sum(value(m.F[comp]) for comp in m.comp) if t == m.feed_tray else 0, + ) ) print(' ') print('__________________________________________') @@ -231,21 +214,33 @@ def display_results(m): print(' Product Top ') print('__________________________________________') for t in reversed(list(m.tray)): - print('[{: >2.0f}] {: >9.0g} S1:{: >2.0f} S2:{: >2.0f} {: >8.0g}' - .format(t, - fabs(value(m.tray_exists[3, t].indicator_var)) - if t in m.candidate_trays_product else 1, - sum(value(m.S[1, comp]) for comp in m.comp) - if t == m.sideout1_tray else 0, - sum(value(m.S[2, comp]) for comp in m.comp) - if t == m.sideout2_tray else 0, - fabs(value(m.tray_exists[4, t].indicator_var)) - if t in m.candidate_trays_main else 1 - ) + print( + '[{: >2.0f}] {: >9.0g} S1:{: >2.0f} S2:{: >2.0f} {: >8.0g}'.format( + t, + ( + fabs(value(m.tray_exists[3, t].indicator_var)) + if t in m.candidate_trays_product + else 1 + ), + ( + sum(value(m.S[1, comp]) for comp in m.comp) + if t == m.sideout1_tray + else 0 + ), + ( + sum(value(m.S[2, comp]) for comp in m.comp) + if t == m.sideout2_tray + else 0 + ), + ( + fabs(value(m.tray_exists[4, t].indicator_var)) + if t in m.candidate_trays_main + else 1 + ), + ) ) print(' 1 = trays exists, 0 = absent tray') - if __name__ == "__main__": main() diff --git a/gdplib/med_term_purchasing/med_term_purchasing.py b/gdplib/med_term_purchasing/med_term_purchasing.py index 16ad6f8..73bb3c8 100644 --- a/gdplib/med_term_purchasing/med_term_purchasing.py +++ b/gdplib/med_term_purchasing/med_term_purchasing.py @@ -181,7 +181,7 @@ def build_model(): # sigmad_jt # sigmad(j, t) in GAMS - # Minimum quantity of chemical j that must be bought before recieving a Discount under discount contract + # Minimum quantity of chemical j that must be bought before receiving a Discount under discount contract model.MinAmount_Discount = Param( model.Streams, model.TimePeriods, @@ -189,7 +189,7 @@ def build_model(): doc='Minimum quantity of chemical j that must be bought before receiving a Discount under discount contract', ) - # min quantity to recieve discount under bulk contract + # min quantity to receive discount under bulk contract # sigmab(j, t) in GAMS model.MinAmount_Bulk = Param( model.Streams, @@ -198,7 +198,7 @@ def build_model(): doc='Minimum quantity of chemical j that must be bought before receiving a Discount under bulk contract', ) - # min quantity to recieve discount under length contract + # min quantity to receive discount under length contract # sigmal(j, p) in GAMS model.MinAmount_Length = Param( model.Streams, @@ -815,7 +815,7 @@ def profit_rule(model): model.profit = Objective(rule=profit_rule, sense=maximize, doc='Maximize profit') - # flow of raw materials is the total amount purchased (accross all contracts) + # flow of raw materials is the total amount purchased (across all contracts) def raw_material_flow_rule(model, j, t): """ Ensures the total flow of raw material j in time period t equals the sum of amounts purchased under all contract types. @@ -1106,7 +1106,7 @@ def process_balance_rule4(model, t): doc='Input/output balance equation 2 for Process 3 in the process network', ) - # process capacity contraints + # process capacity constraints # these are hardcoded based on the three processes and the process flow structure def process_capacity_rule1(model, t): """ @@ -1379,7 +1379,7 @@ def shortfall_max_rule(model, j, t): doc='Maximum shortfall allowed for each product j in each time period t', ) - # maxiumum capacities of suppliers + # maximum capacities of suppliers def supplier_capacity_rule(model, j, t): """ Enforces the upper limits on the supply capacity for each raw material j provided by suppliers in each time period t. diff --git a/gdplib/methanol/methanol.py b/gdplib/methanol/methanol.py index dd7702c..fe85918 100644 --- a/gdplib/methanol/methanol.py +++ b/gdplib/methanol/methanol.py @@ -22,9 +22,13 @@ def fix_vars_with_equal_bounds(m, tol=1e-8): if lb is None or ub is None: continue if lb > ub + tol: - raise InfeasibleError('Variable lb is larger than ub: {0} lb: {1} ub: {2}'.format(v.name, lb, ub)) + raise InfeasibleError( + 'Variable lb is larger than ub: {0} lb: {1} ub: {2}'.format( + v.name, lb, ub + ) + ) elif abs(ub - lb) <= tol: - v.fix(0.5*(lb+ub)) + v.fix(0.5 * (lb + ub)) class MethanolModel(object): @@ -44,8 +48,8 @@ def __init__(self): self.electricity_cost = 0.255 self.cooling_cost = 700 self.heating_cost = 8000 - self.purity_demand = 0.9 #purity demand in product stream - self.demand = 1.0 # flowrate restriction on product flow + self.purity_demand = 0.9 # purity demand in product stream + self.demand = 1.0 # flowrate restriction on product flow self.flow_feed_lb = 0.5 self.flow_feed_ub = 5 self.flow_feed_temp = 3 @@ -58,7 +62,7 @@ def __init__(self): self.cheap_reactor_variable_cost = 5 self.expensive_reactor_fixed_cost = 250 self.expensive_reactor_variable_cost = 10 - self.heat_unit_match = 0.00306 + self.heat_unit_match = 0.00306 self.capacity_redundancy = 1.2 self.antoine_unit_trans = 7500.6168 self.K = 0.415 @@ -66,7 +70,7 @@ def __init__(self): self.reactor_relation = 0.9 self.purity_demand = 0.9 self.fix_electricity_cost = 175 - self.two_stage_fix_cost = 50 + self.two_stage_fix_cost = 50 m.streams = pe.Set(initialize=list(range(1, 34)), ordered=True) m.components = pe.Set(initialize=['H2', 'CO', 'CH3OH', 'CH4'], ordered=True) @@ -79,12 +83,12 @@ def __init__(self): flow_1['H2'] = 0.6 flow_1['CO'] = 0.25 flow_1['CH4'] = 0.15 - m.flow_1_composition = pe.Param(m.components,initialize = flow_1,default = 0) + m.flow_1_composition = pe.Param(m.components, initialize=flow_1, default=0) flow_2 = dict() flow_2['H2'] = 0.65 flow_2['CO'] = 0.30 flow_2['CH4'] = 0.05 - m.flow_2_composition = pe.Param(m.components,initialize = flow_2,default = 0) + m.flow_2_composition = pe.Param(m.components, initialize=flow_2, default=0) m.pressures[13].setlb(2.5) m.temps[13].setlb(4.23) @@ -158,11 +162,15 @@ def __init__(self): self.liquid_outlets[13] = 22 def _total_flow(_m, _s): - return _m.flows[_s] == sum(_m.component_flows[_s, _c] for _c in _m.components) + return _m.flows[_s] == sum( + _m.component_flows[_s, _c] for _c in _m.components + ) + m.total_flow_con = pe.Constraint(m.streams, rule=_total_flow) - m.purity_con = pe.Constraint(expr=m.component_flows[23, 'CH3OH'] >= self.purity_demand * m.flows[23]) - + m.purity_con = pe.Constraint( + expr=m.component_flows[23, 'CH3OH'] >= self.purity_demand * m.flows[23] + ) # ************************************ # Feed @@ -172,7 +180,7 @@ def _total_flow(_m, _s): self.build_stream_doesnt_exist_con(m.cheap_feed_disjunct, 2) m.cheap_feed_disjunct.feed_cons = c = pe.ConstraintList() c.add(m.component_flows[1, 'H2'] == m.flow_1_composition['H2'] * m.flows[1]) - c.add(m.component_flows[1, 'CO'] == m.flow_1_composition['CO']* m.flows[1]) + c.add(m.component_flows[1, 'CO'] == m.flow_1_composition['CO'] * m.flows[1]) c.add(m.component_flows[1, 'CH4'] == m.flow_1_composition['CH4'] * m.flows[1]) c.add(m.flows[1] >= self.flow_feed_lb) c.add(m.flows[1] <= self.flow_feed_ub) @@ -191,7 +199,9 @@ def _total_flow(_m, _s): c.add(m.temps[2] == self.flow_feed_temp) c.add(m.pressures[2] == self.flow_feed_pressure) - m.feed_disjunctions = gdp.Disjunction(expr=[m.cheap_feed_disjunct, m.expensive_feed_disjunct]) + m.feed_disjunctions = gdp.Disjunction( + expr=[m.cheap_feed_disjunct, m.expensive_feed_disjunct] + ) # ************************************ # Feed compressors @@ -213,11 +223,21 @@ def _total_flow(_m, _s): self.build_compressor(m.two_stage_feed_compressor_disjunct, 4) self.build_cooler(m.two_stage_feed_compressor_disjunct, 5) self.build_compressor(m.two_stage_feed_compressor_disjunct, 6) - m.two_stage_feed_compressor_disjunct.equal_electric_requirements = pe.Constraint(expr=m.two_stage_feed_compressor_disjunct.compressor_4.electricity_requirement == m.two_stage_feed_compressor_disjunct.compressor_6.electricity_requirement) + m.two_stage_feed_compressor_disjunct.equal_electric_requirements = pe.Constraint( + expr=m.two_stage_feed_compressor_disjunct.compressor_4.electricity_requirement + == m.two_stage_feed_compressor_disjunct.compressor_6.electricity_requirement + ) m.two_stage_feed_compressor_disjunct.exists = pe.Var(bounds=(0, 1)) - m.two_stage_feed_compressor_disjunct.exists_con = pe.Constraint(expr=m.two_stage_feed_compressor_disjunct.exists == 1) + m.two_stage_feed_compressor_disjunct.exists_con = pe.Constraint( + expr=m.two_stage_feed_compressor_disjunct.exists == 1 + ) - m.feed_compressor_disjunction = gdp.Disjunction(expr=[m.single_stage_feed_compressor_disjunct, m.two_stage_feed_compressor_disjunct]) + m.feed_compressor_disjunction = gdp.Disjunction( + expr=[ + m.single_stage_feed_compressor_disjunct, + m.two_stage_feed_compressor_disjunct, + ] + ) self.build_mixer(m, 'recycle_feed_mixer') self.build_cooler(m, 7) @@ -233,7 +253,9 @@ def _total_flow(_m, _s): self.build_stream_doesnt_exist_con(m.expensive_reactor, 16) self.build_reactor(m.expensive_reactor, 9) m.expensive_reactor.exists = pe.Var(bounds=(0, 1)) - m.expensive_reactor.exists_con = pe.Constraint(expr=m.expensive_reactor.exists == 1) + m.expensive_reactor.exists_con = pe.Constraint( + expr=m.expensive_reactor.exists == 1 + ) m.expensive_reactor.composition_cons = c = pe.ConstraintList() for _comp in m.components: c.add(m.component_flows[17, _comp] >= 0.01) @@ -250,7 +272,9 @@ def _total_flow(_m, _s): for _comp in m.components: c.add(m.component_flows[16, _comp] >= 0.01) - m.reactor_disjunction = gdp.Disjunction(expr=[m.expensive_reactor, m.cheap_reactor]) + m.reactor_disjunction = gdp.Disjunction( + expr=[m.expensive_reactor, m.cheap_reactor] + ) self.build_expansion_valve(m, 11) self.build_cooler(m, 12) @@ -265,10 +289,18 @@ def _total_flow(_m, _s): m.single_stage_recycle_compressor_disjunct = gdp.Disjunct() self.build_equal_streams(m.single_stage_recycle_compressor_disjunct, 26, 27) self.build_equal_streams(m.single_stage_recycle_compressor_disjunct, 29, 33) - self.build_stream_doesnt_exist_con(m.single_stage_recycle_compressor_disjunct, 28) - self.build_stream_doesnt_exist_con(m.single_stage_recycle_compressor_disjunct, 30) - self.build_stream_doesnt_exist_con(m.single_stage_recycle_compressor_disjunct, 31) - self.build_stream_doesnt_exist_con(m.single_stage_recycle_compressor_disjunct, 32) + self.build_stream_doesnt_exist_con( + m.single_stage_recycle_compressor_disjunct, 28 + ) + self.build_stream_doesnt_exist_con( + m.single_stage_recycle_compressor_disjunct, 30 + ) + self.build_stream_doesnt_exist_con( + m.single_stage_recycle_compressor_disjunct, 31 + ) + self.build_stream_doesnt_exist_con( + m.single_stage_recycle_compressor_disjunct, 32 + ) self.build_compressor(m.single_stage_recycle_compressor_disjunct, 16) m.two_stage_recycle_compressor_disjunct = gdp.Disjunct() @@ -279,35 +311,74 @@ def _total_flow(_m, _s): self.build_compressor(m.two_stage_recycle_compressor_disjunct, 17) self.build_cooler(m.two_stage_recycle_compressor_disjunct, 18) self.build_compressor(m.two_stage_recycle_compressor_disjunct, 19) - m.two_stage_recycle_compressor_disjunct.equal_electric_requirements = pe.Constraint(expr=m.two_stage_recycle_compressor_disjunct.compressor_17.electricity_requirement == m.two_stage_recycle_compressor_disjunct.compressor_19.electricity_requirement) + m.two_stage_recycle_compressor_disjunct.equal_electric_requirements = pe.Constraint( + expr=m.two_stage_recycle_compressor_disjunct.compressor_17.electricity_requirement + == m.two_stage_recycle_compressor_disjunct.compressor_19.electricity_requirement + ) m.two_stage_recycle_compressor_disjunct.exists = pe.Var(bounds=(0, 1)) - m.two_stage_recycle_compressor_disjunct.exists_con = pe.Constraint(expr=m.two_stage_recycle_compressor_disjunct.exists == 1) + m.two_stage_recycle_compressor_disjunct.exists_con = pe.Constraint( + expr=m.two_stage_recycle_compressor_disjunct.exists == 1 + ) - m.recycle_compressor_disjunction = gdp.Disjunction(expr=[m.single_stage_recycle_compressor_disjunct, m.two_stage_recycle_compressor_disjunct]) + m.recycle_compressor_disjunction = gdp.Disjunction( + expr=[ + m.single_stage_recycle_compressor_disjunct, + m.two_stage_recycle_compressor_disjunct, + ] + ) # ************************************ # Objective # ************************************ - + e = 0 e -= self.cost_flow_1 * m.flows[1] e -= self.cost_flow_2 * m.flows[2] e += self.price_of_product * m.flows[23] e += self.price_of_byproduct * m.flows[25] - e -= self.cheap_reactor_variable_cost * self.reactor_volume * m.cheap_reactor.exists + e -= ( + self.cheap_reactor_variable_cost + * self.reactor_volume + * m.cheap_reactor.exists + ) e -= self.cheap_reactor_fixed_cost * m.cheap_reactor.exists - e -= self.expensive_reactor_variable_cost * self.reactor_volume * m.expensive_reactor.exists + e -= ( + self.expensive_reactor_variable_cost + * self.reactor_volume + * m.expensive_reactor.exists + ) e -= self.expensive_reactor_fixed_cost * m.expensive_reactor.exists - e -= ( self.fix_electricity_cost+ self.electricity_cost) * m.single_stage_feed_compressor_disjunct.compressor_3.electricity_requirement + e -= ( + (self.fix_electricity_cost + self.electricity_cost) + * m.single_stage_feed_compressor_disjunct.compressor_3.electricity_requirement + ) e -= self.two_stage_fix_cost * m.two_stage_feed_compressor_disjunct.exists - e -= (self.fix_electricity_cost + self.electricity_cost) * m.two_stage_feed_compressor_disjunct.compressor_4.electricity_requirement - e -= (self.fix_electricity_cost + self.electricity_cost) * m.two_stage_feed_compressor_disjunct.compressor_6.electricity_requirement + e -= ( + (self.fix_electricity_cost + self.electricity_cost) + * m.two_stage_feed_compressor_disjunct.compressor_4.electricity_requirement + ) + e -= ( + (self.fix_electricity_cost + self.electricity_cost) + * m.two_stage_feed_compressor_disjunct.compressor_6.electricity_requirement + ) e -= self.cooling_cost * m.two_stage_feed_compressor_disjunct.cooler_5.heat_duty - e -= (self.fix_electricity_cost + self.electricity_cost) * m.single_stage_recycle_compressor_disjunct.compressor_16.electricity_requirement + e -= ( + (self.fix_electricity_cost + self.electricity_cost) + * m.single_stage_recycle_compressor_disjunct.compressor_16.electricity_requirement + ) e -= self.two_stage_fix_cost * m.two_stage_recycle_compressor_disjunct.exists - e -= (self.fix_electricity_cost + self.electricity_cost) * m.two_stage_recycle_compressor_disjunct.compressor_17.electricity_requirement - e -= (self.fix_electricity_cost + self.electricity_cost) * m.two_stage_recycle_compressor_disjunct.compressor_19.electricity_requirement - e -= self.cooling_cost * m.two_stage_recycle_compressor_disjunct.cooler_18.heat_duty + e -= ( + (self.fix_electricity_cost + self.electricity_cost) + * m.two_stage_recycle_compressor_disjunct.compressor_17.electricity_requirement + ) + e -= ( + (self.fix_electricity_cost + self.electricity_cost) + * m.two_stage_recycle_compressor_disjunct.compressor_19.electricity_requirement + ) + e -= ( + self.cooling_cost + * m.two_stage_recycle_compressor_disjunct.cooler_18.heat_duty + ) e -= self.cooling_cost * m.cooler_7.heat_duty e -= self.heating_cost * m.heater_8.heat_duty e -= self.cooling_cost * m.cooler_12.heat_duty @@ -324,18 +395,28 @@ def build_compressor(self, block, unit_number): out_stream = self.outlet_streams[u] b = pe.Block() - setattr(block, 'compressor_'+str(u), b) + setattr(block, 'compressor_' + str(u), b) b.p_ratio = pe.Var(bounds=(0, 1.74)) b.electricity_requirement = pe.Var(bounds=(0, 50)) def _component_balances(_b, _c): return m.component_flows[out_stream, _c] == m.component_flows[in_stream, _c] + b.component_balances = pe.Constraint(m.components, rule=_component_balances) b.t_ratio_con = pe.Constraint(expr=t[out_stream] == b.p_ratio * t[in_stream]) - b.electricity_requirement_con = pe.Constraint(expr=(b.electricity_requirement == self.alpha * - (b.p_ratio - 1) * t[in_stream] * m.flows[in_stream] / - (10.0 * self.eta * self.gamma))) - b.p_ratio_con = pe.Constraint(expr=p[out_stream]**self.gamma == b.p_ratio * p[in_stream]**self.gamma) + b.electricity_requirement_con = pe.Constraint( + expr=( + b.electricity_requirement + == self.alpha + * (b.p_ratio - 1) + * t[in_stream] + * m.flows[in_stream] + / (10.0 * self.eta * self.gamma) + ) + ) + b.p_ratio_con = pe.Constraint( + expr=p[out_stream] ** self.gamma == b.p_ratio * p[in_stream] ** self.gamma + ) def build_expansion_valve(self, block, unit_number): u = unit_number @@ -345,12 +426,16 @@ def build_expansion_valve(self, block, unit_number): in_stream = self.inlet_streams[u] out_stream = self.outlet_streams[u] b = pe.Block() - setattr(block, 'expansion_valve_'+str(u), b) + setattr(block, 'expansion_valve_' + str(u), b) def _component_balances(_b, _c): return m.component_flows[out_stream, _c] == m.component_flows[in_stream, _c] + b.component_balances = pe.Constraint(m.components, rule=_component_balances) - b.ratio_con = pe.Constraint(expr=t[out_stream] * p[in_stream] ** self.gamma == t[in_stream] * p[out_stream] ** self.gamma) + b.ratio_con = pe.Constraint( + expr=t[out_stream] * p[in_stream] ** self.gamma + == t[in_stream] * p[out_stream] ** self.gamma + ) b.expansion_con = pe.Constraint(expr=p[out_stream] <= p[in_stream]) def build_cooler(self, block, unit_number): @@ -362,13 +447,19 @@ def build_cooler(self, block, unit_number): in_stream = self.inlet_streams[u] out_stream = self.outlet_streams[u] b = pe.Block() - setattr(block, 'cooler_'+str(u), b) + setattr(block, 'cooler_' + str(u), b) b.heat_duty = pe.Var(bounds=(0, 50)) def _component_balances(_b, _c): return m.component_flows[out_stream, _c] == m.component_flows[in_stream, _c] + b.component_balances = pe.Constraint(m.components, rule=_component_balances) - b.heat_duty_con = pe.Constraint(expr=b.heat_duty == self.heat_unit_match * self.cp * (f[in_stream] * t[in_stream] - f[out_stream] * t[out_stream])) + b.heat_duty_con = pe.Constraint( + expr=b.heat_duty + == self.heat_unit_match + * self.cp + * (f[in_stream] * t[in_stream] - f[out_stream] * t[out_stream]) + ) b.pressure_con = pe.Constraint(expr=p[out_stream] == p[in_stream]) def build_heater(self, block, unit_number): @@ -380,13 +471,19 @@ def build_heater(self, block, unit_number): in_stream = self.inlet_streams[u] out_stream = self.outlet_streams[u] b = pe.Block() - setattr(block, 'heater_'+str(u), b) + setattr(block, 'heater_' + str(u), b) b.heat_duty = pe.Var(bounds=(0, 50)) def _component_balances(_b, _c): return m.component_flows[out_stream, _c] == m.component_flows[in_stream, _c] + b.component_balances = pe.Constraint(m.components, rule=_component_balances) - b.heat_duty_con = pe.Constraint(expr=b.heat_duty == self.heat_unit_match * self.cp * (f[out_stream] * t[out_stream] - f[in_stream] * t[in_stream])) + b.heat_duty_con = pe.Constraint( + expr=b.heat_duty + == self.heat_unit_match + * self.cp + * (f[out_stream] * t[out_stream] - f[in_stream] * t[in_stream]) + ) b.pressure_con = pe.Constraint(expr=p[out_stream] == p[in_stream]) def build_mixer(self, block, unit_number): @@ -398,13 +495,21 @@ def build_mixer(self, block, unit_number): in_stream1, in_stream2 = self.inlet_streams[u] out_stream = self.outlet_streams[u] b = pe.Block() - setattr(block, 'mixer_'+str(u), b) + setattr(block, 'mixer_' + str(u), b) def _component_balances(_b, _c): - return m.component_flows[out_stream, _c] == m.component_flows[in_stream1, _c] + m.component_flows[in_stream2, _c] + return ( + m.component_flows[out_stream, _c] + == m.component_flows[in_stream1, _c] + m.component_flows[in_stream2, _c] + ) + b.component_balances = pe.Constraint(m.components, rule=_component_balances) - b.average_temp = pe.Constraint(expr=(t[out_stream] * f[out_stream] == (t[in_stream1] * f[in_stream1] + - t[in_stream2] * f[in_stream2]))) + b.average_temp = pe.Constraint( + expr=( + t[out_stream] * f[out_stream] + == (t[in_stream1] * f[in_stream1] + t[in_stream2] * f[in_stream2]) + ) + ) b.pressure_con1 = pe.Constraint(expr=p[in_stream1] == p[out_stream]) b.pressure_con2 = pe.Constraint(expr=p[in_stream2] == p[out_stream]) @@ -416,18 +521,27 @@ def build_splitter(self, block, unit_number): in_stream = self.inlet_streams[u] out_stream1, out_stream2 = self.outlet_streams[u] b = pe.Block() - setattr(block, 'splitter_'+str(u), b) + setattr(block, 'splitter_' + str(u), b) b.split_fraction = pe.Var(bounds=(0, 1)) if unit_number == 'purge_splitter': b.split_fraction.setlb(0.01) b.split_fraction.setub(0.99) def _split_frac_rule(_b, _c): - return m.component_flows[out_stream1, _c] == b.split_fraction * m.component_flows[in_stream, _c] + return ( + m.component_flows[out_stream1, _c] + == b.split_fraction * m.component_flows[in_stream, _c] + ) + b.split_frac_con = pe.Constraint(m.components, rule=_split_frac_rule) def _component_balances(_b, _c): - return m.component_flows[in_stream, _c] == m.component_flows[out_stream1, _c] + m.component_flows[out_stream2, _c] + return ( + m.component_flows[in_stream, _c] + == m.component_flows[out_stream1, _c] + + m.component_flows[out_stream2, _c] + ) + b.component_balances = pe.Constraint(m.components, rule=_component_balances) b.temp_con1 = pe.Constraint(expr=t[in_stream] == t[out_stream1]) b.temp_con2 = pe.Constraint(expr=t[in_stream] == t[out_stream2]) @@ -443,6 +557,7 @@ def build_equal_streams(self, block, stream1, stream2): def _component_balances(_b, _c): return m.component_flows[stream2, _c] == m.component_flows[stream1, _c] + b.component_balances = pe.Constraint(m.components, rule=_component_balances) b.temp_con = pe.Constraint(expr=t[stream1] == t[stream2]) b.pressure_con = pe.Constraint(expr=p[stream1] == p[stream2]) @@ -458,7 +573,7 @@ def build_reactor(self, block, unit_number): out_stream = self.outlet_streams[u] b = pe.Block() - setattr(block, 'reactor_'+str(u), b) + setattr(block, 'reactor_' + str(u), b) b.consumption_rate = pe.Var(bounds=(0, 5)) b.conversion = pe.Var(bounds=(0, 0.42)) b.equilibrium_conversion = pe.Var(bounds=(0, 0.42)) @@ -473,25 +588,46 @@ def build_reactor(self, block, unit_number): b.t_inv_con = pe.Constraint(expr=b.temp * b.t_inv == 1) fbbt(b.p_sq_inv_con) # just getting bounds on p_sq_inv fbbt(b.t_inv_con) # just getting bounds on t_inv - b.conversion_consumption_con = pe.Constraint(expr=b.consumption_rate == b.conversion * component_f[in_stream, - key]) - b.energy_balance = pe.Constraint(expr=(f[in_stream]*t[in_stream] - f[out_stream]*t[ - out_stream])*self.cp == 0.01*self.heat_of_reaction*b.consumption_rate) - b.H2_balance = pe.Constraint(expr=component_f[out_stream,'H2'] == component_f[in_stream,'H2'] - - b.consumption_rate) - b.CO_balance = pe.Constraint(expr=component_f[out_stream,'CO'] == component_f[in_stream,'CO'] - - 0.5*b.consumption_rate) - b.CH3OH_balance = pe.Constraint(expr=component_f[out_stream,'CH3OH'] == component_f[in_stream,'CH3OH'] + - 0.5*b.consumption_rate) - b.CH4_balance = pe.Constraint(expr=component_f[out_stream,'CH4'] == component_f[in_stream,'CH4']) - b.eq_conversion_con = pe.Constraint(expr=b.equilibrium_conversion == self.K * - (1 - (self.delta_H*pe.exp(-18*b.t_inv)*b.p_sq_inv))) - b.conversion_con = pe.Constraint(expr=b.conversion * f[in_stream] == b.equilibrium_conversion * - (1-pe.exp(-self.volume_conversion[u]*self.reactor_volume)) * - (component_f[in_stream,'H2'] + component_f[in_stream, 'CO'] + - component_f[in_stream, 'CH3OH'])) + b.conversion_consumption_con = pe.Constraint( + expr=b.consumption_rate == b.conversion * component_f[in_stream, key] + ) + b.energy_balance = pe.Constraint( + expr=(f[in_stream] * t[in_stream] - f[out_stream] * t[out_stream]) * self.cp + == 0.01 * self.heat_of_reaction * b.consumption_rate + ) + b.H2_balance = pe.Constraint( + expr=component_f[out_stream, 'H2'] + == component_f[in_stream, 'H2'] - b.consumption_rate + ) + b.CO_balance = pe.Constraint( + expr=component_f[out_stream, 'CO'] + == component_f[in_stream, 'CO'] - 0.5 * b.consumption_rate + ) + b.CH3OH_balance = pe.Constraint( + expr=component_f[out_stream, 'CH3OH'] + == component_f[in_stream, 'CH3OH'] + 0.5 * b.consumption_rate + ) + b.CH4_balance = pe.Constraint( + expr=component_f[out_stream, 'CH4'] == component_f[in_stream, 'CH4'] + ) + b.eq_conversion_con = pe.Constraint( + expr=b.equilibrium_conversion + == self.K * (1 - (self.delta_H * pe.exp(-18 * b.t_inv) * b.p_sq_inv)) + ) + b.conversion_con = pe.Constraint( + expr=b.conversion * f[in_stream] + == b.equilibrium_conversion + * (1 - pe.exp(-self.volume_conversion[u] * self.reactor_volume)) + * ( + component_f[in_stream, 'H2'] + + component_f[in_stream, 'CO'] + + component_f[in_stream, 'CH3OH'] + ) + ) b.pressure_con1 = pe.Constraint(expr=b.pressure == p[in_stream]) - b.pressure_con2 = pe.Constraint(expr=p[out_stream] == self.reactor_relation*b.pressure) + b.pressure_con2 = pe.Constraint( + expr=p[out_stream] == self.reactor_relation * b.pressure + ) b.temp_con = pe.Constraint(expr=b.temp == t[out_stream]) def build_flash(self, block, unit_number): @@ -504,7 +640,7 @@ def build_flash(self, block, unit_number): vapor_stream = self.vapor_outlets[u] liquid_stream = self.liquid_outlets[u] b = pe.Block() - setattr(block, 'flash_'+str(u), b) + setattr(block, 'flash_' + str(u), b) b.vapor_pressure = pe.Var(m.components, bounds=(0.001, 80)) b.flash_t = pe.Var(bounds=(3, 5)) @@ -528,23 +664,50 @@ def build_flash(self, block, unit_number): b.antoine_C['CH4'] = -7.16 def _component_balances(_b, _c): - return m.component_flows[in_stream, _c] == m.component_flows[vapor_stream, _c] + m.component_flows[liquid_stream, _c] + return ( + m.component_flows[in_stream, _c] + == m.component_flows[vapor_stream, _c] + + m.component_flows[liquid_stream, _c] + ) + b.component_balances = pe.Constraint(m.components, rule=_component_balances) def _antoine(_b, _c): - return (_b.antoine_A[_c] - pe.log(self.antoine_unit_trans * _b.vapor_pressure[_c])) * (100 * _b.flash_t - _b.antoine_C[_c]) == _b.antoine_B[_c] + return ( + _b.antoine_A[_c] + - pe.log(self.antoine_unit_trans * _b.vapor_pressure[_c]) + ) * (100 * _b.flash_t - _b.antoine_C[_c]) == _b.antoine_B[_c] + b.antoine_con = pe.Constraint(m.components, rule=_antoine) def _vle(_b, _c): - return _b.vapor_recovery['H2'] * (_b.vapor_recovery[_c] * _b.vapor_pressure['H2'] + (1 - _b.vapor_recovery[_c]) * _b.vapor_pressure[_c]) == _b.vapor_pressure['H2'] * _b.vapor_recovery[_c] + return ( + _b.vapor_recovery['H2'] + * ( + _b.vapor_recovery[_c] * _b.vapor_pressure['H2'] + + (1 - _b.vapor_recovery[_c]) * _b.vapor_pressure[_c] + ) + == _b.vapor_pressure['H2'] * _b.vapor_recovery[_c] + ) + b.vle_set = pe.Set(initialize=['CO', 'CH3OH', 'CH4'], ordered=True) b.vle_con = pe.Constraint(b.vle_set, rule=_vle) def _vapor_recovery(_b, _c): - return m.component_flows[vapor_stream, _c] == _b.vapor_recovery[_c] * m.component_flows[in_stream, _c] + return ( + m.component_flows[vapor_stream, _c] + == _b.vapor_recovery[_c] * m.component_flows[in_stream, _c] + ) + b.vapor_recovery_con = pe.Constraint(m.components, rule=_vapor_recovery) - b.total_p_con = pe.Constraint(expr=b.flash_p*f[liquid_stream] == sum(b.vapor_pressure[_c] * m.component_flows[liquid_stream, _c] for _c in m.components)) + b.total_p_con = pe.Constraint( + expr=b.flash_p * f[liquid_stream] + == sum( + b.vapor_pressure[_c] * m.component_flows[liquid_stream, _c] + for _c in m.components + ) + ) b.flash_p_con = pe.ConstraintList() b.flash_p_con.add(b.flash_p == p[in_stream]) @@ -561,22 +724,35 @@ def build_stream_doesnt_exist_con(self, block, stream): b = pe.Block() setattr(block, 'stream_doesnt_exist_con_' + str(stream), b) b.zero_flow_con = pe.Constraint(expr=m.flows[stream] == 0) + def _zero_component_flows(_b, _c): return m.component_flows[stream, _c] == 0 - b.zero_component_flows_con = pe.Constraint(m.components, rule=_zero_component_flows) + + b.zero_component_flows_con = pe.Constraint( + m.components, rule=_zero_component_flows + ) b.fixed_temp_con = pe.Constraint(expr=m.temps[stream] == 3) b.fixed_pressure_con = pe.Constraint(expr=m.pressures[stream] == 1) def enumerate_solutions(): import time + feed_choices = ['cheap', 'expensive'] feed_compressor_choices = ['single_stage', 'two_stage'] reactor_choices = ['cheap', 'expensive'] recycle_compressor_choices = ['single_stage', 'two_stage'] - print('{0:<20}{1:<20}{2:<20}{3:<20}{4:<20}{5:<20}'.format('feed choice', 'feed compressor', 'reactor choice', - 'recycle compressor', 'termination cond', 'profit')) + print( + '{0:<20}{1:<20}{2:<20}{3:<20}{4:<20}{5:<20}'.format( + 'feed choice', + 'feed compressor', + 'reactor choice', + 'recycle compressor', + 'termination cond', + 'profit', + ) + ) since = time.time() for feed_choice in feed_choices: for feed_compressor_choice in feed_compressor_choices: @@ -584,13 +760,16 @@ def enumerate_solutions(): for recycle_compressor_choice in recycle_compressor_choices: m = MethanolModel() m = m.model - for _d in m.component_data_objects(gdp.Disjunct, descend_into=True, active=True, sort=True): + for _d in m.component_data_objects( + gdp.Disjunct, descend_into=True, active=True, sort=True + ): _d.BigM = pe.Suffix() - for _c in _d.component_data_objects(pe.Constraint, descend_into=True, active=True, sort=True): + for _c in _d.component_data_objects( + pe.Constraint, descend_into=True, active=True, sort=True + ): lb, ub = compute_bounds_on_expr(_c.body) _d.BigM[_c] = max(abs(lb), abs(ub)) - if feed_choice == 'cheap': m.cheap_feed_disjunct.indicator_var.fix(1) m.expensive_feed_disjunct.indicator_var.fix(0) @@ -633,25 +812,38 @@ def enumerate_solutions(): opt = pe.SolverFactory('ipopt') res = opt.solve(m, tee=False) - print('{0:<20}{1:<20}{2:<20}{3:<20}{4:<20}{5:<20}'.format(feed_choice, feed_compressor_choice, - reactor_choice, recycle_compressor_choice, - str(res.solver.termination_condition), - str(-pe.value(m.objective)))) - time_elapsed = time.time() - since + print( + '{0:<20}{1:<20}{2:<20}{3:<20}{4:<20}{5:<20}'.format( + feed_choice, + feed_compressor_choice, + reactor_choice, + recycle_compressor_choice, + str(res.solver.termination_condition), + str(-pe.value(m.objective)), + ) + ) + time_elapsed = time.time() - since print('The code run {:.0f}m {:.0f}s'.format(time_elapsed // 60, time_elapsed % 60)) return m - + + def solve_with_gdp_opt(): m = MethanolModel().model - for _d in m.component_data_objects(gdp.Disjunct, descend_into=True, active=True, sort=True): + for _d in m.component_data_objects( + gdp.Disjunct, descend_into=True, active=True, sort=True + ): _d.BigM = pe.Suffix() - for _c in _d.component_data_objects(pe.Constraint, descend_into=True, active=True, sort=True): + for _c in _d.component_data_objects( + pe.Constraint, descend_into=True, active=True, sort=True + ): lb, ub = compute_bounds_on_expr(_c.body) _d.BigM[_c] = max(abs(lb), abs(ub)) opt = pe.SolverFactory('gdpopt') - res = opt.solve(m, algorithm='LOA', mip_solver='gams',nlp_solver='gams',tee=True) - for d in m.component_data_objects(ctype=gdp.Disjunct, active=True, sort=True, descend_into=True): + res = opt.solve(m, algorithm='LOA', mip_solver='gams', nlp_solver='gams', tee=True) + for d in m.component_data_objects( + ctype=gdp.Disjunct, active=True, sort=True, descend_into=True + ): if d.indicator_var.value == 1: print(d.name) print(res) @@ -661,8 +853,8 @@ def solve_with_gdp_opt(): if __name__ == '__main__': logging.basicConfig(level=logging.DEBUG, filename='out.log') - from pyomo.util.model_size import * + from pyomo.util.model_size import * + # m = enumerate() m = solve_with_gdp_opt() print(build_model_size_report(m)) - \ No newline at end of file diff --git a/gdplib/mod_hens/__init__.py b/gdplib/mod_hens/__init__.py index 7ca419a..96bc5d3 100644 --- a/gdplib/mod_hens/__init__.py +++ b/gdplib/mod_hens/__init__.py @@ -1,11 +1,16 @@ from functools import partial from .conventional import build_conventional as _conv -from .modular_discrete import build_modular_option as _disc_opt, build_require_modular as _disc_mod +from .modular_discrete import ( + build_modular_option as _disc_opt, + build_require_modular as _disc_mod, +) from .modular_discrete_single_module import build_single_module as _disc_sing from .modular_integer import ( - build_modular_option as _int_opt, build_require_modular as _int_mod, - build_single_module as _int_sing, ) + build_modular_option as _int_opt, + build_require_modular as _int_mod, + build_single_module as _int_sing, +) # These are the functions that we want to expose as public build_conventional = partial(_conv, cafaro_approx=True, num_stages=4) @@ -16,6 +21,12 @@ build_discrete_require_modular = partial(_disc_mod, cafaro_approx=True, num_stages=4) build_discrete_modular_option = partial(_disc_opt, cafaro_approx=True, num_stages=4) -__all__ = ['build_conventional', 'build_integer_single_module', 'build_integer_require_modular', - 'build_integer_modular_option', 'build_discrete_single_module', 'build_discrete_require_modular', - 'build_discrete_modular_option'] +__all__ = [ + 'build_conventional', + 'build_integer_single_module', + 'build_integer_require_modular', + 'build_integer_modular_option', + 'build_discrete_single_module', + 'build_discrete_require_modular', + 'build_discrete_modular_option', +] diff --git a/gdplib/mod_hens/cafaro_approx.py b/gdplib/mod_hens/cafaro_approx.py index 28e4518..236544f 100644 --- a/gdplib/mod_hens/cafaro_approx.py +++ b/gdplib/mod_hens/cafaro_approx.py @@ -11,9 +11,18 @@ (2) cost = factor * k * ln(bx + 1) """ + from __future__ import division -from pyomo.environ import (ConcreteModel, Constraint, log, NonNegativeReals, SolverFactory, value, Var) +from pyomo.environ import ( + ConcreteModel, + Constraint, + log, + NonNegativeReals, + SolverFactory, + value, + Var, +) def calculate_cafaro_coefficients(area1, area2, exponent): @@ -39,7 +48,7 @@ def calculate_cafaro_coefficients(area1, area2, exponent): ------- tuple of float A tuple containing the coefficients `k` and `b`. - + References ---------- [1] Cafaro, D. C., & Grossmann, I. E. (2014). Alternate approximation of concave cost functions for process design and supply chain optimization problems. Computers & chemical engineering, 60, 376-380. https://doi.org/10.1016/j.compchemeng.2013.10.001 @@ -48,10 +57,8 @@ def calculate_cafaro_coefficients(area1, area2, exponent): m.k = Var(domain=NonNegativeReals) m.b = Var(domain=NonNegativeReals) - m.c1 = Constraint( - expr=area1 ** exponent == m.k * log(m.b * area1 + 1)) - m.c2 = Constraint( - expr=area2 ** exponent == m.k * log(m.b * area2 + 1)) + m.c1 = Constraint(expr=area1**exponent == m.k * log(m.b * area1 + 1)) + m.c2 = Constraint(expr=area2**exponent == m.k * log(m.b * area2 + 1)) SolverFactory('ipopt').solve(m) diff --git a/gdplib/mod_hens/common.py b/gdplib/mod_hens/common.py index 04722cb..2bc7c5f 100644 --- a/gdplib/mod_hens/common.py +++ b/gdplib/mod_hens/common.py @@ -13,6 +13,7 @@ References: Yee, T. F., & Grossmann, I. E. (1990). Simultaneous optimization models for heat integration—II. Heat exchanger network synthesis. Computers & Chemical Engineering, 14(10), 1165–1184. https://doi.org/10.1016/0098-1354(90)85010-8 """ + from __future__ import division from pyomo.environ import ( @@ -407,14 +408,16 @@ def overall_utility_stream_usage(m, strm): for stg in m.stages ) if strm in m.cold_utility_streams - else 0 - + sum( - m.heat_exchanged[strm, cold, stg] - for cold in m.cold_process_streams - for stg in m.stages + else ( + 0 + + sum( + m.heat_exchanged[strm, cold, stg] + for cold in m.cold_process_streams + for stg in m.stages + ) + if strm in m.hot_utility_streams + else 0 ) - if strm in m.hot_utility_streams - else 0 ) @m.Constraint( diff --git a/gdplib/mod_hens/conventional.py b/gdplib/mod_hens/conventional.py index 3eec0fb..a6f8080 100644 --- a/gdplib/mod_hens/conventional.py +++ b/gdplib/mod_hens/conventional.py @@ -7,6 +7,7 @@ This is an implementation of the conventional problem. """ + from __future__ import division from pyomo.environ import Constraint, log, value diff --git a/gdplib/mod_hens/modular_discrete.py b/gdplib/mod_hens/modular_discrete.py index 66205b1..6c70f1f 100644 --- a/gdplib/mod_hens/modular_discrete.py +++ b/gdplib/mod_hens/modular_discrete.py @@ -11,6 +11,7 @@ the nonlinear expressions. """ + from __future__ import division from pyomo.environ import Binary, Constraint, log, Set, Var diff --git a/gdplib/mod_hens/modular_discrete_single_module.py b/gdplib/mod_hens/modular_discrete_single_module.py index f3f863a..0fbe980 100644 --- a/gdplib/mod_hens/modular_discrete_single_module.py +++ b/gdplib/mod_hens/modular_discrete_single_module.py @@ -12,6 +12,7 @@ exchanger module type (size). """ + from __future__ import division from pyomo.environ import Binary, Constraint, RangeSet, Var diff --git a/gdplib/mod_hens/modular_integer.py b/gdplib/mod_hens/modular_integer.py index 6034037..6a4ccee 100644 --- a/gdplib/mod_hens/modular_integer.py +++ b/gdplib/mod_hens/modular_integer.py @@ -8,6 +8,7 @@ modules using integer variables for module selection. """ + from __future__ import division from pyomo.environ import Constraint, Integers, log, Var diff --git a/gdplib/modprodnet/__init__.py b/gdplib/modprodnet/__init__.py index 1bd64af..b0866e5 100644 --- a/gdplib/modprodnet/__init__.py +++ b/gdplib/modprodnet/__init__.py @@ -8,5 +8,10 @@ build_cap_expand_dip = partial(_capacity_expansion, case="Dip") build_cap_expand_decay = partial(_capacity_expansion, case="Decay") -__all__ = ['build_cap_expand_growth', 'build_cap_expand_dip', 'build_cap_expand_decay', 'build_distributed_model', - 'build_quarter_distributed_model'] +__all__ = [ + 'build_cap_expand_growth', + 'build_cap_expand_dip', + 'build_cap_expand_decay', + 'build_distributed_model', + 'build_quarter_distributed_model', +] diff --git a/gdplib/modprodnet/distributed.py b/gdplib/modprodnet/distributed.py index 1988b4f..e4fa585 100644 --- a/gdplib/modprodnet/distributed.py +++ b/gdplib/modprodnet/distributed.py @@ -19,7 +19,9 @@ def build_model(): xls_data = pd.read_excel( os.path.join(os.path.dirname(__file__), "multiple_market_size.xlsx"), - sheet_name=["demand", "locations"], index_col=0) + sheet_name=["demand", "locations"], + index_col=0, + ) @m.Param(m.markets, m.months) def market_demand(m, mkt, mo): @@ -31,20 +33,22 @@ def transport_cost(m, mo): m.route_fixed_cost = Param( initialize=100, - doc="Cost of establishing a route from a modular site to a market") + doc="Cost of establishing a route from a modular site to a market", + ) m.conv_x = Var(bounds=(0, 300), doc="x-coordinate of centralized plant.") m.conv_y = Var(bounds=(0, 300), doc="y-coordinate of centralized plant.") - m.conv_size = Var(bounds=(10, 500), initialize=10, - doc="Size of conventional plant.") + m.conv_size = Var( + bounds=(10, 500), initialize=10, doc="Size of conventional plant." + ) m.conv_cost = Var() m.conv_base_cost = Param(initialize=1000, doc="Cost for size 20") m.conv_exponent = Param(initialize=0.6) m.cost_calc = Constraint( - expr=m.conv_cost == ( - m.conv_base_cost * (m.conv_size / 20) ** m.conv_exponent)) + expr=m.conv_cost == (m.conv_base_cost * (m.conv_size / 20) ** m.conv_exponent) + ) @m.Param(m.markets) def mkt_x(m, mkt): @@ -58,17 +62,21 @@ def mkt_y(m, mkt): @m.Constraint(m.markets) def distance_calculation(m, mkt): - return m.dist_to_mkt[mkt] == sqrt( - (m.conv_x / 300 - m.mkt_x[mkt] / 300)**2 + - (m.conv_y / 300 - m.mkt_y[mkt] / 300)**2) * 300 + return ( + m.dist_to_mkt[mkt] + == sqrt( + (m.conv_x / 300 - m.mkt_x[mkt] / 300) ** 2 + + (m.conv_y / 300 - m.mkt_y[mkt] / 300) ** 2 + ) + * 300 + ) m.shipments_to_mkt = Var(m.markets, m.months, bounds=(0, 100)) m.production = Var(m.months, bounds=(0, 500)) @m.Constraint(m.months) def production_satisfaction(m, mo): - return m.production[mo] == sum(m.shipments_to_mkt[mkt, mo] - for mkt in m.markets) + return m.production[mo] == sum(m.shipments_to_mkt[mkt, mo] for mkt in m.markets) @m.Constraint(m.months) def size_requirement(m, mo): @@ -83,12 +91,15 @@ def demand_satisfaction(m, mkt, mo): m.variable_shipment_cost = Expression( expr=sum( - m.shipments_to_mkt[mkt, mo] * m.dist_to_mkt[mkt] * - m.transport_cost[mo] - for mkt in m.markets for mo in m.months)) + m.shipments_to_mkt[mkt, mo] * m.dist_to_mkt[mkt] * m.transport_cost[mo] + for mkt in m.markets + for mo in m.months + ) + ) m.total_cost = Objective( - expr=m.variable_shipment_cost + m.conv_cost + 5 * m.route_fixed_cost) + expr=m.variable_shipment_cost + m.conv_cost + 5 * m.route_fixed_cost + ) return m @@ -103,14 +114,15 @@ def build_modular_model(): m.markets = RangeSet(5) m.modular_sites = RangeSet(1, 3) m.site_pairs = Set( - initialize=m.modular_sites * m.modular_sites, - filter=lambda _, x, y: not x == y) - m.unique_site_pairs = Set( - initialize=m.site_pairs, filter=lambda _, x, y: x < y) + initialize=m.modular_sites * m.modular_sites, filter=lambda _, x, y: not x == y + ) + m.unique_site_pairs = Set(initialize=m.site_pairs, filter=lambda _, x, y: x < y) xls_data = pd.read_excel( os.path.join(os.path.dirname(__file__), "multiple_market_size.xlsx"), - sheet_name=["demand", "locations"], index_col=0) + sheet_name=["demand", "locations"], + index_col=0, + ) @m.Param(m.markets, m.months) def market_demand(m, mkt, mo): @@ -122,11 +134,13 @@ def transport_cost(m, mo): m.route_fixed_cost = Param( initialize=100, - doc="Cost of establishing a route from a modular site to a market") + doc="Cost of establishing a route from a modular site to a market", + ) @m.Param(m.months, doc="Cost of transporting a module one mile") def modular_transport_cost(m, mo): return 1 * (1 + m.discount_rate / 12) ** (-mo / 12) + m.modular_base_cost = Param(initialize=1000, doc="Cost for size 20") @m.Param(m.months) @@ -141,32 +155,27 @@ def mkt_x(m, mkt): def mkt_y(m, mkt): return float(xls_data["locations"]["y"]["market%s" % mkt]) - m.site_x = Var( - m.modular_sites, bounds=(0, 300), initialize={ - 1: 50, 2: 225, 3: 250}) - m.site_y = Var( - m.modular_sites, bounds=(0, 300), initialize={ - 1: 300, 2: 275, 3: 50}) + m.site_x = Var(m.modular_sites, bounds=(0, 300), initialize={1: 50, 2: 225, 3: 250}) + m.site_y = Var(m.modular_sites, bounds=(0, 300), initialize={1: 300, 2: 275, 3: 50}) - m.num_modules = Var( - m.modular_sites, m.months, domain=Integers, bounds=(0, 25)) + m.num_modules = Var(m.modular_sites, m.months, domain=Integers, bounds=(0, 25)) m.modules_transferred = Var( - m.site_pairs, m.months, - domain=Integers, bounds=(0, 25), - doc="Number of modules moved from one site to another in a month.") + m.site_pairs, + m.months, + domain=Integers, + bounds=(0, 25), + doc="Number of modules moved from one site to another in a month.", + ) # m.modules_transferred[...].fix(0) - m.modules_added = Var( - m.modular_sites, m.months, domain=Integers, bounds=(0, 25)) + m.modules_added = Var(m.modular_sites, m.months, domain=Integers, bounds=(0, 25)) - m.dist_to_mkt = Var( - m.modular_sites, m.markets, bounds=(0, sqrt(300**2 + 300**2))) + m.dist_to_mkt = Var(m.modular_sites, m.markets, bounds=(0, sqrt(300**2 + 300**2))) m.sqr_scaled_dist_to_mkt = Var( - m.modular_sites, m.markets, bounds=(0.001, 8), initialize=0.001) + m.modular_sites, m.markets, bounds=(0.001, 8), initialize=0.001 + ) m.dist_to_site = Var(m.site_pairs, bounds=(0, sqrt(300**2 + 300**2))) - m.sqr_scaled_dist_to_site = Var( - m.site_pairs, bounds=(0.001, 8), initialize=0.001) - m.shipments_to_mkt = Var( - m.modular_sites, m.markets, m.months, bounds=(0, 100)) + m.sqr_scaled_dist_to_site = Var(m.site_pairs, bounds=(0.001, 8), initialize=0.001) + m.shipments_to_mkt = Var(m.modular_sites, m.markets, m.months, bounds=(0, 100)) m.production = Var(m.modular_sites, m.months, bounds=(0, 500)) @m.Disjunct(m.modular_sites) @@ -174,7 +183,8 @@ def site_active(disj, site): @disj.Constraint(m.months) def production_satisfaction(site_disj, mo): return m.production[site, mo] == sum( - m.shipments_to_mkt[site, mkt, mo] for mkt in m.markets) + m.shipments_to_mkt[site, mkt, mo] for mkt in m.markets + ) @disj.Constraint(m.months) def production_limit(site_disj, mo): @@ -183,34 +193,49 @@ def production_limit(site_disj, mo): @disj.Constraint(m.months) def module_balance(site_disj, mo): existing_modules = m.num_modules[site, mo - 1] if mo >= 1 else 0 - new_modules = (m.modules_added[site, mo - m.modular_setup_time] - if mo > m.modular_setup_time else 0) + new_modules = ( + m.modules_added[site, mo - m.modular_setup_time] + if mo > m.modular_setup_time + else 0 + ) xfrd_in_modules = sum( - m.modules_transferred[from_site, - site, mo - m.modular_move_time] + m.modules_transferred[from_site, site, mo - m.modular_move_time] for from_site in m.modular_sites - if (not from_site == site) and mo > m.modular_move_time) + if (not from_site == site) and mo > m.modular_move_time + ) xfrd_out_modules = sum( m.modules_transferred[site, to_site, mo] - for to_site in m.modular_sites if not to_site == site) + for to_site in m.modular_sites + if not to_site == site + ) return m.num_modules[site, mo] == ( - new_modules + xfrd_in_modules - xfrd_out_modules + - existing_modules) + new_modules + xfrd_in_modules - xfrd_out_modules + existing_modules + ) @m.Disjunct(m.modular_sites) def site_inactive(disj, site): disj.no_modules = Constraint( - expr=sum(m.num_modules[site, mo] for mo in m.months) == 0) + expr=sum(m.num_modules[site, mo] for mo in m.months) == 0 + ) disj.no_module_transfer = Constraint( - expr=sum(m.modules_transferred[site1, site2, mo] - for site1, site2 in m.site_pairs - for mo in m.months - if site1 == site or site2 == site) == 0) + expr=sum( + m.modules_transferred[site1, site2, mo] + for site1, site2 in m.site_pairs + for mo in m.months + if site1 == site or site2 == site + ) + == 0 + ) disj.no_shipments = Constraint( - expr=sum(m.shipments_to_mkt[site, mkt, mo] - for mkt in m.markets for mo in m.months) == 0) + expr=sum( + m.shipments_to_mkt[site, mkt, mo] + for mkt in m.markets + for mo in m.months + ) + == 0 + ) @m.Disjunction(m.modular_sites) def site_active_or_not(m, site): @@ -219,40 +244,52 @@ def site_active_or_not(m, site): @m.Constraint(m.modular_sites, doc="Symmetry breaking for site activation") def site_active_ordering(m, site): if site + 1 <= max(m.modular_sites): - return (m.site_active[site].binary_indicator_var >= - m.site_active[site + 1].binary_indicator_var) + return ( + m.site_active[site].binary_indicator_var + >= m.site_active[site + 1].binary_indicator_var + ) else: return Constraint.NoConstraint @m.Disjunct(m.unique_site_pairs) def pair_active(disj, site1, site2): disj.site1_active = Constraint( - expr=m.site_active[site1].binary_indicator_var == 1) + expr=m.site_active[site1].binary_indicator_var == 1 + ) disj.site2_active = Constraint( - expr=m.site_active[site2].binary_indicator_var == 1) + expr=m.site_active[site2].binary_indicator_var == 1 + ) disj.site_distance_calc = Constraint( - expr=m.dist_to_site[site1, site2] == sqrt( - m.sqr_scaled_dist_to_site[site1, site2]) * 150) + expr=m.dist_to_site[site1, site2] + == sqrt(m.sqr_scaled_dist_to_site[site1, site2]) * 150 + ) disj.site_distance_symmetry = Constraint( - expr=m.dist_to_site[site1, site2] == m.dist_to_site[site2, site1]) + expr=m.dist_to_site[site1, site2] == m.dist_to_site[site2, site1] + ) disj.site_sqr_distance_calc = Constraint( - expr=m.sqr_scaled_dist_to_site[site1, site2] == ( - (m.site_x[site1] / 150 - m.site_x[site2] / 150)**2 + - (m.site_y[site1] / 150 - m.site_y[site2] / 150)**2)) + expr=m.sqr_scaled_dist_to_site[site1, site2] + == ( + (m.site_x[site1] / 150 - m.site_x[site2] / 150) ** 2 + + (m.site_y[site1] / 150 - m.site_y[site2] / 150) ** 2 + ) + ) disj.site_sqr_distance_symmetry = Constraint( - expr=m.sqr_scaled_dist_to_site[site1, site2] == - m.sqr_scaled_dist_to_site[site2, site1]) + expr=m.sqr_scaled_dist_to_site[site1, site2] + == m.sqr_scaled_dist_to_site[site2, site1] + ) @m.Disjunct(m.unique_site_pairs) def pair_inactive(disj, site1, site2): disj.site1_inactive = Constraint( - expr=m.site_active[site1].binary_indicator_var == 0) + expr=m.site_active[site1].binary_indicator_var == 0 + ) disj.site2_inactive = Constraint( - expr=m.site_active[site2].binary_indicator_var == 0) + expr=m.site_active[site2].binary_indicator_var == 0 + ) disj.no_module_transfer = Constraint( - expr=sum(m.modules_transferred[site1, site2, mo] - for mo in m.months) == 0) + expr=sum(m.modules_transferred[site1, site2, mo] for mo in m.months) == 0 + ) @m.Disjunction(m.unique_site_pairs) def site_pair_active_or_not(m, site1, site2): @@ -261,61 +298,82 @@ def site_pair_active_or_not(m, site1, site2): @m.Constraint(m.markets, m.months) def demand_satisfaction(m, mkt, mo): return m.market_demand[mkt, mo] <= sum( - m.shipments_to_mkt[site, mkt, mo] for site in m.modular_sites) + m.shipments_to_mkt[site, mkt, mo] for site in m.modular_sites + ) @m.Disjunct(m.modular_sites, m.markets) def product_route_active(disj, site, mkt): disj.site_active = Constraint( - expr=m.site_active[site].binary_indicator_var == 1) + expr=m.site_active[site].binary_indicator_var == 1 + ) @disj.Constraint() def market_distance_calculation(site_disj): - return m.dist_to_mkt[site, mkt] == sqrt( - m.sqr_scaled_dist_to_mkt[site, mkt]) * 150 + return ( + m.dist_to_mkt[site, mkt] + == sqrt(m.sqr_scaled_dist_to_mkt[site, mkt]) * 150 + ) @disj.Constraint() def market_sqr_distance_calc(site_disj): return m.sqr_scaled_dist_to_mkt[site, mkt] == ( - (m.site_x[site] / 150 - m.mkt_x[mkt] / 150)**2 + - (m.site_y[site] / 150 - m.mkt_y[mkt] / 150)**2) + (m.site_x[site] / 150 - m.mkt_x[mkt] / 150) ** 2 + + (m.site_y[site] / 150 - m.mkt_y[mkt] / 150) ** 2 + ) @m.Disjunct(m.modular_sites, m.markets) def product_route_inactive(disj, site, mkt): disj.no_shipments = Constraint( - expr=sum(m.shipments_to_mkt[site, mkt, mo] - for mo in m.months) == 0) + expr=sum(m.shipments_to_mkt[site, mkt, mo] for mo in m.months) == 0 + ) @m.Disjunction(m.modular_sites, m.markets) def product_route_active_or_not(m, site, mkt): - return [m.product_route_active[site, mkt], - m.product_route_inactive[site, mkt]] + return [m.product_route_active[site, mkt], m.product_route_inactive[site, mkt]] m.variable_shipment_cost = Expression( expr=sum( - m.shipments_to_mkt[site, mkt, mo] * m.dist_to_mkt[site, mkt] * - m.transport_cost[mo] - for site in m.modular_sites for mkt in m.markets - for mo in m.months)) + m.shipments_to_mkt[site, mkt, mo] + * m.dist_to_mkt[site, mkt] + * m.transport_cost[mo] + for site in m.modular_sites + for mkt in m.markets + for mo in m.months + ) + ) m.fixed_shipment_cost = Expression( - expr=sum(m.product_route_active[site, mkt].binary_indicator_var * - m.route_fixed_cost - for site in m.modular_sites for mkt in m.markets)) + expr=sum( + m.product_route_active[site, mkt].binary_indicator_var * m.route_fixed_cost + for site in m.modular_sites + for mkt in m.markets + ) + ) m.module_purchase_cost = Expression( - expr=sum(m.modules_added[site, mo] * m.modular_unit_cost[mo] - for site in m.modular_sites for mo in m.months)) + expr=sum( + m.modules_added[site, mo] * m.modular_unit_cost[mo] + for site in m.modular_sites + for mo in m.months + ) + ) - m.module_transfer_cost = Expression(expr=sum( - m.modules_transferred[site1, site2, mo] - * m.dist_to_site[site1, site2] * m.modular_transport_cost[mo] - for site1, site2 in m.site_pairs for mo in m.months)) + m.module_transfer_cost = Expression( + expr=sum( + m.modules_transferred[site1, site2, mo] + * m.dist_to_site[site1, site2] + * m.modular_transport_cost[mo] + for site1, site2 in m.site_pairs + for mo in m.months + ) + ) m.total_cost = Objective( expr=m.variable_shipment_cost + m.fixed_shipment_cost + m.module_purchase_cost - + m.module_transfer_cost) + + m.module_transfer_cost + ) return m @@ -343,8 +401,9 @@ def product_route_active_or_not(m, site, mkt): # 'option reslim=30;']}}) TransformationFactory('gdp.bigm').apply_to(m, bigM=10000) # TransformationFactory('gdp.chull').apply_to(m) - res = SolverFactory('gams').solve(m, tee=True, io_options={ - 'add_options': ['option reslim = 300;']}) + res = SolverFactory('gams').solve( + m, tee=True, io_options={'add_options': ['option reslim = 300;']} + ) # SolverFactory('gams').solve( # m, tee=True, # io_options={ @@ -358,13 +417,13 @@ def record_generator(): for mo in m.months: yield ( (mo,) - + tuple(m.num_modules[site, mo].value - for site in m.modular_sites) - + tuple(m.production[site, mo].value - for site in m.modular_sites) - + tuple(m.shipments_to_mkt[site, mkt, mo].value - for site in m.modular_sites - for mkt in m.markets) + + tuple(m.num_modules[site, mo].value for site in m.modular_sites) + + tuple(m.production[site, mo].value for site in m.modular_sites) + + tuple( + m.shipments_to_mkt[site, mkt, mo].value + for site in m.modular_sites + for mkt in m.markets + ) ) df = pd.DataFrame.from_records( @@ -372,8 +431,12 @@ def record_generator(): columns=("Month",) + tuple("Num Site%s" % site for site in m.modular_sites) + tuple("Prod Site%s" % site for site in m.modular_sites) - + tuple("Ship Site%s to Mkt%s" % (site, mkt) - for site in m.modular_sites for mkt in m.markets)) + + tuple( + "Ship Site%s to Mkt%s" % (site, mkt) + for site in m.modular_sites + for mkt in m.markets + ), + ) df.to_excel("multiple_modular_config.xlsx") print("Total cost: %s" % value(m.total_cost.expr)) print(" Variable ship cost: %s" % value(m.variable_shipment_cost)) @@ -381,40 +444,69 @@ def record_generator(): print(" Module buy cost: %s" % value(m.module_purchase_cost)) print(" Module xfer cost: %s" % value(m.module_transfer_cost)) for site in m.modular_sites: - print("Site {:1.0f} at ({:3.0f}, {:3.0f})".format( - site, m.site_x[site].value, m.site_y[site].value)) - print(" Supplies markets {}".format(tuple( - mkt for mkt in m.markets - if m.product_route_active[site, - mkt].binary_indicator_var.value == 1))) - + print( + "Site {:1.0f} at ({:3.0f}, {:3.0f})".format( + site, m.site_x[site].value, m.site_y[site].value + ) + ) + print( + " Supplies markets {}".format( + tuple( + mkt + for mkt in m.markets + if m.product_route_active[site, mkt].binary_indicator_var.value == 1 + ) + ) + ) if res.solver.termination_condition is not TerminationCondition.optimal: exit() - plt.plot([x.value for x in m.site_x.values()], - [y.value for y in m.site_y.values()], 'k.', markersize=12) - plt.plot([x for x in m.mkt_x.values()], - [y for y in m.mkt_y.values()], 'bo', markersize=12) + plt.plot( + [x.value for x in m.site_x.values()], + [y.value for y in m.site_y.values()], + 'k.', + markersize=12, + ) + plt.plot( + [x for x in m.mkt_x.values()], + [y for y in m.mkt_y.values()], + 'bo', + markersize=12, + ) for mkt in m.markets: - plt.annotate('mkt%s' % mkt, (m.mkt_x[mkt], m.mkt_y[mkt]), - (m.mkt_x[mkt] + 2, m.mkt_y[mkt] + 0)) + plt.annotate( + 'mkt%s' % mkt, + (m.mkt_x[mkt], m.mkt_y[mkt]), + (m.mkt_x[mkt] + 2, m.mkt_y[mkt] + 0), + ) for site in m.modular_sites: plt.annotate( - 'site%s' % site, (m.site_x[site].value, m.site_y[site].value), - (m.site_x[site].value + 2, m.site_y[site].value + 0)) + 'site%s' % site, + (m.site_x[site].value, m.site_y[site].value), + (m.site_x[site].value + 2, m.site_y[site].value + 0), + ) for site, mkt in m.modular_sites * m.markets: if m.product_route_active[site, mkt].binary_indicator_var.value == 1: - plt.arrow(m.site_x[site].value, m.site_y[site].value, - m.mkt_x[mkt] - m.site_x[site].value, - m.mkt_y[mkt] - m.site_y[site].value, - width=0.8, length_includes_head=True, color='r') + plt.arrow( + m.site_x[site].value, + m.site_y[site].value, + m.mkt_x[mkt] - m.site_x[site].value, + m.mkt_y[mkt] - m.site_y[site].value, + width=0.8, + length_includes_head=True, + color='r', + ) for site1, site2 in m.site_pairs: - if sum(m.modules_transferred[site1, site2, mo].value - for mo in m.months) > 0: - plt.arrow(m.site_x[site1].value, m.site_y[site1].value, - m.site_x[site2].value - m.site_x[site1].value, - m.site_y[site2].value - m.site_y[site1].value, - width=0.9, length_includes_head=True, - linestyle='dotted', color='k') + if sum(m.modules_transferred[site1, site2, mo].value for mo in m.months) > 0: + plt.arrow( + m.site_x[site1].value, + m.site_y[site1].value, + m.site_x[site2].value - m.site_x[site1].value, + m.site_y[site2].value - m.site_y[site1].value, + width=0.9, + length_includes_head=True, + linestyle='dotted', + color='k', + ) plt.show() diff --git a/gdplib/modprodnet/model.py b/gdplib/modprodnet/model.py index d5f30b0..562556d 100644 --- a/gdplib/modprodnet/model.py +++ b/gdplib/modprodnet/model.py @@ -4,8 +4,19 @@ import pandas as pd from pyomo.environ import ( - ConcreteModel, Constraint, Integers, maximize, Objective, Param, RangeSet, SolverFactory, summation, - TransformationFactory, value, Var, ) + ConcreteModel, + Constraint, + Integers, + maximize, + Objective, + Param, + RangeSet, + SolverFactory, + summation, + TransformationFactory, + value, + Var, +) from pyomo.gdp import Disjunct, Disjunction @@ -28,17 +39,21 @@ def production_value(m, mo): return (7 / 12) * m.discount_factor[mo] xls_data = pd.read_excel( - os.path.join(os.path.dirname(__file__), "market_size.xlsx"), sheet_name="single_market", index_col=0) + os.path.join(os.path.dirname(__file__), "market_size.xlsx"), + sheet_name="single_market", + index_col=0, + ) @m.Param(m.months) def market_demand(m, mo): return float(xls_data[case][mo]) - m.conv_size = Var(bounds=(25, 350), initialize=25, - doc="Size of conventional plant") + m.conv_size = Var(bounds=(25, 350), initialize=25, doc="Size of conventional plant") m.conv_base_cost = Param(initialize=1000, doc="Cost for size 25") m.conv_exponent = Param(initialize=0.6) - m.conv_cost = Var(bounds=(0, value(m.conv_base_cost * (m.conv_size.ub / 25) ** m.conv_exponent))) + m.conv_cost = Var( + bounds=(0, value(m.conv_base_cost * (m.conv_size.ub / 25) ** m.conv_exponent)) + ) m.module_base_cost = Param(initialize=1000, doc="Cost for size 25") m.production = Var(m.months, bounds=(0, 350)) @@ -68,12 +83,22 @@ def module_buy_cost(m, mo): @m.Expression(m.months) def module_sell_value(m, mo): - return m.module_base_cost * m.discount_factor[mo] * m.module_salvage_value * m.modules_sold[mo] + return ( + m.module_base_cost + * m.discount_factor[mo] + * m.module_salvage_value + * m.modules_sold[mo] + ) @m.Expression() def module_final_salvage(m): mo = max(m.months) - return m.module_base_cost * m.discount_factor[mo] * m.module_salvage_value * m.num_modules[mo] + return ( + m.module_base_cost + * m.discount_factor[mo] + * m.module_salvage_value + * m.num_modules[mo] + ) m.profit = Objective( expr=sum(m.revenue[:]) @@ -82,7 +107,8 @@ def module_final_salvage(m): - summation(m.module_buy_cost) + summation(m.module_sell_value) + m.module_final_salvage, - sense=maximize) + sense=maximize, + ) _build_conventional_disjunct(m) _build_modular_disjunct(m) @@ -95,8 +121,8 @@ def _build_conventional_disjunct(m): m.conventional = Disjunct() m.conventional.cost_calc = Constraint( - expr=m.conv_cost == ( - m.conv_base_cost * (m.conv_size / 25) ** m.conv_exponent)) + expr=m.conv_cost == (m.conv_base_cost * (m.conv_size / 25) ** m.conv_exponent) + ) @m.conventional.Constraint(m.months) def conv_production_limit(conv_disj, mo): @@ -107,7 +133,10 @@ def conv_production_limit(conv_disj, mo): @m.conventional.Constraint() def no_modules(conv_disj): - return sum(m.num_modules[:]) + sum(m.modules_purchased[:]) + sum(m.modules_sold[:]) == 0 + return ( + sum(m.num_modules[:]) + sum(m.modules_purchased[:]) + sum(m.modules_sold[:]) + == 0 + ) def _build_modular_disjunct(m): @@ -116,7 +145,11 @@ def _build_modular_disjunct(m): @m.modular.Constraint(m.months) def module_balance(disj, mo): existing_modules = 0 if mo == 0 else m.num_modules[mo - 1] - new_modules = 0 if mo < m.modular_setup_time else m.modules_purchased[mo - m.modular_setup_time] + new_modules = ( + 0 + if mo < m.modular_setup_time + else m.modules_purchased[mo - m.modular_setup_time] + ) sold_modules = m.modules_sold[mo] return m.num_modules[mo] == existing_modules + new_modules - sold_modules @@ -126,15 +159,22 @@ def modular_production_limit(mod_disj, mo): def display_conventional(m, writer, sheet_name): - df = pd.DataFrame( - list([ - mo, - m.production[mo].value, - m.market_demand[mo], - m.conv_size.value if mo >= m.conv_setup_time else 0 - ] for mo in m.months), - columns=("Month", "Production", "Demand", "Capacity") - ).set_index('Month').round(2) + df = ( + pd.DataFrame( + list( + [ + mo, + m.production[mo].value, + m.market_demand[mo], + m.conv_size.value if mo >= m.conv_setup_time else 0, + ] + for mo in m.months + ), + columns=("Month", "Production", "Demand", "Capacity"), + ) + .set_index('Month') + .round(2) + ) df.to_excel(writer, sheet_name) print('Conventional Profit', round(value(m.profit))) print('Conventional Revenue', round(value(sum(m.revenue[:])))) @@ -145,26 +185,46 @@ def display_conventional(m, writer, sheet_name): def display_modular(m, writer, sheet_name): - df = pd.DataFrame( - list([ - mo, - m.production[mo].value, - m.market_demand[mo], - m.num_modules[mo].value * 25, - m.num_modules[mo].value, - m.modules_purchased[mo].value, - m.modules_sold[mo].value] for mo in m.months - ), - columns=("Month", "Production", "Demand", "Capacity", - "Num Modules", "Add Modules", "Sold Modules") - ).set_index('Month').round(2) + df = ( + pd.DataFrame( + list( + [ + mo, + m.production[mo].value, + m.market_demand[mo], + m.num_modules[mo].value * 25, + m.num_modules[mo].value, + m.modules_purchased[mo].value, + m.modules_sold[mo].value, + ] + for mo in m.months + ), + columns=( + "Month", + "Production", + "Demand", + "Capacity", + "Num Modules", + "Add Modules", + "Sold Modules", + ), + ) + .set_index('Month') + .round(2) + ) df.to_excel(writer, sheet_name) print('Modular Profit', round(value(m.profit))) print('Modular Revenue', round(value(sum(m.revenue[:])))) - print('Modular Revenue before conventional startup', round(value(sum(m.revenue[mo] for mo in m.months if mo < 12)))) + print( + 'Modular Revenue before conventional startup', + round(value(sum(m.revenue[mo] for mo in m.months if mo < 12))), + ) print('Modular Build Cost', round(value(sum(m.module_buy_cost[:])))) print('Modules Purchased', round(value(sum(m.modules_purchased[:])))) - print('Modular Nondiscount Cost', round(value(m.module_base_cost * sum(m.modules_purchased[:])))) + print( + 'Modular Nondiscount Cost', + round(value(m.module_base_cost * sum(m.modules_purchased[:]))), + ) print('Modular Sale Credit', round(value(sum(m.module_sell_value[:])))) print('Modular Final Salvage Credit', round(value(m.module_final_salvage))) print() diff --git a/gdplib/modprodnet/quarter_distributed.py b/gdplib/modprodnet/quarter_distributed.py index f2bae2e..7746881 100644 --- a/gdplib/modprodnet/quarter_distributed.py +++ b/gdplib/modprodnet/quarter_distributed.py @@ -5,10 +5,23 @@ import matplotlib.pyplot as plt import pandas as pd -from pyomo.environ import (ConcreteModel, Constraint, Expression, Integers, - Objective, Param, RangeSet, Set, SolverFactory, - Suffix, TerminationCondition, TransformationFactory, - Var, sqrt, value) +from pyomo.environ import ( + ConcreteModel, + Constraint, + Expression, + Integers, + Objective, + Param, + RangeSet, + Set, + SolverFactory, + Suffix, + TerminationCondition, + TransformationFactory, + Var, + sqrt, + value, +) def build_model(): @@ -20,7 +33,9 @@ def build_model(): xls_data = pd.read_excel( os.path.join(os.path.dirname(__file__), "quarter_multiple_market_size.xlsx"), - sheet_name=["demand", "locations"], index_col=0) + sheet_name=["demand", "locations"], + index_col=0, + ) @m.Param(m.markets, m.quarters) def market_demand(m, mkt, qtr): @@ -32,20 +47,22 @@ def transport_cost(m, qtr): m.route_fixed_cost = Param( initialize=100, - doc="Cost of establishing a route from a modular site to a market") + doc="Cost of establishing a route from a modular site to a market", + ) m.conv_x = Var(bounds=(0, 300), doc="x-coordinate of centralized plant.") m.conv_y = Var(bounds=(0, 300), doc="y-coordinate of centralized plant.") - m.conv_size = Var(bounds=(10, 700), initialize=10, - doc="Size of conventional plant.") + m.conv_size = Var( + bounds=(10, 700), initialize=10, doc="Size of conventional plant." + ) m.conv_cost = Var() m.conv_base_cost = Param(initialize=1000, doc="Cost for size 60") m.conv_exponent = Param(initialize=0.6) m.cost_calc = Constraint( - expr=m.conv_cost == ( - m.conv_base_cost * (m.conv_size / 60) ** m.conv_exponent)) + expr=m.conv_cost == (m.conv_base_cost * (m.conv_size / 60) ** m.conv_exponent) + ) @m.Param(m.markets) def mkt_x(m, mkt): @@ -59,17 +76,23 @@ def mkt_y(m, mkt): @m.Constraint(m.markets) def distance_calculation(m, mkt): - return m.dist_to_mkt[mkt] == sqrt( - (m.conv_x / 150 - m.mkt_x[mkt] / 150)**2 + - (m.conv_y / 150 - m.mkt_y[mkt] / 150)**2) * 150 + return ( + m.dist_to_mkt[mkt] + == sqrt( + (m.conv_x / 150 - m.mkt_x[mkt] / 150) ** 2 + + (m.conv_y / 150 - m.mkt_y[mkt] / 150) ** 2 + ) + * 150 + ) m.shipments_to_mkt = Var(m.markets, m.quarters, bounds=(0, 400)) m.production = Var(m.quarters, bounds=(0, 1500)) @m.Constraint(m.quarters) def production_satisfaction(m, qtr): - return m.production[qtr] == sum(m.shipments_to_mkt[mkt, qtr] - for mkt in m.markets) + return m.production[qtr] == sum( + m.shipments_to_mkt[mkt, qtr] for mkt in m.markets + ) @m.Constraint(m.quarters) def size_requirement(m, qtr): @@ -84,12 +107,15 @@ def demand_satisfaction(m, mkt, qtr): m.variable_shipment_cost = Expression( expr=sum( - m.shipments_to_mkt[mkt, qtr] * m.dist_to_mkt[mkt] * - m.transport_cost[qtr] - for mkt in m.markets for qtr in m.quarters)) + m.shipments_to_mkt[mkt, qtr] * m.dist_to_mkt[mkt] * m.transport_cost[qtr] + for mkt in m.markets + for qtr in m.quarters + ) + ) m.total_cost = Objective( - expr=m.variable_shipment_cost + 5 * m.route_fixed_cost + m.conv_cost) + expr=m.variable_shipment_cost + 5 * m.route_fixed_cost + m.conv_cost + ) return m @@ -104,14 +130,15 @@ def build_modular_model(): m.markets = RangeSet(5) m.modular_sites = RangeSet(3) m.site_pairs = Set( - initialize=m.modular_sites * m.modular_sites, - filter=lambda _, x, y: not x == y) - m.unique_site_pairs = Set( - initialize=m.site_pairs, filter=lambda _, x, y: x < y) + initialize=m.modular_sites * m.modular_sites, filter=lambda _, x, y: not x == y + ) + m.unique_site_pairs = Set(initialize=m.site_pairs, filter=lambda _, x, y: x < y) xls_data = pd.read_excel( os.path.join(os.path.dirname(__file__), "quarter_multiple_market_size.xlsx"), - sheet_name=["demand", "locations"], index_col=0) + sheet_name=["demand", "locations"], + index_col=0, + ) @m.Param(m.markets, m.quarters) def market_demand(m, mkt, qtr): @@ -123,11 +150,13 @@ def transport_cost(m, qtr): m.route_fixed_cost = Param( initialize=100, - doc="Cost of establishing a route from a modular site to a market") + doc="Cost of establishing a route from a modular site to a market", + ) @m.Param(m.quarters, doc="Cost of transporting a module one mile") def modular_transport_cost(m, qtr): return 1 * (1 + m.discount_rate / 4) ** (-qtr / 4) + m.modular_base_cost = Param(initialize=1000, doc="Cost for size 60") @m.Param(m.quarters) @@ -142,32 +171,27 @@ def mkt_x(m, mkt): def mkt_y(m, mkt): return float(xls_data["locations"]["y"]["market%s" % mkt]) - m.site_x = Var( - m.modular_sites, bounds=(0, 300), initialize={ - 1: 50, 2: 225, 3: 250}) - m.site_y = Var( - m.modular_sites, bounds=(0, 300), initialize={ - 1: 300, 2: 275, 3: 50}) + m.site_x = Var(m.modular_sites, bounds=(0, 300), initialize={1: 50, 2: 225, 3: 250}) + m.site_y = Var(m.modular_sites, bounds=(0, 300), initialize={1: 300, 2: 275, 3: 50}) - m.num_modules = Var( - m.modular_sites, m.quarters, domain=Integers, bounds=(0, 12)) + m.num_modules = Var(m.modular_sites, m.quarters, domain=Integers, bounds=(0, 12)) m.modules_transferred = Var( - m.site_pairs, m.quarters, - domain=Integers, bounds=(0, 12), - doc="Number of modules moved from one site to another in a quarter.") + m.site_pairs, + m.quarters, + domain=Integers, + bounds=(0, 12), + doc="Number of modules moved from one site to another in a quarter.", + ) # m.modules_transferred[...].fix(0) - m.modules_added = Var( - m.modular_sites, m.quarters, domain=Integers, bounds=(0, 12)) + m.modules_added = Var(m.modular_sites, m.quarters, domain=Integers, bounds=(0, 12)) - m.dist_to_mkt = Var( - m.modular_sites, m.markets, bounds=(0, sqrt(300**2 + 300**2))) + m.dist_to_mkt = Var(m.modular_sites, m.markets, bounds=(0, sqrt(300**2 + 300**2))) m.sqr_scaled_dist_to_mkt = Var( - m.modular_sites, m.markets, bounds=(0.001, 8), initialize=0.001) + m.modular_sites, m.markets, bounds=(0.001, 8), initialize=0.001 + ) m.dist_to_site = Var(m.site_pairs, bounds=(0, sqrt(300**2 + 300**2))) - m.sqr_scaled_dist_to_site = Var( - m.site_pairs, bounds=(0.001, 8), initialize=0.001) - m.shipments_to_mkt = Var( - m.modular_sites, m.markets, m.quarters, bounds=(0, 300)) + m.sqr_scaled_dist_to_site = Var(m.site_pairs, bounds=(0.001, 8), initialize=0.001) + m.shipments_to_mkt = Var(m.modular_sites, m.markets, m.quarters, bounds=(0, 300)) m.production = Var(m.modular_sites, m.quarters, bounds=(0, 1500)) @m.Disjunct(m.modular_sites) @@ -175,7 +199,8 @@ def site_active(disj, site): @disj.Constraint(m.quarters) def production_satisfaction(site_disj, qtr): return m.production[site, qtr] == sum( - m.shipments_to_mkt[site, mkt, qtr] for mkt in m.markets) + m.shipments_to_mkt[site, mkt, qtr] for mkt in m.markets + ) @disj.Constraint(m.quarters) def production_limit(site_disj, qtr): @@ -184,34 +209,49 @@ def production_limit(site_disj, qtr): @disj.Constraint(m.quarters) def module_balance(site_disj, qtr): existing_modules = m.num_modules[site, qtr - 1] if qtr >= 1 else 0 - new_modules = (m.modules_added[site, qtr - m.modular_setup_time] - if qtr > m.modular_setup_time else 0) + new_modules = ( + m.modules_added[site, qtr - m.modular_setup_time] + if qtr > m.modular_setup_time + else 0 + ) xfrd_in_modules = sum( - m.modules_transferred[from_site, - site, qtr - m.modular_move_time] + m.modules_transferred[from_site, site, qtr - m.modular_move_time] for from_site in m.modular_sites - if (not from_site == site) and qtr > m.modular_move_time) + if (not from_site == site) and qtr > m.modular_move_time + ) xfrd_out_modules = sum( m.modules_transferred[site, to_site, qtr] - for to_site in m.modular_sites if not to_site == site) + for to_site in m.modular_sites + if not to_site == site + ) return m.num_modules[site, qtr] == ( - new_modules + xfrd_in_modules - xfrd_out_modules + - existing_modules) + new_modules + xfrd_in_modules - xfrd_out_modules + existing_modules + ) @m.Disjunct(m.modular_sites) def site_inactive(disj, site): disj.no_modules = Constraint( - expr=sum(m.num_modules[site, qtr] for qtr in m.quarters) == 0) + expr=sum(m.num_modules[site, qtr] for qtr in m.quarters) == 0 + ) disj.no_module_transfer = Constraint( - expr=sum(m.modules_transferred[site1, site2, qtr] - for site1, site2 in m.site_pairs - for qtr in m.quarters - if site1 == site or site2 == site) == 0) + expr=sum( + m.modules_transferred[site1, site2, qtr] + for site1, site2 in m.site_pairs + for qtr in m.quarters + if site1 == site or site2 == site + ) + == 0 + ) disj.no_shipments = Constraint( - expr=sum(m.shipments_to_mkt[site, mkt, qtr] - for mkt in m.markets for qtr in m.quarters) == 0) + expr=sum( + m.shipments_to_mkt[site, mkt, qtr] + for mkt in m.markets + for qtr in m.quarters + ) + == 0 + ) @m.Disjunction(m.modular_sites) def site_active_or_not(m, site): @@ -220,40 +260,53 @@ def site_active_or_not(m, site): @m.Constraint(m.modular_sites, doc="Symmetry breaking for site activation") def site_active_ordering(m, site): if site + 1 <= max(m.modular_sites): - return (m.site_active[site].binary_indicator_var >= - m.site_active[site + 1].binary_indicator_var) + return ( + m.site_active[site].binary_indicator_var + >= m.site_active[site + 1].binary_indicator_var + ) else: return Constraint.NoConstraint @m.Disjunct(m.unique_site_pairs) def pair_active(disj, site1, site2): disj.site1_active = Constraint( - expr=m.site_active[site1].binary_indicator_var == 1) + expr=m.site_active[site1].binary_indicator_var == 1 + ) disj.site2_active = Constraint( - expr=m.site_active[site2].binary_indicator_var == 1) + expr=m.site_active[site2].binary_indicator_var == 1 + ) disj.site_distance_calc = Constraint( - expr=m.dist_to_site[site1, site2] == sqrt( - m.sqr_scaled_dist_to_site[site1, site2]) * 150) + expr=m.dist_to_site[site1, site2] + == sqrt(m.sqr_scaled_dist_to_site[site1, site2]) * 150 + ) disj.site_distance_symmetry = Constraint( - expr=m.dist_to_site[site1, site2] == m.dist_to_site[site2, site1]) + expr=m.dist_to_site[site1, site2] == m.dist_to_site[site2, site1] + ) disj.site_sqr_distance_calc = Constraint( - expr=m.sqr_scaled_dist_to_site[site1, site2] == ( - (m.site_x[site1] / 150 - m.site_x[site2] / 150)**2 + - (m.site_y[site1] / 150 - m.site_y[site2] / 150)**2)) + expr=m.sqr_scaled_dist_to_site[site1, site2] + == ( + (m.site_x[site1] / 150 - m.site_x[site2] / 150) ** 2 + + (m.site_y[site1] / 150 - m.site_y[site2] / 150) ** 2 + ) + ) disj.site_sqr_distance_symmetry = Constraint( - expr=m.sqr_scaled_dist_to_site[site1, site2] == - m.sqr_scaled_dist_to_site[site2, site1]) + expr=m.sqr_scaled_dist_to_site[site1, site2] + == m.sqr_scaled_dist_to_site[site2, site1] + ) @m.Disjunct(m.unique_site_pairs) def pair_inactive(disj, site1, site2): disj.site1_inactive = Constraint( - expr=m.site_active[site1].binary_indicator_var == 0) + expr=m.site_active[site1].binary_indicator_var == 0 + ) disj.site2_inactive = Constraint( - expr=m.site_active[site2].binary_indicator_var == 0) + expr=m.site_active[site2].binary_indicator_var == 0 + ) disj.no_module_transfer = Constraint( - expr=sum(m.modules_transferred[site1, site2, qtr] - for qtr in m.quarters) == 0) + expr=sum(m.modules_transferred[site1, site2, qtr] for qtr in m.quarters) + == 0 + ) @m.Disjunction(m.unique_site_pairs) def site_pair_active_or_not(m, site1, site2): @@ -262,61 +315,82 @@ def site_pair_active_or_not(m, site1, site2): @m.Constraint(m.markets, m.quarters) def demand_satisfaction(m, mkt, qtr): return m.market_demand[mkt, qtr] <= sum( - m.shipments_to_mkt[site, mkt, qtr] for site in m.modular_sites) + m.shipments_to_mkt[site, mkt, qtr] for site in m.modular_sites + ) @m.Disjunct(m.modular_sites, m.markets) def product_route_active(disj, site, mkt): disj.site_active = Constraint( - expr=m.site_active[site].binary_indicator_var == 1) + expr=m.site_active[site].binary_indicator_var == 1 + ) @disj.Constraint() def market_distance_calculation(site_disj): - return m.dist_to_mkt[site, mkt] == sqrt( - m.sqr_scaled_dist_to_mkt[site, mkt]) * 150 + return ( + m.dist_to_mkt[site, mkt] + == sqrt(m.sqr_scaled_dist_to_mkt[site, mkt]) * 150 + ) @disj.Constraint() def market_sqr_distance_calc(site_disj): return m.sqr_scaled_dist_to_mkt[site, mkt] == ( - (m.site_x[site] / 150 - m.mkt_x[mkt] / 150)**2 + - (m.site_y[site] / 150 - m.mkt_y[mkt] / 150)**2) + (m.site_x[site] / 150 - m.mkt_x[mkt] / 150) ** 2 + + (m.site_y[site] / 150 - m.mkt_y[mkt] / 150) ** 2 + ) @m.Disjunct(m.modular_sites, m.markets) def product_route_inactive(disj, site, mkt): disj.no_shipments = Constraint( - expr=sum(m.shipments_to_mkt[site, mkt, qtr] - for qtr in m.quarters) == 0) + expr=sum(m.shipments_to_mkt[site, mkt, qtr] for qtr in m.quarters) == 0 + ) @m.Disjunction(m.modular_sites, m.markets) def product_route_active_or_not(m, site, mkt): - return [m.product_route_active[site, mkt], - m.product_route_inactive[site, mkt]] + return [m.product_route_active[site, mkt], m.product_route_inactive[site, mkt]] m.variable_shipment_cost = Expression( expr=sum( - m.shipments_to_mkt[site, mkt, qtr] * m.dist_to_mkt[site, mkt] * - m.transport_cost[qtr] - for site in m.modular_sites for mkt in m.markets - for qtr in m.quarters)) + m.shipments_to_mkt[site, mkt, qtr] + * m.dist_to_mkt[site, mkt] + * m.transport_cost[qtr] + for site in m.modular_sites + for mkt in m.markets + for qtr in m.quarters + ) + ) m.fixed_shipment_cost = Expression( - expr=sum(m.product_route_active[site, mkt].binary_indicator_var * - m.route_fixed_cost - for site in m.modular_sites for mkt in m.markets)) + expr=sum( + m.product_route_active[site, mkt].binary_indicator_var * m.route_fixed_cost + for site in m.modular_sites + for mkt in m.markets + ) + ) m.module_purchase_cost = Expression( - expr=sum(m.modules_added[site, qtr] * m.modular_unit_cost[qtr] - for site in m.modular_sites for qtr in m.quarters)) + expr=sum( + m.modules_added[site, qtr] * m.modular_unit_cost[qtr] + for site in m.modular_sites + for qtr in m.quarters + ) + ) - m.module_transfer_cost = Expression(expr=sum( - m.modules_transferred[site1, site2, qtr] - * m.dist_to_site[site1, site2] * m.modular_transport_cost[qtr] - for site1, site2 in m.site_pairs for qtr in m.quarters)) + m.module_transfer_cost = Expression( + expr=sum( + m.modules_transferred[site1, site2, qtr] + * m.dist_to_site[site1, site2] + * m.modular_transport_cost[qtr] + for site1, site2 in m.site_pairs + for qtr in m.quarters + ) + ) m.total_cost = Objective( expr=m.variable_shipment_cost + m.fixed_shipment_cost + m.module_purchase_cost - + m.module_transfer_cost) + + m.module_transfer_cost + ) return m @@ -347,10 +421,10 @@ def product_route_active_or_not(m, site, mkt): # res = SolverFactory('gams').solve(m, tee=True, io_options={ # 'add_options': ['option reslim = 300;']}) res = SolverFactory('gams').solve( - m, tee=True, - io_options={ - 'solver': 'baron', - 'add_options': ['option reslim = 600;']}) + m, + tee=True, + io_options={'solver': 'baron', 'add_options': ['option reslim = 600;']}, + ) # from pyomo.util.infeasible import log_infeasible_constraints # log_infeasible_constraints(m) @@ -358,17 +432,18 @@ def record_generator(): for qtr in m.quarters: yield ( (qtr,) - + tuple(m.num_modules[site, qtr].value - for site in m.modular_sites) - + tuple(m.production[site, qtr].value - for site in m.modular_sites) - + tuple(m.shipments_to_mkt[site, mkt, qtr].value - for site in m.modular_sites - for mkt in m.markets) - + tuple(m.modules_added[site, qtr].value - for site in m.modular_sites) - + tuple(m.modules_transferred[site1, site2, qtr].value - for site1, site2 in m.site_pairs) + + tuple(m.num_modules[site, qtr].value for site in m.modular_sites) + + tuple(m.production[site, qtr].value for site in m.modular_sites) + + tuple( + m.shipments_to_mkt[site, mkt, qtr].value + for site in m.modular_sites + for mkt in m.markets + ) + + tuple(m.modules_added[site, qtr].value for site in m.modular_sites) + + tuple( + m.modules_transferred[site1, site2, qtr].value + for site1, site2 in m.site_pairs + ) ) df = pd.DataFrame.from_records( @@ -376,11 +451,14 @@ def record_generator(): columns=("Qtr",) + tuple("Num Site%s" % site for site in m.modular_sites) + tuple("Prod Site%s" % site for site in m.modular_sites) - + tuple("ShipSite%stoMkt%s" % (site, mkt) - for site in m.modular_sites for mkt in m.markets) + + tuple( + "ShipSite%stoMkt%s" % (site, mkt) + for site in m.modular_sites + for mkt in m.markets + ) + tuple("Add Site%s" % site for site in m.modular_sites) - + tuple("Xfer Site%s to %s" % (site1, site2) - for site1, site2 in m.site_pairs)) + + tuple("Xfer Site%s to %s" % (site1, site2) for site1, site2 in m.site_pairs), + ) df.to_excel("quarter_multiple_modular_config.xlsx") print("Total cost: %s" % value(m.total_cost.expr)) print(" Variable ship cost: %s" % value(m.variable_shipment_cost)) @@ -388,39 +466,72 @@ def record_generator(): print(" Module buy cost: %s" % value(m.module_purchase_cost)) print(" Module xfer cost: %s" % value(m.module_transfer_cost)) for site in m.modular_sites: - print("Site {:1.0f} at ({:3.0f}, {:3.0f})".format( - site, m.site_x[site].value, m.site_y[site].value)) - print(" Supplies markets {}".format(tuple( - mkt for mkt in m.markets - if m.product_route_active[site, - mkt].binary_indicator_var.value == 1))) + print( + "Site {:1.0f} at ({:3.0f}, {:3.0f})".format( + site, m.site_x[site].value, m.site_y[site].value + ) + ) + print( + " Supplies markets {}".format( + tuple( + mkt + for mkt in m.markets + if m.product_route_active[site, mkt].binary_indicator_var.value == 1 + ) + ) + ) if res.solver.termination_condition is not TerminationCondition.optimal: exit() - plt.plot([x.value for x in m.site_x.values()], - [y.value for y in m.site_y.values()], 'k.', markersize=12) - plt.plot([x for x in m.mkt_x.values()], - [y for y in m.mkt_y.values()], 'bo', markersize=12) + plt.plot( + [x.value for x in m.site_x.values()], + [y.value for y in m.site_y.values()], + 'k.', + markersize=12, + ) + plt.plot( + [x for x in m.mkt_x.values()], + [y for y in m.mkt_y.values()], + 'bo', + markersize=12, + ) for mkt in m.markets: - plt.annotate('mkt%s' % mkt, (m.mkt_x[mkt], m.mkt_y[mkt]), - (m.mkt_x[mkt] + 2, m.mkt_y[mkt] + 0)) + plt.annotate( + 'mkt%s' % mkt, + (m.mkt_x[mkt], m.mkt_y[mkt]), + (m.mkt_x[mkt] + 2, m.mkt_y[mkt] + 0), + ) for site in m.modular_sites: plt.annotate( - 'site%s' % site, (m.site_x[site].value, m.site_y[site].value), - (m.site_x[site].value + 2, m.site_y[site].value + 0)) + 'site%s' % site, + (m.site_x[site].value, m.site_y[site].value), + (m.site_x[site].value + 2, m.site_y[site].value + 0), + ) for site, mkt in m.modular_sites * m.markets: if m.product_route_active[site, mkt].binary_indicator_var.value == 1: - plt.arrow(m.site_x[site].value, m.site_y[site].value, - m.mkt_x[mkt] - m.site_x[site].value, - m.mkt_y[mkt] - m.site_y[site].value, - width=0.8, length_includes_head=True, color='r') + plt.arrow( + m.site_x[site].value, + m.site_y[site].value, + m.mkt_x[mkt] - m.site_x[site].value, + m.mkt_y[mkt] - m.site_y[site].value, + width=0.8, + length_includes_head=True, + color='r', + ) for site1, site2 in m.site_pairs: - if sum(m.modules_transferred[site1, site2, qtr].value - for qtr in m.quarters) > 1E-6: - plt.arrow(m.site_x[site1].value, m.site_y[site1].value, - m.site_x[site2].value - m.site_x[site1].value, - m.site_y[site2].value - m.site_y[site1].value, - width=0.9, length_includes_head=True, - linestyle='dotted', color='k') + if ( + sum(m.modules_transferred[site1, site2, qtr].value for qtr in m.quarters) + > 1e-6 + ): + plt.arrow( + m.site_x[site1].value, + m.site_y[site1].value, + m.site_x[site2].value - m.site_x[site1].value, + m.site_y[site2].value - m.site_y[site1].value, + width=0.9, + length_includes_head=True, + linestyle='dotted', + color='k', + ) plt.show() diff --git a/gdplib/stranded_gas/model.py b/gdplib/stranded_gas/model.py index 1989d7d..adc6720 100644 --- a/gdplib/stranded_gas/model.py +++ b/gdplib/stranded_gas/model.py @@ -17,9 +17,23 @@ import pandas as pd from pyomo.environ import ( - ConcreteModel, Constraint, Integers, NonNegativeReals, Objective, Param, - RangeSet, Set, SolverFactory, Suffix, TransformationFactory, Var, exp, log, - sqrt, summation, value + ConcreteModel, + Constraint, + Integers, + NonNegativeReals, + Objective, + Param, + RangeSet, + Set, + SolverFactory, + Suffix, + TransformationFactory, + Var, + exp, + log, + sqrt, + summation, + value, ) from gdplib.stranded_gas.util import alphanum_sorted from pyomo.environ import TerminationCondition as tc @@ -43,13 +57,13 @@ def build_model(): m.periods_per_year = Param(initialize=4, doc="Quarters per year") m.project_life = Param(initialize=15, doc="Years") - m.time = RangeSet(0, m.periods_per_year * - m.project_life - 1, doc="Time periods") + m.time = RangeSet(0, m.periods_per_year * m.project_life - 1, doc="Time periods") m.discount_rate = Param(initialize=0.08, doc="8%") - m.learning_rate = Param(initialize=0.1, doc="Fraction discount for doubling of quantity") + m.learning_rate = Param( + initialize=0.1, doc="Fraction discount for doubling of quantity" + ) - m.module_setup_time = Param( - initialize=1, doc="1 quarter for module transfer") + m.module_setup_time = Param(initialize=1, doc="1 quarter for module transfer") @m.Param(m.time) def discount_factor(m, t): @@ -70,7 +84,9 @@ def discount_factor(m, t): """ return (1 + m.discount_rate / m.periods_per_year) ** (-t / m.periods_per_year) - xlsx_data = pd.read_excel(os.path.join(os.path.dirname(__file__), "data.xlsx"), sheet_name=None) + xlsx_data = pd.read_excel( + os.path.join(os.path.dirname(__file__), "data.xlsx"), sheet_name=None + ) module_sheet = xlsx_data['modules'].set_index('Type') m.module_types = Set(initialize=module_sheet.columns.tolist(), doc="Module types") @@ -93,7 +109,9 @@ def module_base_cost(m, mtype): """ return float(module_sheet[mtype]['Capital Cost [MM$]']) - @m.Param(m.module_types, doc="Natural gas consumption per module of this type [MMSCF/d]") + @m.Param( + m.module_types, doc="Natural gas consumption per module of this type [MMSCF/d]" + ) def unit_gas_consumption(m, mtype): """ Calculates the natural gas consumption per module of a given type. @@ -131,7 +149,10 @@ def gasoline_production(m, mtype): """ return float(module_sheet[mtype]['Gasoline [kBD]']) - @m.Param(m.module_types, doc="Overall conversion of natural gas into gasoline per module of this type [kB/MMSCF]") + @m.Param( + m.module_types, + doc="Overall conversion of natural gas into gasoline per module of this type [kB/MMSCF]", + ) def module_conversion(m, mtype): """ Calculates the overall conversion of natural gas into gasoline per module of a given type. @@ -155,7 +176,8 @@ def module_conversion(m, mtype): m.site_pairs = Set( doc="Pairs of potential sites", initialize=m.potential_sites * m.potential_sites, - filter=lambda _, x, y: not x == y) + filter=lambda _, x, y: not x == y, + ) @m.Param(m.potential_sites) def site_x(m, site): @@ -237,14 +259,18 @@ def well_y(m, well): return float(well_sheet['y'][well]) sched_sheet = xlsx_data['well-schedule'] - decay_curve = [1] + [3.69 * exp(-1.31 * (t + 1) ** 0.292) for t in range(m.project_life * 12)] + decay_curve = [1] + [ + 3.69 * exp(-1.31 * (t + 1) ** 0.292) for t in range(m.project_life * 12) + ] well_profiles = {well: [0 for _ in decay_curve] for well in m.well_clusters} for _, well_info in sched_sheet.iterrows(): start_time = int(well_info['Month']) - prod = [0] * start_time + decay_curve[:len(decay_curve) - start_time] + prod = [0] * start_time + decay_curve[: len(decay_curve) - start_time] prod = [x * float(well_info['max prod [MMSCF/d]']) for x in prod] current_profile = well_profiles[well_info['well-cluster']] - well_profiles[well_info['well-cluster']] = [val + prod[i] for i, val in enumerate(current_profile)] + well_profiles[well_info['well-cluster']] = [ + val + prod[i] for i, val in enumerate(current_profile) + ] @m.Param(m.well_clusters, m.time, doc="Supply of gas from well cluster [MMSCF/day]") def gas_supply(m, well, t): @@ -265,7 +291,7 @@ def gas_supply(m, well, t): Pyomo.Parameter A float representing the supply of gas from the well cluster in the given time period. """ - return sum(well_profiles[well][t * 3:t * 3 + 2]) / 3 + return sum(well_profiles[well][t * 3 : t * 3 + 2]) / 3 mkt_sheet = xlsx_data['markets'].set_index('Market') m.markets = Set(initialize=mkt_sheet.index.tolist(), doc="Markets") @@ -335,7 +361,7 @@ def distance(m, src, dest): """ Calculates the Euclidean distance between a source and a destination within the gas processing network. Assuming `src_x`, `src_y` for a source and `dest_x`, `dest_y` for a destination are defined within the model, the distance is calculated as follows: - + distance = sqrt((src_x - dest_x) ** 2 + (src_y - dest_y) ** 2) Parameters @@ -367,17 +393,32 @@ def distance(m, src, dest): return sqrt((src_x - dest_x) ** 2 + (src_y - dest_y) ** 2) m.num_modules = Var( - m.module_types, m.potential_sites, m.time, + m.module_types, + m.potential_sites, + m.time, doc="Number of active modules of each type at a site in a period", - domain=Integers, bounds=(0, 50), initialize=1) + domain=Integers, + bounds=(0, 50), + initialize=1, + ) m.modules_transferred = Var( - m.module_types, m.site_pairs, m.time, + m.module_types, + m.site_pairs, + m.time, doc="Number of module transfers initiated from one site to another in a period.", - domain=Integers, bounds=(0, 15), initialize=0) + domain=Integers, + bounds=(0, 15), + initialize=0, + ) m.modules_purchased = Var( - m.module_types, m.potential_sites, m.time, + m.module_types, + m.potential_sites, + m.time, doc="Number of modules of each type purchased for a site in a period", - domain=Integers, bounds=(0, 30), initialize=1) + domain=Integers, + bounds=(0, 30), + initialize=1, + ) m.pipeline_unit_cost = Param(doc="MM$/mile", initialize=2) @@ -458,7 +499,7 @@ def gasoline_price(m, t): return 2.5 * m.discount_factor[t] @m.Param(m.time, doc="Gasoline transport cost [$/gal/100 miles]") - def gasoline_tranport_cost(m, t): + def gasoline_transport_cost(m, t): """ Calculates the gasoline transport cost in a given time period. @@ -482,7 +523,10 @@ def gasoline_tranport_cost(m, t): m.learning_factor = Var( m.module_types, doc="Fraction of cost due to economies of mass production", - domain=NonNegativeReals, bounds=(0, 1), initialize=1) + domain=NonNegativeReals, + bounds=(0, 1), + initialize=1, + ) @m.Disjunct(m.module_types) def mtype_exists(disj, mtype): @@ -504,11 +548,16 @@ def mtype_exists(disj, mtype): Ensures that at least one module of this type is purchased, activating this disjunct. """ disj.learning_factor_calc = Constraint( - expr=m.learning_factor[mtype] == (1 - m.learning_rate) ** ( - log(sum(m.modules_purchased[mtype, :, :])) / log(2)), doc="Learning factor calculation") + expr=m.learning_factor[mtype] + == (1 - m.learning_rate) + ** (log(sum(m.modules_purchased[mtype, :, :])) / log(2)), + doc="Learning factor calculation", + ) m.BigM[disj.learning_factor_calc] = 1 disj.require_module_purchases = Constraint( - expr=sum(m.modules_purchased[mtype, :, :]) >= 1, doc="At least one module purchase") + expr=sum(m.modules_purchased[mtype, :, :]) >= 1, + doc="At least one module purchase", + ) @m.Disjunct(m.module_types) def mtype_absent(disj, mtype): @@ -523,7 +572,8 @@ def mtype_absent(disj, mtype): Index of the module type. """ disj.constant_learning_factor = Constraint( - expr=m.learning_factor[mtype] == 1, doc="Constant learning factor") + expr=m.learning_factor[mtype] == 1, doc="Constant learning factor" + ) @m.Disjunction(m.module_types) def mtype_existence(m, mtype): @@ -563,25 +613,46 @@ def module_unit_cost(m, mtype, t): Pyomo.Expression A Pyomo Expression that calculates the total unit cost of a module for a given type and time period. """ - return m.module_base_cost[mtype] * m.learning_factor[mtype] * m.discount_factor[t] + return ( + m.module_base_cost[mtype] * m.learning_factor[mtype] * m.discount_factor[t] + ) m.production = Var( - m.potential_sites, m.time, + m.potential_sites, + m.time, doc="Production of gasoline in a time period [kBD]", - domain=NonNegativeReals, bounds=(0, 30), initialize=10) + domain=NonNegativeReals, + bounds=(0, 30), + initialize=10, + ) m.gas_consumption = Var( - m.potential_sites, m.module_types, m.time, + m.potential_sites, + m.module_types, + m.time, doc="Consumption of natural gas by each module type " "at each site in a time period [MMSCF/d]", - domain=NonNegativeReals, bounds=(0, 250), initialize=50) + domain=NonNegativeReals, + bounds=(0, 250), + initialize=50, + ) m.gas_flows = Var( - m.well_clusters, m.potential_sites, m.time, + m.well_clusters, + m.potential_sites, + m.time, doc="Flow of gas from a well cluster to a site [MMSCF/d]", - domain=NonNegativeReals, bounds=(0, 200), initialize=15) + domain=NonNegativeReals, + bounds=(0, 200), + initialize=15, + ) m.product_flows = Var( - m.potential_sites, m.markets, m.time, + m.potential_sites, + m.markets, + m.time, doc="Product shipments from a site to a market in a period [kBD]", - domain=NonNegativeReals, bounds=(0, 30), initialize=10) + domain=NonNegativeReals, + bounds=(0, 30), + initialize=10, + ) @m.Constraint(m.potential_sites, m.module_types, m.time) def consumption_capacity(m, site, mtype, t): @@ -605,7 +676,8 @@ def consumption_capacity(m, site, mtype, t): A constraint that limits the gas consumption per module type at each site, ensuring it does not exceed the capacity provided by the number of active modules of that type at the site during the time period. """ return m.gas_consumption[site, mtype, t] <= ( - m.num_modules[mtype, site, t] * m.unit_gas_consumption[mtype]) + m.num_modules[mtype, site, t] * m.unit_gas_consumption[mtype] + ) @m.Constraint(m.potential_sites, m.time) def production_limit(m, site, t): @@ -628,7 +700,8 @@ def production_limit(m, site, t): """ return m.production[site, t] <= sum( m.gas_consumption[site, mtype, t] * m.module_conversion[mtype] - for mtype in m.module_types) + for mtype in m.module_types + ) @m.Expression(m.potential_sites, m.time) def capacity(m, site, t): @@ -650,8 +723,11 @@ def capacity(m, site, t): An expression that sums up the potential production capacity at a site, calculated as the product of the number of modules, their individual gas consumption rates, and their conversion efficiency. """ return sum( - m.num_modules[mtype, site, t] * m.unit_gas_consumption[mtype] - * m.module_conversion[mtype] for mtype in m.module_types) + m.num_modules[mtype, site, t] + * m.unit_gas_consumption[mtype] + * m.module_conversion[mtype] + for mtype in m.module_types + ) @m.Constraint(m.potential_sites, m.time) def gas_supply_meets_consumption(m, site, t): @@ -693,8 +769,10 @@ def gas_supply_limit(m, well, t): Pyomo.Constraint A constraint that limits the total gas flow from a well cluster to various sites to not exceed the gas supply available at that well cluster for the given time period. """ - return sum(m.gas_flows[well, site, t] - for site in m.potential_sites) <= m.gas_supply[well, t] + return ( + sum(m.gas_flows[well, site, t] for site in m.potential_sites) + <= m.gas_supply[well, t] + ) @m.Constraint(m.potential_sites, m.time) def gasoline_production_requirement(m, site, t): @@ -715,8 +793,10 @@ def gasoline_production_requirement(m, site, t): Pyomo.Constraint A constraint that the sum of product flows (gasoline) from a site to various markets equals the total production at that site for the given period. """ - return sum(m.product_flows[site, mkt, t] - for mkt in m.markets) == m.production[site, t] + return ( + sum(m.product_flows[site, mkt, t] for mkt in m.markets) + == m.production[site, t] + ) @m.Constraint(m.potential_sites, m.module_types, m.time) def module_balance(m, site, mtype, t): @@ -740,12 +820,14 @@ def module_balance(m, site, mtype, t): A constraint that maintains an accurate balance of module counts at each site, considering new purchases, transfers in, existing inventory, and transfers out. """ if t >= m.module_setup_time: - modules_added = m.modules_purchased[ - mtype, site, t - m.module_setup_time] + modules_added = m.modules_purchased[mtype, site, t - m.module_setup_time] modules_transferred_in = sum( m.modules_transferred[ - mtype, from_site, to_site, t - m.module_setup_time] - for from_site, to_site in m.site_pairs if to_site == site) + mtype, from_site, to_site, t - m.module_setup_time + ] + for from_site, to_site in m.site_pairs + if to_site == site + ) else: modules_added = 0 modules_transferred_in = 0 @@ -755,11 +837,16 @@ def module_balance(m, site, mtype, t): existing_modules = 0 modules_transferred_out = sum( m.modules_transferred[mtype, from_site, to_site, t] - for from_site, to_site in m.site_pairs if from_site == site) + for from_site, to_site in m.site_pairs + if from_site == site + ) return m.num_modules[mtype, site, t] == ( - existing_modules + modules_added - + modules_transferred_in - modules_transferred_out) + existing_modules + + modules_added + + modules_transferred_in + - modules_transferred_out + ) @m.Disjunct(m.potential_sites) def site_active(disj, site): @@ -787,27 +874,33 @@ def site_inactive(disj, site): site : str The index for the potential site. """ - disj.no_production = Constraint( - expr=sum(m.production[site, :]) == 0) + disj.no_production = Constraint(expr=sum(m.production[site, :]) == 0) disj.no_gas_consumption = Constraint( - expr=sum(m.gas_consumption[site, :, :]) == 0) - disj.no_gas_flows = Constraint( - expr=sum(m.gas_flows[:, site, :]) == 0) - disj.no_product_flows = Constraint( - expr=sum(m.product_flows[site, :, :]) == 0) - disj.no_modules = Constraint( - expr=sum(m.num_modules[:, site, :]) == 0) + expr=sum(m.gas_consumption[site, :, :]) == 0 + ) + disj.no_gas_flows = Constraint(expr=sum(m.gas_flows[:, site, :]) == 0) + disj.no_product_flows = Constraint(expr=sum(m.product_flows[site, :, :]) == 0) + disj.no_modules = Constraint(expr=sum(m.num_modules[:, site, :]) == 0) disj.no_modules_transferred = Constraint( expr=sum( m.modules_transferred[mtypes, from_site, to_site, t] for mtypes in m.module_types for from_site, to_site in m.site_pairs for t in m.time - if from_site == site or to_site == site) == 0, doc="No modules transferred") + if from_site == site or to_site == site + ) + == 0, + doc="No modules transferred", + ) disj.no_modules_purchased = Constraint( expr=sum( m.modules_purchased[mtype, site, t] - for mtype in m.module_types for t in m.time) == 0, doc="No modules purchased") + for mtype in m.module_types + for t in m.time + ) + == 0, + doc="No modules purchased", + ) @m.Disjunction(m.potential_sites) def site_active_or_not(m, site): @@ -859,7 +952,9 @@ def pipeline_absent(disj, well, site): The index for the potential site. """ disj.no_natural_gas_flow = Constraint( - expr=sum(m.gas_flows[well, site, t] for t in m.time) == 0, doc="No natural gas flow") + expr=sum(m.gas_flows[well, site, t] for t in m.time) == 0, + doc="No natural gas flow", + ) @m.Disjunction(m.well_clusters, m.potential_sites) def pipeline_existence(m, well, site): @@ -903,11 +998,13 @@ def product_revenue(m, site): return sum( m.product_flows[site, mkt, t] # kBD * 1000 # bbl/kB - / 1E6 # $ to MM$ + / 1e6 # $ to MM$ * m.days_per_period - * m.gasoline_price[t] * m.gal_per_bbl + * m.gasoline_price[t] + * m.gal_per_bbl for mkt in m.markets - for t in m.time) + for t in m.time + ) @m.Expression(m.potential_sites, doc="MM$") def raw_material_cost(m, site): @@ -927,15 +1024,20 @@ def raw_material_cost(m, site): An expression calculating the total cost of natural gas used, taking into account the gas price and the conversion factor from MSCF to MMSCF. """ return sum( - m.gas_consumption[site, mtype, t] * m.days_per_period - / 1E6 # $ to MM$ + m.gas_consumption[site, mtype, t] + * m.days_per_period + / 1e6 # $ to MM$ * m.nat_gas_price[t] * 1000 # MMSCF to MSCF - for mtype in m.module_types for t in m.time) + for mtype in m.module_types + for t in m.time + ) @m.Expression( - m.potential_sites, m.markets, - doc="Aggregate cost to transport gasoline from a site to market [MM$]") + m.potential_sites, + m.markets, + doc="Aggregate cost to transport gasoline from a site to market [MM$]", + ) def product_transport_cost(m, site, mkt): """ Computes the cost of transporting gasoline from each production site to different markets, expressed in million dollars. @@ -955,11 +1057,15 @@ def product_transport_cost(m, site, mkt): The total transportation cost for shipping gasoline from a site to a market, adjusted for the distance and transportation rate. """ return sum( - m.product_flows[site, mkt, t] * m.gal_per_bbl + m.product_flows[site, mkt, t] + * m.gal_per_bbl * 1000 # bbl/kB - / 1E6 # $ to MM$ - * m.distance[site, mkt] / 100 * m.gasoline_tranport_cost[t] - for t in m.time) + / 1e6 # $ to MM$ + * m.distance[site, mkt] + / 100 + * m.gasoline_transport_cost[t] + for t in m.time + ) @m.Expression(m.well_clusters, m.potential_sites, doc="MM$") def pipeline_construction_cost(m, well, site): @@ -980,8 +1086,11 @@ def pipeline_construction_cost(m, well, site): Pyomo.Expression The cost of pipeline construction, in million dollars, if a pipeline is established between the well cluster and the site. """ - return (m.pipeline_unit_cost * m.distance[well, site] - * m.pipeline_exists[well, site].binary_indicator_var) + return ( + m.pipeline_unit_cost + * m.distance[well, site] + * m.pipeline_exists[well, site].binary_indicator_var + ) # Module transport cost @m.Expression(m.site_pairs, doc="MM$") @@ -1005,13 +1114,15 @@ def module_relocation_cost(m, from_site, to_site): """ return sum( m.modules_transferred[mtype, from_site, to_site, t] - * m.distance[from_site, to_site] / 100 + * m.distance[from_site, to_site] + / 100 * m.module_transport_distance_cost[t] - / 1E3 # M$ to MM$ + / 1e3 # M$ to MM$ + m.modules_transferred[mtype, from_site, to_site, t] * m.module_transport_unit_cost[t] for mtype in m.module_types - for t in m.time) + for t in m.time + ) @m.Expression(m.potential_sites, doc="MM$") def module_purchase_cost(m, site): @@ -1033,7 +1144,8 @@ def module_purchase_cost(m, site): return sum( m.module_unit_cost[mtype, t] * m.modules_purchased[mtype, site, t] for mtype in m.module_types - for t in m.time) + for t in m.time + ) @m.Expression(doc="MM$") def profit(m): @@ -1059,7 +1171,9 @@ def profit(m): - summation(m.module_purchase_cost) ) - m.neg_profit = Objective(expr=-m.profit, doc="Objective Function: Minimize Negative Profit") + m.neg_profit = Objective( + expr=-m.profit, doc="Objective Function: Minimize Negative Profit" + ) # Tightening constraints @m.Constraint(doc="Limit total module purchases over project span.") diff --git a/gdplib/syngas/syngas_adapted.py b/gdplib/syngas/syngas_adapted.py index e656ca5..634e1c8 100644 --- a/gdplib/syngas/syngas_adapted.py +++ b/gdplib/syngas/syngas_adapted.py @@ -3,7 +3,9 @@ import pandas as pd from pyomo.core.expr.logical_expr import * -from pyomo.core.plugins.transform.logical_to_linear import update_boolean_vars_from_binary +from pyomo.core.plugins.transform.logical_to_linear import ( + update_boolean_vars_from_binary, +) from pyomo.environ import * from pyomo.environ import TerminationCondition as tc import os @@ -16,39 +18,47 @@ def build_model(): m.syngas_techs = Set( doc="syngas process technologies", - initialize=['SMR', 'POX', 'ATR', 'CR', 'DMR', 'BR', 'TR'] + initialize=['SMR', 'POX', 'ATR', 'CR', 'DMR', 'BR', 'TR'], ) m.species = Set( - doc="chemical species", - initialize=['CH4', 'H2O', 'O2', 'H2', 'CO', 'CO2'] + doc="chemical species", initialize=['CH4', 'H2O', 'O2', 'H2', 'CO', 'CO2'] ) m.syngas_tech_units = Set( doc="process units involved in syngas process technologies", - initialize=['compressor', 'exchanger', 'reformer'] + initialize=['compressor', 'exchanger', 'reformer'], ) m.utilities = Set( - doc="process utilities", - initialize=['power', 'coolingwater', 'naturalgas'] + doc="process utilities", initialize=['power', 'coolingwater', 'naturalgas'] ) m.aux_equipment = Set( doc="auxiliary process equipment to condition syngas", - initialize=['absorber1', 'bypass1', 'WGS', 'flash', 'PSA', 'absorber2', 'bypass3', 'compressor', 'bypass4'] + initialize=[ + 'absorber1', + 'bypass1', + 'WGS', + 'flash', + 'PSA', + 'absorber2', + 'bypass3', + 'compressor', + 'bypass4', + ], ) m.process_options = Set( doc="process superstructure options", - initialize=m.syngas_techs | m.aux_equipment + initialize=m.syngas_techs | m.aux_equipment, ) m.extra_process_nodes = Set( doc="extra process nodes for mixers and splitters", - initialize=['in', 'ms1', 'm1', 's1', 'ms2', 'ms3', 'm2', 'ms4', 's2'] + initialize=['in', 'ms1', 'm1', 's1', 'ms2', 'ms3', 'm2', 'ms4', 's2'], ) m.all_unit_types = Set( doc="all process unit types", - initialize=m.process_options | m.syngas_tech_units | m.extra_process_nodes + initialize=m.process_options | m.syngas_tech_units | m.extra_process_nodes, ) m.superstructure_nodes = Set( doc="nodes in the superstructure", - initialize=m.process_options | m.extra_process_nodes + initialize=m.process_options | m.extra_process_nodes, ) group1 = {'absorber1', 'bypass1', 'WGS'} group2 = {'compressor', 'bypass3'} @@ -64,7 +74,7 @@ def build_model(): + [(option, 'ms3') for option in group2] + [('ms3', option) for option in group3] + [(option, 'm2') for option in group3] - + [('PSA', 'ms4'), ('ms4', 'ms3'), ('ms4', 's2'), ('s2', 'm1'), ('s2', 'ms1')] + + [('PSA', 'ms4'), ('ms4', 'ms3'), ('ms4', 's2'), ('s2', 'm1'), ('s2', 'ms1')], ) """ @@ -72,12 +82,21 @@ def build_model(): """ m.flow_limit = Param(initialize=5) - m.min_flow_division = Param(initialize=0.1, doc="minimum flow fraction into active unit") + m.min_flow_division = Param( + initialize=0.1, doc="minimum flow fraction into active unit" + ) m.raw_material_cost = Param( m.species, doc="raw material cost [dollar·kmol-1]", - initialize={'CH4': 2.6826, 'H2O': 0.18, 'O2': 0.7432, 'H2': 8, 'CO': 0, 'CO2': 1.8946} + initialize={ + 'CH4': 2.6826, + 'H2O': 0.18, + 'O2': 0.7432, + 'H2': 8, + 'CO': 0, + 'CO2': 1.8946, + }, ) feed_ratios = { @@ -95,137 +114,280 @@ def build_model(): ('TR', 'O2'): 0.47, } - data_dict = pd.read_csv('%s/syngas_conversion_data.txt' % syngas_dir, delimiter=r'\s+').fillna(0).stack().to_dict() + data_dict = ( + pd.read_csv('%s/syngas_conversion_data.txt' % syngas_dir, delimiter=r'\s+') + .fillna(0) + .stack() + .to_dict() + ) m.syngas_conversion_factor = Param(m.species, m.syngas_techs, initialize=data_dict) - m.co2_ratio = Param(doc="CO2-CO ratio of total carbon in final syngas", initialize=0.05) + m.co2_ratio = Param( + doc="CO2-CO ratio of total carbon in final syngas", initialize=0.05 + ) # Capital costs --> cap = (p1*x + p2)*(B1 + B2*Fmf*Fp) m.p1 = Param( m.all_unit_types, doc="capital cost variable parameter", initialize={ - 'compressor': 172.4, 'exchanger': 59.99, 'reformer': 67.64, 'absorber1': 314.1, 'bypass1': 0, - 'WGS': 314.1, 'flash': 314.1, 'PSA': 3.1863e+04, 'absorber2': 314.1, 'bypass3': 0}, - default=0 + 'compressor': 172.4, + 'exchanger': 59.99, + 'reformer': 67.64, + 'absorber1': 314.1, + 'bypass1': 0, + 'WGS': 314.1, + 'flash': 314.1, + 'PSA': 3.1863e04, + 'absorber2': 314.1, + 'bypass3': 0, + }, + default=0, ) m.p2 = Param( m.all_unit_types, doc="capital cost fixed parameter", initialize={ - 'compressor': 104300, 'exchanger': 187100, 'reformer': 480100, 'absorber1': 1.531e+04, 'bypass1': 0, - 'WGS': 1.531e+04, 'flash': 1.531e+04, 'PSA': 666.34e+03, 'absorber2': 1.531e+04, 'bypass3': 0}, - default=0 + 'compressor': 104300, + 'exchanger': 187100, + 'reformer': 480100, + 'absorber1': 1.531e04, + 'bypass1': 0, + 'WGS': 1.531e04, + 'flash': 1.531e04, + 'PSA': 666.34e03, + 'absorber2': 1.531e04, + 'bypass3': 0, + }, + default=0, ) m.material_factor = Param( m.all_unit_types, doc="capital cost material factor", initialize={ - 'compressor': 3.5, 'exchanger': 1.7, 'reformer': 4, 'absorber1': 1, 'bypass1': 0, - 'WGS': 1, 'flash': 1, 'PSA': 3.5, 'absorber2': 1, 'bypass3': 0}, - default=0 + 'compressor': 3.5, + 'exchanger': 1.7, + 'reformer': 4, + 'absorber1': 1, + 'bypass1': 0, + 'WGS': 1, + 'flash': 1, + 'PSA': 3.5, + 'absorber2': 1, + 'bypass3': 0, + }, + default=0, ) m.B1 = Param( m.all_unit_types, doc="bare module parameter 1", initialize={ - 'compressor': 0, 'exchanger': 1.63, 'reformer': 0, 'absorber1': 2.25, 'bypass1': 0, - 'WGS': 1.49, 'flash': 2.25, 'PSA': 0, 'absorber2': 2.25, 'bypass3': 0}, - default=0 + 'compressor': 0, + 'exchanger': 1.63, + 'reformer': 0, + 'absorber1': 2.25, + 'bypass1': 0, + 'WGS': 1.49, + 'flash': 2.25, + 'PSA': 0, + 'absorber2': 2.25, + 'bypass3': 0, + }, + default=0, ) m.B2 = Param( m.all_unit_types, doc="bare module parameter 2", initialize={ - 'compressor': 1, 'exchanger': 1.66, 'reformer': 1, 'absorber1': 1.82, 'bypass1': 0, - 'WGS': 1.52, 'flash': 1.82, 'PSA': 1, 'absorber2': 1.82, 'bypass3': 0}, - default=0 + 'compressor': 1, + 'exchanger': 1.66, + 'reformer': 1, + 'absorber1': 1.82, + 'bypass1': 0, + 'WGS': 1.52, + 'flash': 1.82, + 'PSA': 1, + 'absorber2': 1.82, + 'bypass3': 0, + }, + default=0, + ) + data_dict = ( + pd.read_csv('%s/syngas_pressure_factor_data.txt' % syngas_dir, delimiter=r'\s+') + .stack() + .to_dict() + ) + m.syngas_pressure_factor = Param( + m.syngas_tech_units, m.syngas_techs, initialize=data_dict ) - data_dict = pd.read_csv('%s/syngas_pressure_factor_data.txt' % syngas_dir, delimiter=r'\s+').stack().to_dict() - m.syngas_pressure_factor = Param(m.syngas_tech_units, m.syngas_techs, initialize=data_dict) m.utility_cost = Param( m.utilities, doc="dollars per kWh of utility u [dollar·KWh-1]", - initialize={'power': 0.127, 'coolingwater': 0.01, 'naturalgas': 0.036} + initialize={'power': 0.127, 'coolingwater': 0.01, 'naturalgas': 0.036}, ) m.utility_emission = Param( m.utilities, doc="CO2 emitted per kW [kgCO2·kWh-1]", - initialize={'power': 0.44732, 'coolingwater': 0, 'naturalgas': 0.2674} + initialize={'power': 0.44732, 'coolingwater': 0, 'naturalgas': 0.2674}, ) m.raw_material_emission = Param( m.species, doc="kg CO2 emitted per kmol of raw material [kgCO2·kmol-1]", - initialize={'CH4': 11.7749, 'H2O': 0, 'O2': 10.9525, 'H2': 0, 'CO': 0, 'CO2': 0} + initialize={ + 'CH4': 11.7749, + 'H2O': 0, + 'O2': 10.9525, + 'H2': 0, + 'CO': 0, + 'CO2': 0, + }, ) m.interest_rate = Param(initialize=0.1, doc="annualization index") m.project_years = Param(initialize=8, doc="annualization years") m.annualization_factor = Expression( - expr=m.interest_rate * (1 + m.interest_rate)**m.project_years / ((1 + m.interest_rate)**m.project_years - 1)) + expr=m.interest_rate + * (1 + m.interest_rate) ** m.project_years + / ((1 + m.interest_rate) ** m.project_years - 1) + ) m.CEPCI2015 = Param(initialize=560, doc="equipment cost index 2015") m.CEPCI2001 = Param(initialize=397, doc="equipment cost index 2001") - m.cost_index_ratio = Param(initialize=m.CEPCI2015 / m.CEPCI2001, doc="cost index ratio") + m.cost_index_ratio = Param( + initialize=m.CEPCI2015 / m.CEPCI2001, doc="cost index ratio" + ) - data_dict = pd.read_csv('%s/syngas_utility_data.txt' % syngas_dir, delimiter=r'\s+').stack().to_dict() + data_dict = ( + pd.read_csv('%s/syngas_utility_data.txt' % syngas_dir, delimiter=r'\s+') + .stack() + .to_dict() + ) m.syngas_tech_utility_rate = Param( - m.utilities, m.syngas_techs, initialize=data_dict, - doc="kWh of utility u per kmol of methane fed in syngas process i [kWh·kmol methane -1]") + m.utilities, + m.syngas_techs, + initialize=data_dict, + doc="kWh of utility u per kmol of methane fed in syngas process i [kWh·kmol methane -1]", + ) - data_dict = pd.read_csv('%s/syngas_num_units_data.txt' % syngas_dir, delimiter=r'\s+').stack().to_dict() + data_dict = ( + pd.read_csv('%s/syngas_num_units_data.txt' % syngas_dir, delimiter=r'\s+') + .stack() + .to_dict() + ) m.syngas_tech_num_units = Param( - m.syngas_tech_units, m.syngas_techs, initialize=data_dict, - doc="number of units h in syngas process i") + m.syngas_tech_units, + m.syngas_techs, + initialize=data_dict, + doc="number of units h in syngas process i", + ) m.syngas_tech_exchanger_area = Param( m.syngas_techs, doc="total exchanger area in process i per kmol·h-1 methane fed [m2·h·kmol methane -1]", - initialize={'SMR': 0.885917, 'POX': 0.153036, 'ATR': 0.260322, 'CR': 0.726294, - 'DMR': 0.116814, 'BR': 0.825808, 'TR': 0.10539}) + initialize={ + 'SMR': 0.885917, + 'POX': 0.153036, + 'ATR': 0.260322, + 'CR': 0.726294, + 'DMR': 0.116814, + 'BR': 0.825808, + 'TR': 0.10539, + }, + ) m.syngas_tech_reformer_duty = Param( m.syngas_techs, doc="reformer duties per kmol methane fed [kWh·kmol methane-1]", - initialize={'SMR': 54.654, 'POX': 39.0104, 'ATR': 44.4600, 'CR': 68.2382, - 'DMR': 68.412, 'BR': 61.442, 'TR': 6.592}) + initialize={ + 'SMR': 54.654, + 'POX': 39.0104, + 'ATR': 44.4600, + 'CR': 68.2382, + 'DMR': 68.412, + 'BR': 61.442, + 'TR': 6.592, + }, + ) m.process_tech_pressure = Param( m.syngas_techs, doc="process i operating pressure [bar]", - initialize={'SMR': 20, 'POX': 30, 'ATR': 25, 'CR': 25, 'DMR': 1, 'BR': 7, 'TR': 20} + initialize={ + 'SMR': 20, + 'POX': 30, + 'ATR': 25, + 'CR': 25, + 'DMR': 1, + 'BR': 7, + 'TR': 20, + }, ) m.final_syngas_pressure = Param(doc="final syngas pressure [bar]", initialize=1) - m.psa_hydrogen_recovery = Param(initialize=0.9, doc="percentage of hydrogen separated from the inlet syngas stream") + m.psa_hydrogen_recovery = Param( + initialize=0.9, + doc="percentage of hydrogen separated from the inlet syngas stream", + ) m.psa_separation_hydrogen_purity = Param(initialize=0.999) - m.Keqw = Param(initialize=83.3429, doc="equilibrium constant for WGS reaction at 250ºC") + m.Keqw = Param( + initialize=83.3429, doc="equilibrium constant for WGS reaction at 250ºC" + ) - m.stoichiometric_number = Param(doc="stoichiometric number of product syngas", initialize=1) + m.stoichiometric_number = Param( + doc="stoichiometric number of product syngas", initialize=1 + ) m.max_impurity = Param(initialize=0.1, doc="maximum allowed of impurities") - m.max_syngas_techs = Param(initialize=1, doc="Number of syngas technologies that can be selected") + m.max_syngas_techs = Param( + initialize=1, doc="Number of syngas technologies that can be selected" + ) """ Variables """ m.flow = Var(m.streams, m.species, bounds=(0, m.flow_limit)) - m.wgs_steam = Var(doc="steam molar flow provided in the WGS reactor [kmol·s-1]", bounds=(0, None)) - m.oxygen_flow = Var(doc="O2 molar flow provided in the selective oxidation reactor [kmol·s-1]", bounds=(0, None)) - m.Fabs1 = Var(bounds=(0, None), doc="molar flow of CO2 absorbed in absorber1 [kmol·s-1]") - m.Fabs2 = Var(bounds=(0, None), doc="molar flow of CO2 absorbed in absorber2 [kmol·s-1]") + m.wgs_steam = Var( + doc="steam molar flow provided in the WGS reactor [kmol·s-1]", bounds=(0, None) + ) + m.oxygen_flow = Var( + doc="O2 molar flow provided in the selective oxidation reactor [kmol·s-1]", + bounds=(0, None), + ) + m.Fabs1 = Var( + bounds=(0, None), doc="molar flow of CO2 absorbed in absorber1 [kmol·s-1]" + ) + m.Fabs2 = Var( + bounds=(0, None), doc="molar flow of CO2 absorbed in absorber2 [kmol·s-1]" + ) m.flash_water = Var(bounds=(0, None), doc="water removed in flash [kmol·s-1]") - m.co2_inject = Var(bounds=(0, None), doc="molar flow of CO2 used to adjust syngas composition [kmol·s-1]") - m.psa_recovered = Var(m.species, bounds=(0, None), doc="pure hydrogen stream retained in the PSA [kmol·s-1]") - m.purge_flow = Var(m.species, bounds=(0, None), doc="purged molar flow from the PSA [kmol·s-1]") - m.final_syngas_flow = Var(m.species, bounds=(0, m.flow_limit), doc="final adjusted syngas molar flow [kmol·s-1]") + m.co2_inject = Var( + bounds=(0, None), + doc="molar flow of CO2 used to adjust syngas composition [kmol·s-1]", + ) + m.psa_recovered = Var( + m.species, + bounds=(0, None), + doc="pure hydrogen stream retained in the PSA [kmol·s-1]", + ) + m.purge_flow = Var( + m.species, bounds=(0, None), doc="purged molar flow from the PSA [kmol·s-1]" + ) + m.final_syngas_flow = Var( + m.species, + bounds=(0, m.flow_limit), + doc="final adjusted syngas molar flow [kmol·s-1]", + ) @m.Expression(m.superstructure_nodes, m.species) def flow_into(m, option, species): - return sum(m.flow[src, sink, species] for src, sink in m.streams if sink == option) + return sum( + m.flow[src, sink, species] for src, sink in m.streams if sink == option + ) @m.Expression(m.superstructure_nodes, m.species) def flow_out_from(m, option, species): - return sum(m.flow[src, sink, species] for src, sink in m.streams if src == option) + return sum( + m.flow[src, sink, species] for src, sink in m.streams if src == option + ) @m.Expression(m.superstructure_nodes) def total_flow_into(m, option): @@ -235,63 +397,115 @@ def total_flow_into(m, option): def total_flow_from(m, option): return sum(m.flow_out_from[option, species] for species in m.species) - m.base_tech_capital_cost = Var(m.syngas_techs, m.syngas_tech_units, bounds=(0, None)) + m.base_tech_capital_cost = Var( + m.syngas_techs, m.syngas_tech_units, bounds=(0, None) + ) m.base_tech_operating_cost = Var(m.syngas_techs, m.utilities, bounds=(0, None)) - m.raw_material_total_cost = Var(bounds=(0, None), doc="total cost of raw materials [$·s-1]") + m.raw_material_total_cost = Var( + bounds=(0, None), doc="total cost of raw materials [$·s-1]" + ) @m.Expression(m.species) def raw_material_flow(m, species): return sum(m.flow['in', tech, species] for tech in m.syngas_techs) - m.syngas_tech_cost = Var(bounds=(0, None), doc="total cost of sygas process [$·y-1]") - m.syngas_tech_emissions = Var(bounds=(0, None), doc="CO2 emission of syngas processes") + m.syngas_tech_cost = Var( + bounds=(0, None), doc="total cost of sygas process [$·y-1]" + ) + m.syngas_tech_emissions = Var( + bounds=(0, None), doc="CO2 emission of syngas processes" + ) @m.Expression(m.syngas_techs, m.syngas_tech_units) def module_factors(m, tech, equip): - return m.B1[equip] + m.B2[equip] * m.material_factor[equip] * m.syngas_pressure_factor[equip, tech] + return ( + m.B1[equip] + + m.B2[equip] + * m.material_factor[equip] + * m.syngas_pressure_factor[equip, tech] + ) @m.Expression(m.syngas_techs, m.syngas_tech_units) def variable_utilization(m, tech, equip): - variable_rate_term = {'compressor': m.syngas_tech_utility_rate['power', tech], - 'exchanger': m.syngas_tech_exchanger_area[tech], - 'reformer': m.syngas_tech_reformer_duty[tech]} + variable_rate_term = { + 'compressor': m.syngas_tech_utility_rate['power', tech], + 'exchanger': m.syngas_tech_exchanger_area[tech], + 'reformer': m.syngas_tech_reformer_duty[tech], + } return variable_rate_term[equip] * m.flow['in', tech, 'CH4'] * 3600 - m.aux_unit_capital_cost = Var(m.aux_equipment, bounds=(0, None), doc="auxiliary unit capital cost [$·h-1]") + m.aux_unit_capital_cost = Var( + m.aux_equipment, bounds=(0, None), doc="auxiliary unit capital cost [$·h-1]" + ) - m.first_stage_outlet_pressure = Var(doc="final pressure in the mixer before WGS and absorber", bounds=(0, None)) - m.syngas_tech_outlet_pressure = Var(m.syngas_techs, doc="final pressure after compression after syngas synthesis [bar]", bounds=(0, None)) - m.syngas_tech_compressor_power = Var(m.syngas_techs, doc="utility of compressor i after syngas synthesis", bounds=(0, None)) - m.syngas_tech_compressor_cost = Var(doc="capital cost of compressors after syngas synthesis", bounds=(0, None)) + m.first_stage_outlet_pressure = Var( + doc="final pressure in the mixer before WGS and absorber", bounds=(0, None) + ) + m.syngas_tech_outlet_pressure = Var( + m.syngas_techs, + doc="final pressure after compression after syngas synthesis [bar]", + bounds=(0, None), + ) + m.syngas_tech_compressor_power = Var( + m.syngas_techs, + doc="utility of compressor i after syngas synthesis", + bounds=(0, None), + ) + m.syngas_tech_compressor_cost = Var( + doc="capital cost of compressors after syngas synthesis", bounds=(0, None) + ) m.Xw = Var(bounds=(0, None), doc="Moles per second reacted in WGS reactor") - m.wgs_inlet_temperature = Var(bounds=(0, None), doc="WGS reactor temperature before adjustment [ºC]") - m.wgs_heater = Var(bounds=(0, None), doc="duty required to preheat the syngas molar flow entering the WGS reactor [kW]") + m.wgs_inlet_temperature = Var( + bounds=(0, None), doc="WGS reactor temperature before adjustment [ºC]" + ) + m.wgs_heater = Var( + bounds=(0, None), + doc="duty required to preheat the syngas molar flow entering the WGS reactor [kW]", + ) m.psa_power = Var(bounds=(0, None), doc="power consumed by the PSA [kWh]") - m.syngas_power = Var(bounds=(0, None), doc="power needed to compress syngas from Pinlet to 30.01 bar") + m.syngas_power = Var( + bounds=(0, None), doc="power needed to compress syngas from Pinlet to 30.01 bar" + ) - m.syngas_total_flow = Expression(expr=sum(m.final_syngas_flow[species] for species in {'H2', 'CO2', 'CO', 'CH4'})) + m.syngas_total_flow = Expression( + expr=sum(m.final_syngas_flow[species] for species in {'H2', 'CO2', 'CO', 'CH4'}) + ) @m.Expression(m.aux_equipment) def aux_module_factors(m, equip): return m.B1[equip] + m.B2[equip] * m.material_factor[equip] m.final_total_emissions = Expression( - expr=m.syngas_tech_emissions*3600 - + m.Fabs1*3600*44 + m.Fabs2*3600*44 - - m.co2_inject*3600*44 + m.oxygen_flow*m.raw_material_emission['O2']*3600 - + m.wgs_steam*m.raw_material_emission['H2O']*3600 - + m.purge_flow['CO2']*3600*44 - + (m.psa_power + m.syngas_power + sum(m.syngas_tech_compressor_power[tech] for tech in m.syngas_techs) - )*m.utility_emission['power'] - + m.wgs_heater*0.094316389565193) + expr=m.syngas_tech_emissions * 3600 + + m.Fabs1 * 3600 * 44 + + m.Fabs2 * 3600 * 44 + - m.co2_inject * 3600 * 44 + + m.oxygen_flow * m.raw_material_emission['O2'] * 3600 + + m.wgs_steam * m.raw_material_emission['H2O'] * 3600 + + m.purge_flow['CO2'] * 3600 * 44 + + ( + m.psa_power + + m.syngas_power + + sum(m.syngas_tech_compressor_power[tech] for tech in m.syngas_techs) + ) + * m.utility_emission['power'] + + m.wgs_heater * 0.094316389565193 + ) m.final_total_cost = Expression( - expr=m.syngas_tech_cost*3600 + m.syngas_tech_compressor_cost - + sum(m.aux_unit_capital_cost[option] for option in m.aux_equipment) * m.annualization_factor - + (m.psa_power + m.syngas_power + sum(m.syngas_tech_compressor_power[tech] for tech in m.syngas_techs) - )*m.utility_cost['power'] - + m.wgs_heater*0.064) + expr=m.syngas_tech_cost * 3600 + + m.syngas_tech_compressor_cost + + sum(m.aux_unit_capital_cost[option] for option in m.aux_equipment) + * m.annualization_factor + + ( + m.psa_power + + m.syngas_power + + sum(m.syngas_tech_compressor_power[tech] for tech in m.syngas_techs) + ) + * m.utility_cost['power'] + + m.wgs_heater * 0.064 + ) """ Constraints @@ -301,16 +515,29 @@ def aux_module_factors(m, equip): def syngas_process_feed_species_ratio(m, tech, species): if species == 'CH4': return Constraint.Skip - return m.flow['in', tech, species] == feed_ratios.get((tech, species), 0) * m.flow['in', tech, 'CH4'] + return ( + m.flow['in', tech, species] + == feed_ratios.get((tech, species), 0) * m.flow['in', tech, 'CH4'] + ) @m.Constraint(m.syngas_techs, m.species) def syngas_conversion_calc(m, tech, species): - return m.flow[tech, 'ms1', species] == m.flow['in', tech, 'CH4'] * m.syngas_conversion_factor[species, tech] + return ( + m.flow[tech, 'ms1', species] + == m.flow['in', tech, 'CH4'] * m.syngas_conversion_factor[species, tech] + ) - m.raw_material_cost_calc = Constraint(expr=m.raw_material_total_cost == ( - sum(m.raw_material_flow[species] * m.raw_material_cost[species] for species in m.species) - + m.wgs_steam * m.raw_material_cost['H2O'] + m.oxygen_flow * m.raw_material_cost['O2'] - )) + m.raw_material_cost_calc = Constraint( + expr=m.raw_material_total_cost + == ( + sum( + m.raw_material_flow[species] * m.raw_material_cost[species] + for species in m.species + ) + + m.wgs_steam * m.raw_material_cost['H2O'] + + m.oxygen_flow * m.raw_material_cost['O2'] + ) + ) @m.Disjunct(m.process_options) def unit_exists(disj, option): @@ -330,7 +557,9 @@ def no_flow_out(disj, species): def unit_exists_or_not(disj, option): return [m.unit_exists[option], m.unit_absent[option]] - m.Yunit = BooleanVar(m.process_options, doc="Boolean variable for existence of a process unit") + m.Yunit = BooleanVar( + m.process_options, doc="Boolean variable for existence of a process unit" + ) for option in m.process_options: m.Yunit[option].associate_binary_var(m.unit_exists[option].binary_indicator_var) @@ -341,44 +570,93 @@ def unit_exists_or_not(disj, option): @tech_selected.Constraint(m.syngas_tech_units) def base_tech_capital_cost_calc(disj, equip): return m.base_tech_capital_cost[tech, equip] == ( - (m.p1[equip] * m.variable_utilization[tech, equip] - + m.p2[equip] * m.syngas_tech_num_units[equip, tech] * m.module_factors[tech, equip]) - * m.cost_index_ratio / 8000 + ( + m.p1[equip] * m.variable_utilization[tech, equip] + + m.p2[equip] + * m.syngas_tech_num_units[equip, tech] + * m.module_factors[tech, equip] + ) + * m.cost_index_ratio + / 8000 ) @tech_selected.Constraint(m.utilities) def base_tech_operating_cost_calc(disj, util): return m.base_tech_operating_cost[tech, util] == ( - m.syngas_tech_utility_rate[util, tech] * m.flow['in', tech, 'CH4'] * 3600 * m.utility_cost[util] + m.syngas_tech_utility_rate[util, tech] + * m.flow['in', tech, 'CH4'] + * 3600 + * m.utility_cost[util] ) - m.syngas_process_cost_calc = Constraint(expr=m.syngas_tech_cost == ( - sum(sum(m.base_tech_capital_cost[tech, equip] for equip in m.syngas_tech_units) * m.annualization_factor - + sum(m.base_tech_operating_cost[tech, util] for util in m.utilities) - for tech in m.syngas_techs) + m.raw_material_total_cost * 3600) / 3600) + m.syngas_process_cost_calc = Constraint( + expr=m.syngas_tech_cost + == ( + sum( + sum( + m.base_tech_capital_cost[tech, equip] + for equip in m.syngas_tech_units + ) + * m.annualization_factor + + sum(m.base_tech_operating_cost[tech, util] for util in m.utilities) + for tech in m.syngas_techs + ) + + m.raw_material_total_cost * 3600 + ) + / 3600 + ) - m.syngas_emissions_calc = Constraint(expr=m.syngas_tech_emissions == ( - # Emissions from utilities - sum(m.base_tech_operating_cost[tech, util] / m.utility_cost[util] * m.utility_emission[util] - for tech in m.syngas_techs for util in m.utilities) - # CO2 consumed by syngas processes - - sum(m.flow['in', tech, 'CO2'] for tech in m.syngas_techs) * 3600 * 44 - # Emissions from raw materials - + sum(m.raw_material_flow[species] * m.raw_material_emission[species] * 3600 for species in m.species)) / 3600) + m.syngas_emissions_calc = Constraint( + expr=m.syngas_tech_emissions + == ( + # Emissions from utilities + sum( + m.base_tech_operating_cost[tech, util] + / m.utility_cost[util] + * m.utility_emission[util] + for tech in m.syngas_techs + for util in m.utilities + ) + # CO2 consumed by syngas processes + - sum(m.flow['in', tech, 'CO2'] for tech in m.syngas_techs) * 3600 * 44 + # Emissions from raw materials + + sum( + m.raw_material_flow[species] * m.raw_material_emission[species] * 3600 + for species in m.species + ) + ) + / 3600 + ) # Syngas process pressure adjustment @m.Disjunct(m.syngas_techs) def stage_one_compressor(disj, tech): - disj.compressor_power_calc = Constraint(expr=m.syngas_tech_compressor_power[tech] == ( - (1.5 / (1.5 - 1)) / 0.8 * (40 + 273) * 8.314 * sum(m.flow[tech, 'ms1', species] for species in m.species) - * ((m.syngas_tech_outlet_pressure[tech] / m.process_tech_pressure[tech]) ** (1.5 - 1 / 1.5) - 1) - )) + disj.compressor_power_calc = Constraint( + expr=m.syngas_tech_compressor_power[tech] + == ( + (1.5 / (1.5 - 1)) + / 0.8 + * (40 + 273) + * 8.314 + * sum(m.flow[tech, 'ms1', species] for species in m.species) + * ( + ( + m.syngas_tech_outlet_pressure[tech] + / m.process_tech_pressure[tech] + ) + ** (1.5 - 1 / 1.5) + - 1 + ) + ) + ) pass @m.Disjunct(m.syngas_techs) def stage_one_bypass(bypass, tech): - bypass.no_pressure_increase = Constraint(expr=m.syngas_tech_outlet_pressure[tech] == m.process_tech_pressure[tech]) + bypass.no_pressure_increase = Constraint( + expr=m.syngas_tech_outlet_pressure[tech] == m.process_tech_pressure[tech] + ) pass @m.Disjunction(m.syngas_techs) @@ -387,21 +665,33 @@ def stage_one_compressor_or_bypass(m, tech): m.Ycomp = BooleanVar(m.syngas_techs) for tech in m.syngas_techs: - m.Ycomp[tech].associate_binary_var(m.stage_one_compressor[tech].binary_indicator_var) + m.Ycomp[tech].associate_binary_var( + m.stage_one_compressor[tech].binary_indicator_var + ) @m.LogicalConstraint(m.syngas_techs) def compressor_implies_tech(m, tech): return m.Ycomp[tech].implies(m.Yunit[tech]) - m.syngas_tech_compressor_cost_calc = Constraint(expr=m.syngas_tech_compressor_cost == ( - ((3.553 * 10 ** 5) * sum(m.Ycomp[tech].get_associated_binary() for tech in m.syngas_techs) - + 586 * sum(m.syngas_tech_compressor_power[tech] for tech in m.syngas_techs)) - * m.annualization_factor / 8000 - )) + m.syngas_tech_compressor_cost_calc = Constraint( + expr=m.syngas_tech_compressor_cost + == ( + ( + (3.553 * 10**5) + * sum(m.Ycomp[tech].get_associated_binary() for tech in m.syngas_techs) + + 586 + * sum(m.syngas_tech_compressor_power[tech] for tech in m.syngas_techs) + ) + * m.annualization_factor + / 8000 + ) + ) for tech in m.syngas_techs: tech_selected = m.unit_exists[tech] - tech_selected.pressure_balance = Constraint(expr=m.first_stage_outlet_pressure <= m.syngas_tech_outlet_pressure[tech]) + tech_selected.pressure_balance = Constraint( + expr=m.first_stage_outlet_pressure <= m.syngas_tech_outlet_pressure[tech] + ) # ms1 balances @m.Constraint(m.species) @@ -414,36 +704,66 @@ def ms1_mass_balance(m, species): @unit_exists.Constraint(m.species) def unit_inlet_composition_balance(disj, species): total_flow = sum(m.flow_out_from['ms1', jj] for jj in m.species) - total_flow_to_this_unit = sum(m.flow_into[this_unit, jj] for jj in m.species) + total_flow_to_this_unit = sum( + m.flow_into[this_unit, jj] for jj in m.species + ) total_flow_species = m.flow_out_from['ms1', species] - return total_flow * m.flow['ms1', this_unit, species] == total_flow_to_this_unit * total_flow_species + return ( + total_flow * m.flow['ms1', this_unit, species] + == total_flow_to_this_unit * total_flow_species + ) # WGS Reactor CO + H2O <-> CO2 + H2 wgs_exists = m.unit_exists['WGS'] - wgs_exists.CH4_balance = Constraint(expr=m.flow_out_from['WGS', 'CH4'] == m.flow_into['WGS', 'CH4']) - wgs_exists.CO_balance = Constraint(expr=m.flow_out_from['WGS', 'CO'] == m.flow_into['WGS', 'CO'] - m.Xw) - wgs_exists.CO2_balance = Constraint(expr=m.flow_out_from['WGS', 'CO2'] == m.flow_into['WGS', 'CO2'] + m.Xw) - wgs_exists.H2_balance = Constraint(expr=m.flow_out_from['WGS', 'H2'] == m.flow_into['WGS', 'H2'] + m.Xw) - wgs_exists.H2O_balance = Constraint(expr=m.flow_out_from['WGS', 'H2O'] == m.flow_into['WGS', 'H2O'] + m.wgs_steam - m.Xw) + wgs_exists.CH4_balance = Constraint( + expr=m.flow_out_from['WGS', 'CH4'] == m.flow_into['WGS', 'CH4'] + ) + wgs_exists.CO_balance = Constraint( + expr=m.flow_out_from['WGS', 'CO'] == m.flow_into['WGS', 'CO'] - m.Xw + ) + wgs_exists.CO2_balance = Constraint( + expr=m.flow_out_from['WGS', 'CO2'] == m.flow_into['WGS', 'CO2'] + m.Xw + ) + wgs_exists.H2_balance = Constraint( + expr=m.flow_out_from['WGS', 'H2'] == m.flow_into['WGS', 'H2'] + m.Xw + ) + wgs_exists.H2O_balance = Constraint( + expr=m.flow_out_from['WGS', 'H2O'] + == m.flow_into['WGS', 'H2O'] + m.wgs_steam - m.Xw + ) wgs_exists.max_molar_reaction = Constraint(expr=m.Xw <= m.flow_into['WGS', 'CO']) wgs_exists.rxn_equilibrium = Constraint( - expr=m.Keqw * m.flow_out_from['WGS', 'CO'] * m.flow_out_from['WGS', 'H2O'] == ( - m.flow_out_from['WGS', 'CO2'] * m.flow_out_from['WGS', 'H2'] - )) + expr=m.Keqw * m.flow_out_from['WGS', 'CO'] * m.flow_out_from['WGS', 'H2O'] + == (m.flow_out_from['WGS', 'CO2'] * m.flow_out_from['WGS', 'H2']) + ) - wgs_exists.capital_cost = Constraint(expr=m.aux_unit_capital_cost['WGS'] == ( - (m.p1['WGS'] * 100 * m.flow_out_from['WGS', 'H2'] + m.p2['WGS']) - * m.aux_module_factors['WGS'] / 8000 * m.cost_index_ratio - )) + wgs_exists.capital_cost = Constraint( + expr=m.aux_unit_capital_cost['WGS'] + == ( + (m.p1['WGS'] * 100 * m.flow_out_from['WGS', 'H2'] + m.p2['WGS']) + * m.aux_module_factors['WGS'] + / 8000 + * m.cost_index_ratio + ) + ) wgs_exists.temperature_balance = Constraint( - expr=m.wgs_inlet_temperature * m.total_flow_into['WGS'] == ( - sum(m.flow[tech, 'ms1', species] for tech in m.syngas_techs for species in m.species) * 250 + expr=m.wgs_inlet_temperature * m.total_flow_into['WGS'] + == ( + sum( + m.flow[tech, 'ms1', species] + for tech in m.syngas_techs + for species in m.species + ) + * 250 + sum(m.flow['s2', 'ms1', species] for species in m.species) * 40 - )) + ) + ) wgs_exists.heater_duty = Constraint( - expr=m.wgs_heater == m.total_flow_into['WGS'] * 46 * (250 - m.wgs_inlet_temperature)) + expr=m.wgs_heater + == m.total_flow_into['WGS'] * 46 * (250 - m.wgs_inlet_temperature) + ) # Bypass 1 bypass1_exists = m.unit_exists['bypass1'] @@ -457,20 +777,31 @@ def bypass1_mass_balance(disj, species): @absorber_exists.Constraint(m.species) def absorber_mass_balance(disj, species): - return m.flow_out_from['absorber1', species] == m.flow_into['absorber1', species] - ( - m.Fabs1 if species == 'CO2' else 0) + return m.flow_out_from['absorber1', species] == m.flow_into[ + 'absorber1', species + ] - (m.Fabs1 if species == 'CO2' else 0) - absorber_exists.co2_absorption = Constraint(expr=m.Fabs1 == 0.96 * m.flow_into['absorber1', 'CO2']) - absorber_exists.cost = Constraint(expr=m.aux_unit_capital_cost['absorber1'] == ( - (m.p1['absorber1'] * 100 * m.Fabs1 + m.p2['absorber1']) - * m.aux_module_factors['absorber1'] / 8000 * m.cost_index_ratio - )) + absorber_exists.co2_absorption = Constraint( + expr=m.Fabs1 == 0.96 * m.flow_into['absorber1', 'CO2'] + ) + absorber_exists.cost = Constraint( + expr=m.aux_unit_capital_cost['absorber1'] + == ( + (m.p1['absorber1'] * 100 * m.Fabs1 + m.p2['absorber1']) + * m.aux_module_factors['absorber1'] + / 8000 + * m.cost_index_ratio + ) + ) m.unit_absent['absorber1'].no_absorption = Constraint(expr=m.Fabs1 == 0) for this_unit in group1: unit_exists = m.unit_exists[this_unit] - unit_exists.minimum_flow = Constraint(expr=m.total_flow_into[this_unit] >= m.total_flow_from['ms1'] * m.min_flow_division) + unit_exists.minimum_flow = Constraint( + expr=m.total_flow_into[this_unit] + >= m.total_flow_from['ms1'] * m.min_flow_division + ) # Flash @m.Constraint(m.species) @@ -481,18 +812,30 @@ def m1_mass_balance(m, species): @flash_exists.Constraint(m.species) def flash_mass_balance(disj, species): - return m.flow_out_from['flash', species] == (m.flow_into['flash', species] if not species == 'H2O' else 0) + return m.flow_out_from['flash', species] == ( + m.flow_into['flash', species] if not species == 'H2O' else 0 + ) - flash_exists.water_sep = Constraint(expr=m.flash_water == m.flow_into['flash', 'H2O']) + flash_exists.water_sep = Constraint( + expr=m.flash_water == m.flow_into['flash', 'H2O'] + ) @m.Constraint(m.species) def post_flash_split_outlet(m, species): - return m.flow_out_from['flash', species] == m.flow_into['PSA', species] + m.flow_into['ms2', species] + return ( + m.flow_out_from['flash', species] + == m.flow_into['PSA', species] + m.flow_into['ms2', species] + ) - flash_exists.cost = Constraint(expr=m.aux_unit_capital_cost['flash'] == ( - (m.p1['flash'] * 100 * m.flash_water + m.p2['flash']) - * m.aux_module_factors['flash'] / 8000 * m.cost_index_ratio - )) + flash_exists.cost = Constraint( + expr=m.aux_unit_capital_cost['flash'] + == ( + (m.p1['flash'] * 100 * m.flash_water + m.p2['flash']) + * m.aux_module_factors['flash'] + / 8000 + * m.cost_index_ratio + ) + ) # PSA psa_exists = m.unit_exists['PSA'] @@ -502,32 +845,60 @@ def psa_inlet_composition_balance(disj, species): total_flow = sum(m.flow_out_from['s1', jj] for jj in m.species) total_flow_to_this_unit = sum(m.flow_into['PSA', jj] for jj in m.species) total_flow_species = m.flow_out_from['s1', species] - return total_flow * m.flow['s1', 'PSA', species] == total_flow_to_this_unit * total_flow_species + return ( + total_flow * m.flow['s1', 'PSA', species] + == total_flow_to_this_unit * total_flow_species + ) @m.Constraint(m.species) def ms2_inlet_composition_balance(disj, species): total_flow = sum(m.flow_out_from['s1', jj] for jj in m.species) total_flow_to_this_unit = sum(m.flow_into['ms2', jj] for jj in m.species) total_flow_species = m.flow_out_from['s1', species] - return total_flow * m.flow['s1', 'ms2', species] == total_flow_to_this_unit * total_flow_species + return ( + total_flow * m.flow['s1', 'ms2', species] + == total_flow_to_this_unit * total_flow_species + ) @psa_exists.Constraint(m.species) def psa_mass_balance(disj, species): - return m.flow_out_from['PSA', species] + m.psa_recovered[species] == m.flow_into['PSA', species] + return ( + m.flow_out_from['PSA', species] + m.psa_recovered[species] + == m.flow_into['PSA', species] + ) @psa_exists.Constraint(m.species) def psa_recovery(disj, species): return m.flow_out_from['PSA', species] == m.flow_into['PSA', species] * ( - (1 - m.psa_hydrogen_recovery if species == 'H2' else m.psa_separation_hydrogen_purity) + ( + 1 - m.psa_hydrogen_recovery + if species == 'H2' + else m.psa_separation_hydrogen_purity + ) ) - psa_exists.cost = Constraint(expr=m.aux_unit_capital_cost['PSA'] == ( - (m.p1['PSA'] * m.flow_into['PSA', 'H2'] + m.p2['PSA']) - * m.aux_module_factors['PSA'] / 8000 - )) - psa_exists.psa_utility = Constraint(expr=m.psa_power*m.first_stage_outlet_pressure**(1.5-1/1.5) == ( - (1.5/(1.5-1))/0.8*(40+273) * 8.314 * m.total_flow_into['PSA'] - * ((30+1e-6)**(1.5-1/1.5) - m.first_stage_outlet_pressure**(1.5-1/1.5)))) + psa_exists.cost = Constraint( + expr=m.aux_unit_capital_cost['PSA'] + == ( + (m.p1['PSA'] * m.flow_into['PSA', 'H2'] + m.p2['PSA']) + * m.aux_module_factors['PSA'] + / 8000 + ) + ) + psa_exists.psa_utility = Constraint( + expr=m.psa_power * m.first_stage_outlet_pressure ** (1.5 - 1 / 1.5) + == ( + (1.5 / (1.5 - 1)) + / 0.8 + * (40 + 273) + * 8.314 + * m.total_flow_into['PSA'] + * ( + (30 + 1e-6) ** (1.5 - 1 / 1.5) + - m.first_stage_outlet_pressure ** (1.5 - 1 / 1.5) + ) + ) + ) psa_absent = m.unit_absent['PSA'] @@ -546,7 +917,10 @@ def ms4_inlet_mass_balance(m, species): @m.Constraint(m.species) def ms4_outlet_mass_balance(m, species): - return m.flow_out_from['ms4', species] + m.purge_flow[species] == m.flow_into['ms4', species] + return ( + m.flow_out_from['ms4', species] + m.purge_flow[species] + == m.flow_into['ms4', species] + ) @m.Constraint(m.species) def purge_flow_limit(m, species): @@ -557,14 +931,20 @@ def s2_inlet_composition(m, species): total_flow = sum(m.flow_into['ms4', jj] for jj in m.species) total_flow_to_this_unit = sum(m.flow_into['s2', jj] for jj in m.species) total_flow_species = m.flow_into['ms4', species] - return total_flow * m.flow['ms4', 's2', species] == total_flow_to_this_unit * total_flow_species + return ( + total_flow * m.flow['ms4', 's2', species] + == total_flow_to_this_unit * total_flow_species + ) @m.Constraint(m.species) def ms4_to_ms3_composition(m, species): total_flow = sum(m.flow_into['ms4', jj] for jj in m.species) total_flow_to_this_unit = sum(m.flow['ms4', 's2', jj] for jj in m.species) total_flow_species = m.flow_into['ms4', species] - return total_flow * m.flow['ms4', 's2', species] == total_flow_to_this_unit * total_flow_species + return ( + total_flow * m.flow['ms4', 's2', species] + == total_flow_to_this_unit * total_flow_species + ) # s2 @m.Constraint(m.species) @@ -579,7 +959,8 @@ def no_flow_s2_to_m1(m, species): @m.Constraint(m.species) def ms2_mass_balance(m, species): return m.flow_out_from['ms2', species] == ( - m.flow_into['ms2', species] + (m.co2_inject if species == 'CO2' else 0)) + m.flow_into['ms2', species] + (m.co2_inject if species == 'CO2' else 0) + ) # bypass3 bypass3_exists = m.unit_exists['bypass3'] @@ -597,27 +978,47 @@ def compressor_inlet_mass_balance(disj, species): @compressor_exists.Constraint(m.species) def compressor_mass_balance(disj, species): - return m.flow_out_from['compressor', species] == m.flow_into['compressor', species] + return ( + m.flow_out_from['compressor', species] == m.flow_into['compressor', species] + ) - compressor_exists.cost = Constraint(expr=m.aux_unit_capital_cost['compressor'] == ( - ((3.553 * 10 ** 5) + 586 * m.syngas_power) / 8000 * m.cost_index_ratio - )) + compressor_exists.cost = Constraint( + expr=m.aux_unit_capital_cost['compressor'] + == (((3.553 * 10**5) + 586 * m.syngas_power) / 8000 * m.cost_index_ratio) + ) - compressor_exists.work = Constraint(expr=m.syngas_power * m.first_stage_outlet_pressure**(1.5-1/1.5) == ( - (1.5/(1.5-1))/0.8*(40+273)*8.314*m.total_flow_into['compressor'] - * (m.final_syngas_pressure**(1.5-1/1.5) - m.first_stage_outlet_pressure**(1.5-1/1.5)))) + compressor_exists.work = Constraint( + expr=m.syngas_power * m.first_stage_outlet_pressure ** (1.5 - 1 / 1.5) + == ( + (1.5 / (1.5 - 1)) + / 0.8 + * (40 + 273) + * 8.314 + * m.total_flow_into['compressor'] + * ( + m.final_syngas_pressure ** (1.5 - 1 / 1.5) + - m.first_stage_outlet_pressure ** (1.5 - 1 / 1.5) + ) + ) + ) no_compressor = m.unit_absent['compressor'] - no_compressor.final_pressure = Constraint(expr=m.first_stage_outlet_pressure >= m.final_syngas_pressure) + no_compressor.final_pressure = Constraint( + expr=m.first_stage_outlet_pressure >= m.final_syngas_pressure + ) - compressor_exists.compressor_minimum_flow = Constraint(expr=m.total_flow_into['compressor'] >= ( - m.total_flow_from['flash'] * m.min_flow_division - )) - psa_exists.psa_minimum_flow = Constraint(expr=m.total_flow_into['PSA'] >= ( - m.total_flow_from['flash'] * m.min_flow_division - )) + compressor_exists.compressor_minimum_flow = Constraint( + expr=m.total_flow_into['compressor'] + >= (m.total_flow_from['flash'] * m.min_flow_division) + ) + psa_exists.psa_minimum_flow = Constraint( + expr=m.total_flow_into['PSA'] + >= (m.total_flow_from['flash'] * m.min_flow_division) + ) - m.compressor_or_bypass = LogicalConstraint(expr=exactly(1, m.Yunit['bypass3'], m.Yunit['compressor'])) + m.compressor_or_bypass = LogicalConstraint( + expr=exactly(1, m.Yunit['bypass3'], m.Yunit['compressor']) + ) # ms3 @m.Constraint(m.species) @@ -633,37 +1034,53 @@ def bypass4_mass_balance(disj, species): # absorber 2 absorber2_exists = m.unit_exists['absorber2'] - + @absorber2_exists.Constraint(m.species) def absorber2_mass_balance(disj, species): return m.flow_out_from['absorber2', species] == ( - m.flow_into['absorber2', species] - (m.Fabs2 if species == 'CO2' else 0)) - - absorber2_exists.co2_absorption = Constraint(expr=m.Fabs2 == 0.96 * m.flow_into['absorber2', 'CO2']) - absorber2_exists.cost = Constraint(expr=m.aux_unit_capital_cost['absorber2'] == ( - (m.p1['absorber2'] * 100 * m.Fabs2 + m.p2['absorber2']) - * m.aux_module_factors['absorber2'] / 8000 * m.cost_index_ratio - )) + m.flow_into['absorber2', species] - (m.Fabs2 if species == 'CO2' else 0) + ) + + absorber2_exists.co2_absorption = Constraint( + expr=m.Fabs2 == 0.96 * m.flow_into['absorber2', 'CO2'] + ) + absorber2_exists.cost = Constraint( + expr=m.aux_unit_capital_cost['absorber2'] + == ( + (m.p1['absorber2'] * 100 * m.Fabs2 + m.p2['absorber2']) + * m.aux_module_factors['absorber2'] + / 8000 + * m.cost_index_ratio + ) + ) m.unit_absent['absorber2'].no_absorption = Constraint(expr=m.Fabs2 == 0) - - m.only_one_absorber = LogicalConstraint(expr=atmost(1, m.Yunit['absorber1'], m.Yunit['absorber2'])) + + m.only_one_absorber = LogicalConstraint( + expr=atmost(1, m.Yunit['absorber1'], m.Yunit['absorber2']) + ) @m.Constraint(m.species) def final_mass_balance(m, species): return m.final_syngas_flow[species] == m.flow_into['m2', species] m.syngas_stoich_number = Constraint( - expr=m.stoichiometric_number * (m.final_syngas_flow['CO'] + m.final_syngas_flow['CO2']) == ( - m.final_syngas_flow['H2'] - m.final_syngas_flow['CO2'] - )) + expr=m.stoichiometric_number + * (m.final_syngas_flow['CO'] + m.final_syngas_flow['CO2']) + == (m.final_syngas_flow['H2'] - m.final_syngas_flow['CO2']) + ) m.impurity_limit = Constraint( - expr=m.max_impurity * sum(m.final_syngas_flow[species] for species in {'CO', 'H2', 'CH4', 'CO2', 'H2O'}) + expr=m.max_impurity + * sum( + m.final_syngas_flow[species] + for species in {'CO', 'H2', 'CH4', 'CO2', 'H2O'} + ) >= sum(m.final_syngas_flow[species] for species in {'CH4', 'H2O'}) ) m.syngas_process_limits = LogicalConstraint( - expr=atmost(m.max_syngas_techs, [m.Yunit[tech] for tech in m.syngas_techs])) + expr=atmost(m.max_syngas_techs, [m.Yunit[tech] for tech in m.syngas_techs]) + ) # Bounds m.wgs_heater.setub(10000) @@ -717,7 +1134,7 @@ def final_mass_balance(m, species): m.syngas_maximum_demand = Constraint(expr=m.syngas_total_flow <= 5) m.final_syngas_flow['CO'].fix(0.3) - m.final_syngas_flow['CO2'].fix(0.3*m.co2_ratio) + m.final_syngas_flow['CO2'].fix(0.3 * m.co2_ratio) m.obj = Objective(expr=m.final_total_cost) @@ -742,15 +1159,26 @@ def final_mass_balance(m, species): def display_nonzeros(var): if var.is_indexed(): + def nonzero_rows(): for k, v in var.items(): if v.value == 0: continue yield k, [value(v.lb), v.value, value(v.ub), v.fixed, v.stale, v.domain] + _attr, _, _header, _ = var._pprint() var._pprint_base_impl( - None, False, "", var.local_name, var.doc, - var.is_constructed(), _attr, nonzero_rows(), _header, lambda k, v: v) + None, + False, + "", + var.local_name, + var.doc, + var.is_constructed(), + _attr, + nonzero_rows(), + _header, + lambda k, v: v, + ) else: var.display() @@ -765,9 +1193,14 @@ def nonzero_rows(): # # solver="cplex", add_options=['GAMS_MODEL.optfile=1;', '$onecho > cplex.opt', 'iis=1', '$offecho'], # ) result = SolverFactory('gdpopt').solve( - m, strategy='LOA', tee=True, mip_solver='gams', - nlp_solver='gams', nlp_solver_args=dict(solver='scip', add_options=['option optcr=0;']), - minlp_solver='gams', minlp_solver_args=dict(solver='baron', add_options=['option optcr=0;']) + m, + strategy='LOA', + tee=True, + mip_solver='gams', + nlp_solver='gams', + nlp_solver_args=dict(solver='scip', add_options=['option optcr=0;']), + minlp_solver='gams', + minlp_solver_args=dict(solver='baron', add_options=['option optcr=0;']), ) if not result.solver.termination_condition == tc.optimal: print("Termination Condition: ", result.solver.termination_condition) diff --git a/setup.py b/setup.py index a338303..bd40d61 100644 --- a/setup.py +++ b/setup.py @@ -33,7 +33,7 @@ "Intended Audience :: Developers", "License :: OSI Approved :: BSD License", "Operating System :: OS Independent", - "Topic :: Software Development :: Libraries :: Python Modules" + "Topic :: Software Development :: Libraries :: Python Modules", ], ) @@ -41,7 +41,9 @@ setup(setup_requires=['setuptools_scm'], use_scm_version=True, **kwargs) except (ImportError, LookupError): default_version = '1.0.0' - warning('Cannot use .git version: package setuptools_scm not installed ' - 'or .git directory not present.') + warning( + 'Cannot use .git version: package setuptools_scm not installed ' + 'or .git directory not present.' + ) print('Defaulting to version: {}'.format(default_version)) setup(**kwargs)