From 9ef74b09852c92fb653a5c36914bf6c56f5f1134 Mon Sep 17 00:00:00 2001 From: ZedongPeng Date: Thu, 14 Sep 2023 13:52:47 -0400 Subject: [PATCH 01/99] add citation --- CITATION.ccf | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 CITATION.ccf diff --git a/CITATION.ccf b/CITATION.ccf new file mode 100644 index 0000000..a529319 --- /dev/null +++ b/CITATION.ccf @@ -0,0 +1,16 @@ +cff-version: 1.0.0 +message: "If you use this software, please cite it as below." +authors: + - family-names: Bernal + given-names: David E. + orcid: https://orcid.org/0000-0002-8308-5016 + - family-names: Chen + given-names: Qi + - family-names: Liu + given-names: Yunshan + - family-names: Johnson + given-names: Emma +title: "GDPLib: an open library of Generalized Disjunctive Programming (GDP) models" +version: 1.0.0 +url: https://github.com/SECQUOIA/gdplib +license-url: https://github.com/SECQUOIA/gdplib/blob/master/LICENSE From 5f0319c5d8b70e0b1a1eeac527477176c05457a0 Mon Sep 17 00:00:00 2001 From: ZedongPeng Date: Fri, 15 Sep 2023 13:37:47 -0400 Subject: [PATCH 02/99] update citation --- CITATION.ccf | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/CITATION.ccf b/CITATION.ccf index a529319..84da065 100644 --- a/CITATION.ccf +++ b/CITATION.ccf @@ -14,3 +14,25 @@ title: "GDPLib: an open library of Generalized Disjunctive Programming (GDP) mod version: 1.0.0 url: https://github.com/SECQUOIA/gdplib license-url: https://github.com/SECQUOIA/gdplib/blob/master/LICENSE +preferred-citation: + type: incollection + booktitle: "Computer Aided Chemical Engineering" + volume: 49 + start: 1285 + end: 1290 + title: "Advances in Generalized Disjunctive and Mixed-Integer Nonlinear Programming Algorithms and Software for Superstructure Optimization" + year:2022 + publisher: "Elsevier" + authors: + - family-names: "Bernal" + given-names: "David E." + - family-names: "Liu" + given-names: "Yunshan" + - family-names: "Bynum" + given-names: "Michael L" + - family-names: "Laird" + given-names: "Carl D" + - family-names: "Siirola" + given-names: "John D" + - family-names: "Grossmann" + given-names: "Ignacio E" From 756c0239e4bc8295d26b643bca7ea1b3cf9a7d8c Mon Sep 17 00:00:00 2001 From: ZedongPeng Date: Tue, 19 Sep 2023 17:00:02 -0400 Subject: [PATCH 03/99] update --- CITATION.ccf | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/CITATION.ccf b/CITATION.ccf index 84da065..54026d5 100644 --- a/CITATION.ccf +++ b/CITATION.ccf @@ -1,15 +1,20 @@ cff-version: 1.0.0 message: "If you use this software, please cite it as below." authors: - - family-names: Bernal + - family-names: Bernal Neira given-names: David E. orcid: https://orcid.org/0000-0002-8308-5016 + - family-names: Peng + given-names: Zedong + orcid: https://orcid.org/0000-0001-6001-1738 - family-names: Chen given-names: Qi + orcid: https://orcid.org/0000-0002-2389-2238 - family-names: Liu given-names: Yunshan - family-names: Johnson given-names: Emma + orcid: https://orcid.org/0000-0002-4285-5184 title: "GDPLib: an open library of Generalized Disjunctive Programming (GDP) models" version: 1.0.0 url: https://github.com/SECQUOIA/gdplib @@ -24,7 +29,7 @@ preferred-citation: year:2022 publisher: "Elsevier" authors: - - family-names: "Bernal" + - family-names: "Bernal Neira" given-names: "David E." - family-names: "Liu" given-names: "Yunshan" From a79ce209fe46cbefc70fa8363bfb869d5372a59a Mon Sep 17 00:00:00 2001 From: ZedongPeng Date: Tue, 19 Sep 2023 17:49:54 -0400 Subject: [PATCH 04/99] move gdpopt config --- gdplib/methanol/methanol.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/gdplib/methanol/methanol.py b/gdplib/methanol/methanol.py index 16f802a..dd7702c 100644 --- a/gdplib/methanol/methanol.py +++ b/gdplib/methanol/methanol.py @@ -650,11 +650,7 @@ def solve_with_gdp_opt(): lb, ub = compute_bounds_on_expr(_c.body) _d.BigM[_c] = max(abs(lb), abs(ub)) opt = pe.SolverFactory('gdpopt') - opt.CONFIG.strategy = 'LOA' - opt.CONFIG.mip_solver = 'gams' - opt.CONFIG.nlp_solver = 'gams' - opt.CONFIG.tee = True - res = opt.solve(m) + 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) From 430be5ceae6fa7b353032aa1a1ecfe6cbe1fd437 Mon Sep 17 00:00:00 2001 From: David Bernal Date: Fri, 20 Oct 2023 14:04:37 -0400 Subject: [PATCH 05/99] Rename CITATION.ccf to CITATION.cff --- CITATION.ccf => CITATION.cff | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename CITATION.ccf => CITATION.cff (100%) diff --git a/CITATION.ccf b/CITATION.cff similarity index 100% rename from CITATION.ccf rename to CITATION.cff From 3b718c4135db8fe8ae2ee3c39775c351a976dce4 Mon Sep 17 00:00:00 2001 From: Albert Lee Date: Thu, 2 Nov 2023 11:56:29 -0400 Subject: [PATCH 06/99] Added Documentation for pyomo examples --- gdplib/pyomo_examples/batch_processing.py | 29 +++++++++-------------- 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/gdplib/pyomo_examples/batch_processing.py b/gdplib/pyomo_examples/batch_processing.py index 3695e19..00daaea 100644 --- a/gdplib/pyomo_examples/batch_processing.py +++ b/gdplib/pyomo_examples/batch_processing.py @@ -1,3 +1,12 @@ +""" +batch_processing.py +This problem seeks to minimize the investment cost in the design of a plant with multiple units in parallel and intermediate storage tanks [1]. + +Reference: + [1] Ravemark, E. Optimization models for design and operation of chemical batch processes. Ph.D. Thesis, ETH Zurich, 1995. + [2] Vecchietti, A.; Grossmann, I. E. LOGMIP: a disjunctive 0-1 non-linear optimizer for process system models. Computers and Chemical Engineering 1994, 23, 555–565. + +""" from os.path import join from pyomo.common.fileutils import this_file_dir @@ -23,7 +32,7 @@ def build_model(): model.BigM[None] = 1000 - ## Constants from GAMS + # Constants from GAMS StorageTankSizeFactor = 2*5 # btw, I know 2*5 is 10... I don't know why it's written this way in GAMS? StorageTankSizeFactorByProd = 3 MinFlow = -log(10000) @@ -36,10 +45,7 @@ def build_model(): # TODO: YOU ARE HERE. YOU HAVEN'T ACTUALLY MADE THESE THE BOUNDS YET, NOR HAVE YOU FIGURED OUT WHOSE # BOUNDS THEY ARE. AND THERE ARE MORE IN GAMS. - - ########## # Sets - ########## model.PRODUCTS = Set() model.STAGES = Set(ordered=True) @@ -52,12 +58,9 @@ def filter_out_last(model, j): # TODO: these aren't in the formulation?? - #model.STORAGE_TANKS = Set() + # model.STORAGE_TANKS = Set() - - ############### # Parameters - ############### model.HorizonTime = Param() model.Alpha1 = Param() @@ -92,9 +95,7 @@ def get_log_coeffs(model, k): model.unitsOutOfPhaseUB = Param(model.STAGES, default=UnitsOutOfPhaseUB) - ################ # Variables - ################ # TODO: right now these match the formulation. There are more in GAMS... @@ -138,9 +139,7 @@ def get_storageTankSize_bounds(model, j): model.outOfPhase = Var(model.STAGES, model.PARALLELUNITS, within=Binary) model.inPhase = Var(model.STAGES, model.PARALLELUNITS, within=Binary) - ############### # Objective - ############### def get_cost_rule(model): return model.Alpha1 * sum(exp(model.unitsInPhase_log[j] + model.unitsOutOfPhase_log[j] + \ @@ -148,11 +147,7 @@ def get_cost_rule(model): model.Alpha2 * sum(exp(model.Beta2 * model.storageTankSize_log[j]) for j in model.STAGESExceptLast) model.min_cost = Objective(rule=get_cost_rule) - - ############## # Constraints - ############## - def processing_capacity_rule(model, j, i): return model.volume_log[j] >= log(model.ProductSizeFactor[i, j]) + model.batchSize_log[i, j] - \ model.unitsInPhase_log[j] @@ -169,9 +164,7 @@ def finish_in_time_rule(model): model.finish_in_time = Constraint(rule=finish_in_time_rule) - ############### # Disjunctions - ############### def storage_tank_selection_disjunct_rule(disjunct, selectStorageTank, j): model = disjunct.model() From 99b2abca659b7bfdf4ae7f981771aea835b9f977 Mon Sep 17 00:00:00 2001 From: Albert Lee Date: Thu, 21 Dec 2023 21:35:35 -0500 Subject: [PATCH 07/99] Add jobshop scheduling model and small concrete model documentation --- gdplib/pyomo_examples/jobshop.py | 97 +++++++++++++++++++++++++++++--- 1 file changed, 89 insertions(+), 8 deletions(-) diff --git a/gdplib/pyomo_examples/jobshop.py b/gdplib/pyomo_examples/jobshop.py index 84bb159..b9815f3 100644 --- a/gdplib/pyomo_examples/jobshop.py +++ b/gdplib/pyomo_examples/jobshop.py @@ -32,11 +32,22 @@ # def build_model(): + """ + Build the jobshop scheduling model, which has a set of jobs which must be processed in sequence of stages but not all jobs require all stages. + A zero wait transfer policy is assumed between stages. + To obtain a feasible solution it is necessary to eliminate all clashes between jobs. + It requires that no two jobs be performed at any stage at any time. The objective is to minimize the makespan, the time to complete all jobs. + + References: + Raman & Grossmann, Computers and Chemical Engineering 18, 7, p.563-578, 1994. + Aldo Vecchietti, LogMIP User's Manual, http://www.logmip.ceride.gov.ar/, 2007 + """ model = AbstractModel() - model.JOBS = Set(ordered=True) - model.STAGES = Set(ordered=True) + model.JOBS = Set(ordered=True, doc='Set of jobs') + model.STAGES = Set(ordered=True, doc='Set of stages') model.I_BEFORE_K = RangeSet(0,1) + # Task durations model.tau = Param(model.JOBS, model.STAGES, default=0) @@ -45,25 +56,72 @@ def build_model(): model.ms = Var() # Start time of each job def t_bounds(model, I): + """ + Calculate the time bounds for the start time of each job in a scheduling model. + + Args: + model (Pyomo.Abstractmodel): jobshop scheduling model, which has a set of jobs which must be processed in sequence of stages but not all jobs require all stages. + A zero wait transfer policy is assumed between stages. + I (str): job index + + Returns: + tuple: (lower bound, upper bound) for the start time of each job in a scheduling model. + """ return (0, sum(value(model.tau[idx]) for idx in model.tau)) - model.t = Var( model.JOBS, within=NonNegativeReals, bounds=t_bounds ) + model.t = Var( model.JOBS, within=NonNegativeReals, bounds=t_bounds, doc='Start time of each job') # Auto-generate the L set (potential collisions between 2 jobs at any stage. def _L_filter(model, I, K, J): + """ + Filter for the L set (potential collisions between 2 jobs at any stage. + + Args: + model (Pyomo.Abstractmodel): jobshop scheduling model, which has a set of jobs which must be processed in sequence of stages but not all jobs require all stages. + A zero wait transfer policy is assumed between stages. + I (str): job index + K (str): job index that is greater than I (After I) + J (int): stage index + + Returns: + bool: True if I < K and the parameters, model.tau[I,J] and model.tau[K,J] + """ return I < K and model.tau[I,J] and model.tau[K,J] model.L = Set( initialize=model.JOBS * model.JOBS * model.STAGES, - dimen=3, filter=_L_filter) + dimen=3, filter=_L_filter, doc='Set of potential collisions between 2 jobs at any stage') # Makespan is greater than the start time of every job + that job's # total duration def _feas(model, I): + """_summary_ + + Args: + model (Pyomo.Abstractmodel): jobshop scheduling model, which has a set of jobs which must be processed in sequence of stages but not all jobs require all stages. + A zero wait transfer policy is assumed between stages. + I (str): job index + + Returns: + bool: True if the makespan is greater than the sum of the start time of every job and that job's total duration. + """ return model.ms >= model.t[I] + sum(model.tau[I,M] for M in model.STAGES) - model.Feas = Constraint(model.JOBS, rule=_feas) + model.Feas = Constraint(model.JOBS, rule=_feas, doc='Makespan is greater than the start time of every job + that job''s total duration') # Disjunctions to prevent clashes at a stage: This creates a set of # disjunct pairs: one if job I occurs before job K and the other if job # K occurs before job I. def _NoClash(disjunct, I, K, J, IthenK): + """ + Disjunctions to prevent clashes at a stage: This creates a set of disjunct pairs: one if job I occurs before job K and the other if job K occurs before job I. + + Args: + model (Pyomo.Disjunction): The disjunction of the model. + I (str): job index + K (str): job index that is greater than I (After I) + J (int): stage index + IthenK (bool): True if I occurs before K, False if K occurs before I + + Returns: + None, but creates a disjunction to prevent clashes at a stage. + """ model = disjunct.model() lhs = model.t[I] + sum([M Date: Fri, 22 Dec 2023 16:21:24 -0500 Subject: [PATCH 08/99] Add documentation on the disease_model.py --- gdplib/pyomo_examples/disease_model.py | 130 +++++++++++++++++++++---- 1 file changed, 113 insertions(+), 17 deletions(-) diff --git a/gdplib/pyomo_examples/disease_model.py b/gdplib/pyomo_examples/disease_model.py index 8695c85..e5d9db1 100644 --- a/gdplib/pyomo_examples/disease_model.py +++ b/gdplib/pyomo_examples/disease_model.py @@ -21,16 +21,26 @@ import math def build_model(): + """ + Builds the model for the SIR disease model using a low/high transmission parameter + + Returns: + _type_: _description_ + """ # import data + # Population Data pop = [ 15.881351, 15.881339, 15.881320, 15.881294, 15.881261, 15.881223, 15.881180, 15.881132, 15.881079, 15.881022, 15.880961, 15.880898, 15.880832, 15.880764, 15.880695, 15.880624, 15.880553, 15.880480, 15.880409, 15.880340, 15.880270, 15.880203, 15.880138, 15.880076, 15.880016, 15.879960, 15.879907, 15.879852, 15.879799, 15.879746, 15.879693, 15.879638, 15.879585, 15.879531, 15.879477, 15.879423, 15.879370, 15.879315, 15.879262, 15.879209, 15.879155, 15.879101, 15.879048, 15.878994, 15.878940, 15.878886, 15.878833, 15.878778, 15.878725, 15.878672, 15.878618, 15.878564, 15.878510, 15.878457, 15.878402, 15.878349, 15.878295, 15.878242, 15.878187, 15.878134, 15.878081, 15.878026, 15.877973, 15.877919, 15.877864, 15.877811, 15.877758, 15.877704, 15.877650, 15.877596, 15.877543, 15.877488, 15.877435, 15.877381, 15.877326, 15.877273, 15.877220, 15.877166, 15.877111, 15.877058, 15.877005, 15.876950, 15.876896, 15.876843, 15.876789, 15.876735, 15.876681, 15.876628, 15.876573, 15.876520, 15.876466, 15.876411, 15.876358, 15.876304, 15.876251, 15.876196, 15.876143, 15.876089, 15.876034, 15.875981, 15.875927, 15.875872, 15.875819, 15.875765, 15.875712, 15.875657, 15.875604, 15.875550, 15.875495, 15.875442, 15.875388, 15.875335, 15.875280, 15.875226, 15.875173, 15.875118, 15.875064, 15.875011, 15.874956, 15.874902, 15.874849, 15.874795, 15.874740, 15.874687, 15.874633, 15.874578, 15.874525, 15.874471, 15.874416, 15.874363, 15.874309, 15.874256, 15.874201, 15.874147, 15.874094, 15.874039, 15.873985, 15.873931, 15.873878, 15.873823, 15.873769, 15.873716, 15.873661, 15.873607, 15.873554, 15.873499, 15.873445, 15.873391, 15.873338, 15.873283, 15.873229, 15.873175, 15.873121, 15.873067, 15.873013, 15.872960, 15.872905, 15.872851, 15.872797, 15.872742, 15.872689, 15.872635, 15.872580, 15.872526, 15.872473, 15.872419, 15.872364, 15.872310, 15.872256, 15.872202, 15.872148, 15.872094, 15.872039, 15.871985, 15.871932, 15.871878, 15.871823, 15.871769, 15.871715, 15.871660, 15.871607, 15.871553, 15.871499, 15.871444, 15.871390, 15.871337, 15.871282, 15.871228, 15.871174, 15.871119, 15.871065, 15.871012, 15.870958, 15.870903, 15.870849, 15.870795, 15.870740, 15.870686, 15.870633, 15.870577, 15.870524, 15.870470, 15.870416, 15.870361, 15.870307, 15.870253, 15.870198, 15.870144, 15.870091, 15.870037, 15.869982, 15.869928, 15.869874, 15.869819, 15.869765, 15.869711, 15.869656, 15.869602, 15.869548, 15.869495, 15.869439, 15.869386, 15.869332, 15.869277, 15.869223, 15.869169, 15.869114, 15.869060, 15.869006, 15.868952, 15.868897, 15.868843, 15.868789, 15.868734, 15.868679, 15.868618, 15.868556, 15.868489, 15.868421, 15.868351, 15.868280, 15.868208, 15.868134, 15.868063, 15.867991, 15.867921, 15.867852, 15.867785, 15.867721, 15.867659, 15.867601, 15.867549, 15.867499, 15.867455, 15.867416, 15.867383, 15.867357, 15.867338, 15.867327, 15.867321, 15.867327, 15.867338, 15.867359, 15.867386, 15.867419, 15.867459, 15.867505, 15.867555, 15.867610, 15.867671, 15.867734, 15.867801, 15.867869, 15.867941, 15.868012, 15.868087, 15.868161, 15.868236, 15.868310, 15.868384, 15.868457, 15.868527, 15.868595, 15.868661, 15.868722, 15.868780, 15.868837, 15.868892, 15.868948, 15.869005, 15.869061, 15.869116, 15.869173, 15.869229, 15.869284, 15.869341, 15.869397, 15.869452, 15.869509, 15.869565, 15.869620, 15.869677, 15.869733, 15.869788, 15.869845, 15.869901, 15.869956, 15.870012, 15.870069, 15.870124, 15.870180, 15.870237, 15.870292, 15.870348, 15.870405, 15.870461, 15.870516, 15.870572, 15.870629, 15.870684, 15.870740, 15.870796, 15.870851, 15.870908, 15.870964, 15.871019, 15.871076, 15.871132, 15.871187, 15.871243, 15.871300, 15.871355, 15.871411, 15.871467, 15.871522, 15.871579, 15.871635, 15.871691, 15.871746, 15.871802, 15.871859, 15.871914, 15.871970, 15.872026, 15.872081, 15.872138, 15.872194, 15.872249, 15.872305, 15.872361, 15.872416, 15.872473, 15.872529, 15.872584, 15.872640, 15.872696, 15.872751, 15.872807, 15.872864, 15.872919, 15.872975, 15.873031, 15.873087, 15.873142, 15.873198, 15.873255, 15.873310, 15.873366, 15.873422, 15.873477, 15.873533, 15.873589, 15.873644, 15.873700, 15.873757, 15.873811, 15.873868, 15.873924, 15.873979, 15.874035, 15.874091, 15.874146, 15.874202, 15.874258, 15.874313, 15.874369, 15.874425, 15.874481, 15.874536, 15.874592] + # Logarithm of Infected Data logIstar = [7.943245, 8.269994, 8.517212, 8.814208, 9.151740, 9.478472, 9.559847, 9.664087, 9.735378, 9.852583, 9.692265, 9.498807, 9.097634, 8.388878, 7.870516, 7.012956, 6.484941, 5.825368, 5.346815, 5.548361, 5.706732, 5.712617, 5.709714, 5.696888, 5.530087, 5.826563, 6.643563, 7.004292, 7.044663, 7.190259, 7.335926, 7.516861, 7.831779, 8.188895, 8.450204, 8.801436, 8.818379, 8.787658, 8.601685, 8.258338, 7.943364, 7.425585, 7.062834, 6.658307, 6.339600, 6.526984, 6.679178, 6.988758, 7.367331, 7.746694, 8.260558, 8.676522, 9.235582, 9.607778, 9.841917, 10.081571, 10.216090, 10.350366, 10.289668, 10.248842, 10.039504, 9.846343, 9.510392, 9.190923, 8.662465, 7.743221, 7.128458, 5.967898, 5.373883, 5.097497, 4.836570, 5.203345, 5.544798, 5.443047, 5.181152, 5.508669, 6.144130, 6.413744, 6.610423, 6.748885, 6.729511, 6.789841, 6.941034, 7.093516, 7.307039, 7.541077, 7.644803, 7.769145, 7.760187, 7.708017, 7.656795, 7.664983, 7.483828, 6.887324, 6.551093, 6.457449, 6.346064, 6.486300, 6.612378, 6.778753, 6.909477, 7.360570, 8.150303, 8.549044, 8.897572, 9.239323, 9.538751, 9.876531, 10.260911, 10.613536, 10.621510, 10.661115, 10.392899, 10.065536, 9.920090, 9.933097, 9.561691, 8.807713, 8.263463, 7.252184, 6.669083, 5.877763, 5.331878, 5.356563, 5.328469, 5.631146, 6.027497, 6.250717, 6.453919, 6.718444, 7.071636, 7.348905, 7.531528, 7.798226, 8.197941, 8.578809, 8.722964, 8.901152, 8.904370, 8.889865, 8.881902, 8.958903, 8.721281, 8.211509, 7.810624, 7.164607, 6.733688, 6.268503, 5.905983, 5.900432, 5.846547, 6.245427, 6.786271, 7.088480, 7.474295, 7.650063, 7.636703, 7.830990, 8.231516, 8.584816, 8.886908, 9.225216, 9.472778, 9.765505, 9.928623, 10.153033, 10.048574, 9.892620, 9.538818, 8.896100, 8.437584, 7.819738, 7.362598, 6.505880, 5.914972, 6.264584, 6.555019, 6.589319, 6.552029, 6.809771, 7.187616, 7.513918, 8.017712, 8.224957, 8.084474, 8.079148, 8.180991, 8.274269, 8.413748, 8.559599, 8.756090, 9.017927, 9.032720, 9.047983, 8.826873, 8.366489, 8.011876, 7.500830, 7.140406, 6.812626, 6.538719, 6.552218, 6.540129, 6.659927, 6.728530, 7.179692, 7.989210, 8.399173, 8.781128, 9.122303, 9.396378, 9.698512, 9.990104, 10.276543, 10.357284, 10.465869, 10.253833, 10.018503, 9.738407, 9.484367, 9.087025, 8.526409, 8.041126, 7.147168, 6.626706, 6.209446, 5.867231, 5.697439, 5.536769, 5.421413, 5.238297, 5.470136, 5.863007, 6.183083, 6.603569, 6.906278, 7.092324, 7.326612, 7.576052, 7.823430, 7.922775, 8.041677, 8.063403, 8.073229, 8.099726, 8.168522, 8.099041, 8.011404, 7.753147, 6.945211, 6.524244, 6.557723, 6.497742, 6.256247, 5.988794, 6.268093, 6.583316, 7.106842, 8.053929, 8.508237, 8.938915, 9.311863, 9.619753, 9.931745, 10.182361, 10.420978, 10.390829, 10.389230, 10.079342, 9.741479, 9.444561, 9.237448, 8.777687, 7.976436, 7.451502, 6.742856, 6.271545, 5.782289, 5.403089, 5.341954, 5.243509, 5.522993, 5.897001, 6.047042, 6.100738, 6.361727, 6.849562, 7.112544, 7.185346, 7.309412, 7.423746, 7.532142, 7.510318, 7.480175, 7.726362, 8.061117, 8.127072, 8.206166, 8.029634, 7.592953, 7.304869, 7.005394, 6.750019, 6.461377, 6.226432, 6.287047, 6.306452, 6.783694, 7.450957, 7.861692, 8.441530, 8.739626, 8.921994, 9.168961, 9.428077, 9.711664, 10.032714, 10.349937, 10.483985, 10.647475, 10.574038, 10.522431, 10.192246, 9.756246, 9.342511, 8.872072, 8.414189, 7.606582, 7.084701, 6.149903, 5.517257, 5.839429, 6.098090, 6.268935, 6.475965, 6.560543, 6.598942, 6.693938, 6.802531, 6.934345, 7.078370, 7.267736, 7.569640, 7.872204, 8.083603, 8.331226, 8.527144, 8.773523, 8.836599, 8.894303, 8.808326, 8.641717, 8.397901, 7.849034, 7.482899, 7.050252, 6.714103, 6.900603, 7.050765, 7.322905, 7.637986, 8.024340, 8.614505, 8.933591, 9.244008, 9.427410, 9.401385, 9.457744, 9.585068, 9.699673, 9.785478, 9.884559, 9.769732, 9.655075, 9.423071, 9.210198, 8.786654, 8.061787, 7.560976, 6.855829, 6.390707, 5.904006, 5.526631, 5.712303, 5.867027, 5.768367, 5.523352, 5.909118, 6.745543, 6.859218 ] + # Change in Susceptible Population deltaS = [ 9916.490263 ,12014.263380 ,13019.275755 ,12296.373612 ,8870.995603 ,1797.354574 ,-6392.880771 ,-16150.825387 ,-27083.245106 ,-40130.421462 ,-50377.169958 ,-57787.717468 ,-60797.223427 ,-59274.041897 ,-55970.213230 ,-51154.650927 ,-45877.841034 ,-40278.553775 ,-34543.967175 ,-28849.633641 ,-23192.776605 ,-17531.130740 ,-11862.021829 ,-6182.456792 ,-450.481090 ,5201.184400 ,10450.773882 ,15373.018272 ,20255.699431 ,24964.431669 ,29470.745887 ,33678.079947 ,37209.808930 ,39664.432393 ,41046.735479 ,40462.982011 ,39765.070209 ,39270.815830 ,39888.077002 ,42087.276604 ,45332.012929 ,49719.128772 ,54622.190928 ,59919.718626 ,65436.341097 ,70842.911460 ,76143.747430 ,81162.358574 ,85688.102884 ,89488.917734 ,91740.108470 ,91998.787916 ,87875.986012 ,79123.877908 ,66435.611045 ,48639.250610 ,27380.282817 ,2166.538464 ,-21236.428084 ,-43490.803535 ,-60436.624080 ,-73378.401966 ,-80946.278268 ,-84831.969493 ,-84696.627286 ,-81085.365407 ,-76410.847049 ,-70874.415387 ,-65156.276464 ,-59379.086883 ,-53557.267619 ,-47784.164830 ,-42078.001172 ,-36340.061427 ,-30541.788202 ,-24805.281435 ,-19280.817165 ,-13893.690606 ,-8444.172221 ,-3098.160839 ,2270.908649 ,7594.679295 ,12780.079247 ,17801.722109 ,22543.091206 ,26897.369814 ,31051.285734 ,34933.809557 ,38842.402859 ,42875.230152 ,47024.395356 ,51161.516122 ,55657.298307 ,60958.155424 ,66545.635029 ,72202.930397 ,77934.761905 ,83588.207792 ,89160.874522 ,94606.115027 ,99935.754968 ,104701.404975 ,107581.670606 ,108768.440311 ,107905.700480 ,104062.148863 ,96620.281684 ,83588.443029 ,61415.088182 ,27124.031692 ,-7537.285321 ,-43900.451653 ,-70274.062783 ,-87573.481475 ,-101712.148408 ,-116135.719087 ,-124187.225446 ,-124725.278371 ,-122458.145590 ,-117719.918256 ,-112352.138605 ,-106546.806030 ,-100583.803012 ,-94618.253238 ,-88639.090897 ,-82725.009842 ,-76938.910669 ,-71248.957807 ,-65668.352795 ,-60272.761991 ,-55179.538428 ,-50456.021161 ,-46037.728058 ,-42183.912670 ,-39522.184006 ,-38541.255303 ,-38383.665728 ,-39423.998130 ,-40489.466130 ,-41450.406768 ,-42355.156592 ,-43837.562085 ,-43677.262972 ,-41067.896944 ,-37238.628465 ,-32230.392026 ,-26762.766062 ,-20975.163308 ,-15019.218554 ,-9053.105545 ,-3059.663132 ,2772.399618 ,8242.538397 ,13407.752291 ,18016.047539 ,22292.125752 ,26616.583347 ,30502.564253 ,33153.890890 ,34216.684448 ,33394.220786 ,29657.417791 ,23064.375405 ,12040.831532 ,-2084.921068 ,-21390.235970 ,-38176.615985 ,-51647.714482 ,-59242.564959 ,-60263.150854 ,-58599.245165 ,-54804.972560 ,-50092.112608 ,-44465.812552 ,-38533.096297 ,-32747.104307 ,-27130.082610 ,-21529.632955 ,-15894.611939 ,-10457.566933 ,-5429.042583 ,-903.757828 ,2481.947589 ,5173.789976 ,8358.768202 ,11565.584635 ,14431.147931 ,16951.619820 ,18888.807708 ,20120.884465 ,20222.141242 ,18423.168124 ,16498.668271 ,14442.624242 ,14070.038273 ,16211.370808 ,19639.815904 ,24280.360465 ,29475.380079 ,35030.793540 ,40812.325095 ,46593.082382 ,52390.906885 ,58109.310860 ,63780.896094 ,68984.456561 ,72559.442320 ,74645.487900 ,74695.219755 ,72098.143876 ,66609.929889 ,56864.971296 ,41589.295266 ,19057.032104 ,-5951.329863 ,-34608.796853 ,-56603.801584 ,-72678.838057 ,-83297.070856 ,-90127.593511 ,-92656.040614 ,-91394.995510 ,-88192.056842 ,-83148.833075 ,-77582.587173 ,-71750.440823 ,-65765.369857 ,-59716.101820 ,-53613.430067 ,-47473.832358 ,-41287.031890 ,-35139.919259 ,-29097.671507 ,-23178.836760 ,-17486.807388 ,-12046.775779 ,-6802.483422 ,-1867.556171 ,2644.380534 ,6615.829501 ,10332.557518 ,13706.737038 ,17017.991307 ,20303.136670 ,23507.386461 ,26482.194102 ,29698.585356 ,33196.305757 ,37385.914179 ,42872.996212 ,48725.617879 ,54564.488527 ,60453.841604 ,66495.146265 ,72668.620416 ,78723.644870 ,84593.136677 ,89974.936239 ,93439.798630 ,95101.207834 ,94028.126381 ,89507.925620 ,80989.846001 ,66944.274744 ,47016.422041 ,19932.783790 ,-6198.433172 ,-32320.379400 ,-49822.852084 ,-60517.553414 ,-66860.548269 ,-70849.714105 ,-71058.721556 ,-67691.947812 ,-63130.703822 ,-57687.607311 ,-51916.952488 ,-45932.054982 ,-39834.909941 ,-33714.535713 ,-27564.443333 ,-21465.186188 ,-15469.326408 ,-9522.358787 ,-3588.742161 ,2221.802073 ,7758.244339 ,13020.269708 ,18198.562827 ,23211.338588 ,28051.699645 ,32708.577247 ,37413.795242 ,42181.401920 ,46462.499633 ,49849.582315 ,53026.578940 ,55930.600705 ,59432.642178 ,64027.356857 ,69126.843653 ,74620.328837 ,80372.056070 ,86348.152766 ,92468.907239 ,98568.998246 ,104669.511588 ,110445.790143 ,115394.348973 ,119477.553152 ,121528.574511 ,121973.674087 ,121048.017786 ,118021.473181 ,112151.993711 ,102195.999157 ,85972.731130 ,61224.719621 ,31949.279603 ,-3726.022971 ,-36485.298619 ,-67336.469799 ,-87799.366129 ,-98865.713558 ,-104103.651120 ,-105068.402300 ,-103415.820781 ,-99261.356633 ,-94281.850081 ,-88568.701325 ,-82625.711921 ,-76766.776770 ,-70998.803524 ,-65303.404499 ,-59719.198305 ,-54182.230439 ,-48662.904657 ,-43206.731668 ,-37732.701095 ,-32375.478519 ,-27167.508567 ,-22197.211891 ,-17722.869502 ,-13925.135219 ,-10737.893027 ,-8455.327914 ,-7067.008358 ,-7086.991191 ,-7527.693561 ,-8378.025732 ,-8629.383998 ,-7854.586079 ,-5853.040657 ,-1973.225485 ,2699.850783 ,8006.098287 ,13651.734934 ,19139.318072 ,24476.645420 ,29463.480336 ,33899.078820 ,37364.528796 ,38380.214949 ,37326.585649 ,33428.470616 ,27441.000494 ,21761.126583 ,15368.408081 ,7224.234078 ,-2702.217396 ,-14109.682505 ,-27390.915614 ,-38569.562393 ,-47875.155339 ,-53969.121872 ,-57703.473001 ,-57993.198171 ,-54908.391840 ,-50568.410328 ,-45247.622563 ,-39563.224328 ,-33637.786521 ,-27585.345413 ,-21572.074797 ,-15597.363909 ,-9577.429076 ,-3475.770622 ,2520.378408 ,8046.881775 ,13482.345595 ] + # Transmission Rate Set beta_set = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26] - #from new_data_set import * + # from new_data_set import * # Uncomment this line to use new data set # declare model name model = ConcreteModel() @@ -47,29 +57,48 @@ def build_model(): # define variable bounds def _gt_zero(m,i): + """ + boundary greater than zero + + Args: + m (Pyomo.ConcreteModel): SIR disease model using a low/high transmission parameter + i (int): index of biweekly periods in the data set + + Returns: + None, but set up the boundary greater than zero. + """ return (0.0,1e7) def _beta_bounds(m): + """ + Set up the Transition Parameters Bounds + + Args: + m (Pyomo.ConcreteModel): SIR disease model using a low/high transmission parameter + + Returns: + None, but set up the transition parameters bounds. + """ return (None,5.0) # define variables # log of estimated cases - #model.logI = Var(model.S_meas, bounds=_gt_zero) - model.logI = Var(model.S_meas, bounds=(0.001,1e7)) + # model.logI = Var(model.S_meas, bounds=_gt_zero, doc='log of estimated cases') + model.logI = Var(model.S_meas, bounds=(0.001,1e7), doc='log of estimated cases') # log of transmission parameter beta - #model.logbeta = Var(model.S_beta, bounds=_gt_zero) - model.logbeta = Var(model.S_beta, bounds=(0.0001,5)) + # model.logbeta = Var(model.S_beta, bounds=_gt_zero, doc='log of transmission parameter beta') + model.logbeta = Var(model.S_beta, bounds=(0.0001,5), doc='log of transmission parameter beta') # binary variable y over all betas - #model.y = Var(model.S_beta, within=Binary) + # model.y = Var(model.S_beta, within=Binary, doc='binary variable y over all betas') # low value of beta - #model.logbeta_low = Var(bounds=_beta_bounds) + # model.logbeta_low = Var(bounds=_beta_bounds, doc='low value of beta') model.logbeta_low = Var(bounds=(0.0001,5)) # high value of beta - #model.logbeta_high = Var(bounds=_beta_bounds) - model.logbeta_high = Var(bounds=(0.0001,5)) + # model.logbeta_high = Var(bounds=_beta_bounds, doc='high value of beta') + model.logbeta_high = Var(bounds=(0.0001,5), doc='high value of beta') # dummy variables - model.p = Var(model.S_meas, bounds=_gt_zero) - model.n = Var(model.S_meas, bounds=_gt_zero) + model.p = Var(model.S_meas, bounds=_gt_zero, doc='dummy variable p') + model.n = Var(model.S_meas, bounds=_gt_zero, doc='dummy variable n') # define indexed constants @@ -78,7 +107,7 @@ def _beta_bounds(m): # changes in susceptible population profile from susceptible reconstruction deltaS = deltaS # mean susceptibles - #meanS = 1.04e6 + # meanS = 1.04e6 meanS = 8.65e5 # log of measured population logN = pop @@ -87,39 +116,106 @@ def _beta_bounds(m): # define objective def _obj_rule(m): + """ + Minimize the total discrepancy or error in the SIR disease model model over time. + These variables ('p' and 'n') are likely to represent the overestimation and underestimation errors, respectively, + in the model's estimation of infectious cases compared to the observed data. + By minimizing their sum, the model seeks to closely align its estimations with the actual observed data. + + Args: + m (Pyomo.ConcreteModel): SIR disease model using a low/high transmission parameter + + Returns: + expr (Pyomo.Expression): The objective function expression, which is the sum of the variables 'p' and 'n' for each time period. + This expression represents the total error (both overestimation and underestimation) across all time periods in the model. + """ expr = sum(m.p[i] + m.n[i] for i in m.S_meas) return expr - model.obj = Objective(rule=_obj_rule, sense=minimize) + model.obj = Objective(rule=_obj_rule, sense=minimize, doc='objective function') # define constraints def _logSIR(m,i): + """ + Constraint function for the SIR disease model that represents the dynamics of infectious disease spread. + This constraint is based on a logarithmic formulation of the SIR model. + + Args: + m (Pyomo.ConcreteModel): SIR disease model using a low/high transmission parameter + i (int): index of biweekly periods in the data set + + Returns: + A tuple (0.0, expr) where expr is the Pyomo expression for the SIR constraint at the i-th biweekly period. + This represents the change in the logarithm of estimated infectious cases adjusted by the transmission rate, + change in susceptibles, and the total population. + """ expr = m.logI[i+1] - ( m.logbeta[beta_set[i-1]] + m.logI[i] + math.log(deltaS[i-1] + meanS) - logN[i-1] ) return (0.0, expr) - model.logSIR = Constraint(model.S_meas_small, rule=_logSIR) + model.logSIR = Constraint(model.S_meas_small, rule=_logSIR, doc='log of SIR disease model') # objective function constraint def _p_n_const(m,i): + """ + Defines a constraint for the SIR disease model that relates the model's estimated infectious cases to observed data. + This constraint is essential for ensuring the model's estimates are consistent with actual data, adjusted for overestimation and underestimation. + The constraint is formulated to account for the difference between the logarithm of observed infectious cases (logIstar) and the model's logarithm of estimated infectious cases (m.logI). + It includes the variables 'p' and 'n', which represent the overestimation and underestimation errors, respectively. + + Args: + m (Pyomo.ConcreteModel): SIR disease model using a low/high transmission parameter + i (int): index of biweekly periods in the data set + + Returns: + A tuple (0.0, expr) where expr is the Pyomo expression for the constraint at the i-th biweekly period. + This represents the adjustment of the model's estimated cases by accounting for overestimation and underestimation errors compared to the observed data. + """ expr = logIstar[i-1] - m.logI[i] - m.p[i] + m.n[i] return (0.0, expr) - model.p_n_const = Constraint(model.S_meas,rule=_p_n_const) + model.p_n_const = Constraint(model.S_meas,rule=_p_n_const, doc='constraint for p and n') # disjuncts model.BigM = Suffix() model.y = RangeSet(0,1) def _high_low(disjunct, i, y): + """ + Disjunct for the high and low beta values + + Args: + disjunct (Pyomo.Disjunction): _description_ + i (int): index of biweekly periods in the data set + y (int): binary variable + + Returns: + None, but sets up the disjuncts for the high and low beta values. + """ model = disjunct.model() if y: disjunct.c = Constraint(expr=model.logbeta_high - model.logbeta[i]== 0.0) else: disjunct.c = Constraint(expr=model.logbeta[i] - model.logbeta_low == 0.0) model.BigM[disjunct.c] = bigM - model.high_low = Disjunct(model.S_beta, model.y, rule=_high_low) + model.high_low = Disjunct(model.S_beta, model.y, rule=_high_low, doc='disjunct for high and low beta values') # disjunctions def _disj(model, i): + """ + Disjunction for the high and low beta values + + Args: + model (Pyomo.Disjunction): disjunction for the high and low beta values + i (int): index of biweekly periods in the data set + + Returns: + A list of disjuncts (model.high_low[i, j]) for each binary state 'j' in 'model.y'. + This list represents the two possible states for each biweekly period 'i': + - model.high_low[i, 0]: The set of constraints when the low beta value is active. + - model.high_low[i, 1]: The set of constraints when the high beta value is active. + + This list is used by Pyomo to create a disjunction for each biweekly period, allowing the model to choose between the high or low beta value constraints based on the optimization process. + The defined disjunctions are integral to the model, enabling it to adaptively select the appropriate beta value for each time period, reflecting changes in disease transmission dynamics. + """ return [model.high_low[i,j] for j in model.y] - model.disj = Disjunction(model.S_beta, rule=_disj) + model.disj = Disjunction(model.S_beta, rule=_disj, doc='disjunction for high and low beta values') return model From 246f6577efa5ff4163bb29eadc0904126f21794f Mon Sep 17 00:00:00 2001 From: Albert Lee Date: Fri, 19 Jan 2024 14:13:15 -0500 Subject: [PATCH 09/99] Modified Errors --- gdplib/pyomo_examples/jobshop.py | 81 ++++++++++++++++++++++---------- 1 file changed, 57 insertions(+), 24 deletions(-) diff --git a/gdplib/pyomo_examples/jobshop.py b/gdplib/pyomo_examples/jobshop.py index b9815f3..06fb44b 100644 --- a/gdplib/pyomo_examples/jobshop.py +++ b/gdplib/pyomo_examples/jobshop.py @@ -31,36 +31,37 @@ # Aldo Vecchietti, LogMIP User's Manual, http://www.logmip.ceride.gov.ar/, 2007 # + def build_model(): """ Build the jobshop scheduling model, which has a set of jobs which must be processed in sequence of stages but not all jobs require all stages. A zero wait transfer policy is assumed between stages. - To obtain a feasible solution it is necessary to eliminate all clashes between jobs. + To obtain a feasible solution it is necessary to eliminate all clashes between jobs. It requires that no two jobs be performed at any stage at any time. The objective is to minimize the makespan, the time to complete all jobs. References: Raman & Grossmann, Computers and Chemical Engineering 18, 7, p.563-578, 1994. Aldo Vecchietti, LogMIP User's Manual, http://www.logmip.ceride.gov.ar/, 2007 - """ + """ model = AbstractModel() model.JOBS = Set(ordered=True, doc='Set of jobs') model.STAGES = Set(ordered=True, doc='Set of stages') - model.I_BEFORE_K = RangeSet(0,1) - + model.I_BEFORE_K = RangeSet(0, 1) # Task durations model.tau = Param(model.JOBS, model.STAGES, default=0) # Total Makespan (this will be the objective) model.ms = Var() + # Start time of each job def t_bounds(model, I): """ Calculate the time bounds for the start time of each job in a scheduling model. Args: - model (Pyomo.Abstractmodel): jobshop scheduling model, which has a set of jobs which must be processed in sequence of stages but not all jobs require all stages. + model (Pyomo.Abstractmodel): jobshop scheduling model, which has a set of jobs which must be processed in sequence of stages but not all jobs require all stages. A zero wait transfer policy is assumed between stages. I (str): job index @@ -68,7 +69,13 @@ def t_bounds(model, I): tuple: (lower bound, upper bound) for the start time of each job in a scheduling model. """ return (0, sum(value(model.tau[idx]) for idx in model.tau)) - model.t = Var( model.JOBS, within=NonNegativeReals, bounds=t_bounds, doc='Start time of each job') + + model.t = Var( + model.JOBS, + within=NonNegativeReals, + bounds=t_bounds, + doc='Start time of each job', + ) # Auto-generate the L set (potential collisions between 2 jobs at any stage. def _L_filter(model, I, K, J): @@ -83,11 +90,16 @@ def _L_filter(model, I, K, J): J (int): stage index Returns: - bool: True if I < K and the parameters, model.tau[I,J] and model.tau[K,J] + expression: True if I < K and the parameters, model.tau[I,J] and model.tau[K,J] """ - return I < K and model.tau[I,J] and model.tau[K,J] - model.L = Set( initialize=model.JOBS * model.JOBS * model.STAGES, - dimen=3, filter=_L_filter, doc='Set of potential collisions between 2 jobs at any stage') + return I < K and model.tau[I, J] and model.tau[K, J] + + model.L = Set( + initialize=model.JOBS * model.JOBS * model.STAGES, + dimen=3, + filter=_L_filter, + doc='Set of potential collisions between 2 jobs at any stage', + ) # Makespan is greater than the start time of every job + that job's # total duration @@ -100,10 +112,16 @@ def _feas(model, I): I (str): job index Returns: - bool: True if the makespan is greater than the sum of the start time of every job and that job's total duration. + expression: True if the makespan is greater than the sum of the start time of every job and that job's total duration. """ - return model.ms >= model.t[I] + sum(model.tau[I,M] for M in model.STAGES) - model.Feas = Constraint(model.JOBS, rule=_feas, doc='Makespan is greater than the start time of every job + that job''s total duration') + return model.ms >= model.t[I] + sum(model.tau[I, M] for M in model.STAGES) + + model.Feas = Constraint( + model.JOBS, + rule=_feas, + doc='Makespan is greater than the start time of every job + that job' + 's total duration', + ) # Disjunctions to prevent clashes at a stage: This creates a set of # disjunct pairs: one if job I occurs before job K and the other if job @@ -113,7 +131,7 @@ def _NoClash(disjunct, I, K, J, IthenK): Disjunctions to prevent clashes at a stage: This creates a set of disjunct pairs: one if job I occurs before job K and the other if job K occurs before job I. Args: - model (Pyomo.Disjunction): The disjunction of the model. + model (Pyomo.Disjunction): The disjunction of the model. I (str): job index K (str): job index that is greater than I (After I) J (int): stage index @@ -123,13 +141,19 @@ def _NoClash(disjunct, I, K, J, IthenK): None, but creates a disjunction to prevent clashes at a stage. """ model = disjunct.model() - lhs = model.t[I] + sum([M Date: Fri, 19 Jan 2024 14:37:51 -0500 Subject: [PATCH 10/99] jobshop.py black formatted. --- gdplib/pyomo_examples/jobshop.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/gdplib/pyomo_examples/jobshop.py b/gdplib/pyomo_examples/jobshop.py index 06fb44b..abc4bf0 100644 --- a/gdplib/pyomo_examples/jobshop.py +++ b/gdplib/pyomo_examples/jobshop.py @@ -26,7 +26,7 @@ # # References: # -# Raman & Grossmann, Computers and Chemical Engineering 18, 7, p.563-578, 1994. +# Raman & Grossmann, Modelling and computational techniques for logic based integer programming, Computers and Chemical Engineering 18, 7, p.563-578, 1994. # # Aldo Vecchietti, LogMIP User's Manual, http://www.logmip.ceride.gov.ar/, 2007 # @@ -40,7 +40,7 @@ def build_model(): It requires that no two jobs be performed at any stage at any time. The objective is to minimize the makespan, the time to complete all jobs. References: - Raman & Grossmann, Computers and Chemical Engineering 18, 7, p.563-578, 1994. + Raman & Grossmann, Modelling and computational techniques for logic based integer programming, Computers and Chemical Engineering 18, 7, p.563-578, 1994. Aldo Vecchietti, LogMIP User's Manual, http://www.logmip.ceride.gov.ar/, 2007 """ model = AbstractModel() @@ -61,7 +61,7 @@ def t_bounds(model, I): Calculate the time bounds for the start time of each job in a scheduling model. Args: - model (Pyomo.Abstractmodel): jobshop scheduling model, which has a set of jobs which must be processed in sequence of stages but not all jobs require all stages. + model (Pyomo.Abstractmodel): job shop scheduling model, which has a set of jobs which must be processed in sequence of stages but not all jobs require all stages. A zero wait transfer policy is assumed between stages. I (str): job index From 46a2cace37840aa6a45023f39d6edfb81cfb82a8 Mon Sep 17 00:00:00 2001 From: Albert Lee Date: Fri, 19 Jan 2024 16:23:15 -0500 Subject: [PATCH 11/99] Add explantion on the commented code and update documentation --- gdplib/pyomo_examples/disease_model.py | 115 +++++++++++++++++++------ 1 file changed, 87 insertions(+), 28 deletions(-) diff --git a/gdplib/pyomo_examples/disease_model.py b/gdplib/pyomo_examples/disease_model.py index e5d9db1..458429c 100644 --- a/gdplib/pyomo_examples/disease_model.py +++ b/gdplib/pyomo_examples/disease_model.py @@ -24,8 +24,11 @@ def build_model(): """ Builds the model for the SIR disease model using a low/high transmission parameter + Arg: + None + Returns: - _type_: _description_ + model(Pyomo.ConcreteModel): Pyomo model object that represent the SIR disease model using a low/high transmission parameter. """ # import data # Population Data @@ -58,7 +61,7 @@ def build_model(): # define variable bounds def _gt_zero(m,i): """ - boundary greater than zero + Boundary greater than zero Args: m (Pyomo.ConcreteModel): SIR disease model using a low/high transmission parameter @@ -68,6 +71,7 @@ def _gt_zero(m,i): None, but set up the boundary greater than zero. """ return (0.0,1e7) + def _beta_bounds(m): """ Set up the Transition Parameters Bounds @@ -85,15 +89,15 @@ def _beta_bounds(m): # log of estimated cases # model.logI = Var(model.S_meas, bounds=_gt_zero, doc='log of estimated cases') model.logI = Var(model.S_meas, bounds=(0.001,1e7), doc='log of estimated cases') - # log of transmission parameter beta + # # log of transmission parameter beta # model.logbeta = Var(model.S_beta, bounds=_gt_zero, doc='log of transmission parameter beta') model.logbeta = Var(model.S_beta, bounds=(0.0001,5), doc='log of transmission parameter beta') - # binary variable y over all betas + # # binary variable y over all betas # model.y = Var(model.S_beta, within=Binary, doc='binary variable y over all betas') - # low value of beta + # # low value of beta # model.logbeta_low = Var(bounds=_beta_bounds, doc='low value of beta') model.logbeta_low = Var(bounds=(0.0001,5)) - # high value of beta + # # high value of beta # model.logbeta_high = Var(bounds=_beta_bounds, doc='high value of beta') model.logbeta_high = Var(bounds=(0.0001,5), doc='high value of beta') # dummy variables @@ -219,31 +223,86 @@ def _disj(model, i): return model - """ -# high beta disjuncts -def highbeta_L(m,i): - expr = m.logbeta[i] - m.logbeta_high + bigM*(1-m.y[i]) - return (0.0, expr, None) -model.highbeta_L = Constraint(model.S_beta, rule=highbeta_L) - -def highbeta_U(m,i): - expr = m.logbeta[i] - m.logbeta_high - return (None, expr, 0.0) -model.highbeta_U = Constraint(model.S_beta, rule=highbeta_U) - -# low beta disjuncts -def lowbeta_U(m,i): - expr = m.logbeta[i] - m.logbeta_low - bigM*(m.y[i]) - return (None, expr, 0.0) -model.lowbeta_U = Constraint(model.S_beta, rule=lowbeta_U) - -def lowbeta_L(m,i): - expr = m.logbeta[i] - m.logbeta_low - return (0.0, expr, None) -model.lowbeta_L = Constraint(model.S_beta, rule=lowbeta_L) +The original code inside build_model employs a disjunctive approach with integrated constraints. On the other hand, the commented code uses separate constraints for each scenario, applying the big-M Reformulation. """ +# # disjuncts +# # high beta disjuncts +# def highbeta_L(m,i): +# """ +# Args: +# m (Pyomo.ConcreteModel): SIR disease model using a low/high transmission parameter +# i (int): index of biweekly periods in the data set + +# Returns: +# A tuple (0.0, expr, None) where expr is the Pyomo expression for the lower bound of the high beta disjunct at the i-th biweekly period. +# This represents the lower bound of the high beta disjunct at the i-th biweekly period. + +# Note: +# The given function is given as the expression that the disjunctions are converted by big-M reformulation. +# The binary variable m.y[i] is commented inside the model function. +# """ +# expr = m.logbeta[i] - m.logbeta_high + bigM*(1-m.y[i]) +# return (0.0, expr, None) +# model.highbeta_L = Constraint(model.S_beta, rule=highbeta_L) + +# def highbeta_U(m,i): +# """ +# Args: +# m (Pyomo.ConcreteModel): SIR disease model using a low/high transmission parameter +# i (int): index of biweekly periods in the data set + +# Returns: +# A tuple (None, expr, 0.0) where expr is the Pyomo expression for the upper bound of the high beta disjunct at the i-th biweekly period. +# This represents the upper bound of the high beta disjunct at the i-th biweekly period. + +# Note: +# The given function is given as the expression that the disjunctions are converted by big-M reformulation. +# The binary variable m.y[i] is commented inside the model function. +# """ +# expr = m.logbeta[i] - m.logbeta_high +# return (None, expr, 0.0) +# model.highbeta_U = Constraint(model.S_beta, rule=highbeta_U) + +# # low beta disjuncts +# def lowbeta_U(m,i): +# """ +# Args: +# m (Pyomo.ConcreteModel): SIR disease model using a low/high transmission parameter +# i (int): index of biweekly periods in the data set + +# Returns: +# A tuple (None, expr, 0.0) where expr is the Pyomo expression for the upper bound of the low beta disjunct at the i-th biweekly period. +# This represents the upper bound of the low beta disjunct at the i-th biweekly period. + +# Note: +# The given function is given as the expression that the disjunctions are converted by big-M reformulation. +# The binary variable m.y[i] is commented inside the model function. +# """ +# expr = m.logbeta[i] - m.logbeta_low - bigM*(m.y[i]) +# return (None, expr, 0.0) +# model.lowbeta_U = Constraint(model.S_beta, rule=lowbeta_U) + +# def lowbeta_L(m,i): +# """ +# Args: +# m (Pyomo.ConcreteModel): SIR disease model using a low/high transmission parameter +# i (int): index of biweekly periods in the data set + +# Returns: +# A tuple (0.0, expr, None) where expr is the Pyomo expression for the lower bound of the low beta disjunct at the i-th biweekly period. +# This represents the lower bound of the low beta disjunct at the i-th biweekly period. + +# Note: +# The given function is given as the expression that the disjunctions are converted by big-M reformulation. +# The binary variable m.y[i] is commented inside the model function. +# """ +# expr = m.logbeta[i] - m.logbeta_low +# return (0.0, expr, None) +# model.lowbeta_L = Constraint(model.S_beta, rule=lowbeta_L) + + if __name__ == "__main__": m = build_model() TransformationFactory('gdp.bigm').apply_to(m) From a16499d275612234d40e7e13397f7cebcb1de7a7 Mon Sep 17 00:00:00 2001 From: Albert Lee Date: Tue, 23 Jan 2024 11:14:03 -0500 Subject: [PATCH 12/99] manage the comments of the disease_model.py --- gdplib/pyomo_examples/disease_model.py | 35 +++++++++++++++----------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/gdplib/pyomo_examples/disease_model.py b/gdplib/pyomo_examples/disease_model.py index 458429c..65aad88 100644 --- a/gdplib/pyomo_examples/disease_model.py +++ b/gdplib/pyomo_examples/disease_model.py @@ -87,17 +87,22 @@ def _beta_bounds(m): # define variables # log of estimated cases + """ + The original code inside build_model employs a disjunctive approach with integrated constraints. + On the other hand, the commented code uses separate constraints for each scenario, applying the Big-M Reformulation. + Binary variables (model.y) are defined inside on the build_model() function. The disjuncts for the Big-M Reformulation is written outside of the code. + """ # model.logI = Var(model.S_meas, bounds=_gt_zero, doc='log of estimated cases') model.logI = Var(model.S_meas, bounds=(0.001,1e7), doc='log of estimated cases') - # # log of transmission parameter beta + # log of transmission parameter beta # model.logbeta = Var(model.S_beta, bounds=_gt_zero, doc='log of transmission parameter beta') model.logbeta = Var(model.S_beta, bounds=(0.0001,5), doc='log of transmission parameter beta') - # # binary variable y over all betas + # binary variable y over all betas # model.y = Var(model.S_beta, within=Binary, doc='binary variable y over all betas') - # # low value of beta + # low value of beta # model.logbeta_low = Var(bounds=_beta_bounds, doc='low value of beta') model.logbeta_low = Var(bounds=(0.0001,5)) - # # high value of beta + # high value of beta # model.logbeta_high = Var(bounds=_beta_bounds, doc='high value of beta') model.logbeta_high = Var(bounds=(0.0001,5), doc='high value of beta') # dummy variables @@ -106,16 +111,16 @@ def _beta_bounds(m): # define indexed constants - # log of measured cases after adjusting for underreporting + # log of measured cases after adjusting for under-reporting logIstar = logIstar # changes in susceptible population profile from susceptible reconstruction deltaS = deltaS - # mean susceptibles + # mean susceptibles (Number of Population) # meanS = 1.04e6 meanS = 8.65e5 - # log of measured population + # log of measured population (Number of Population(log scale)) logN = pop - # define index for beta over all measurements + # define index for beta over all measurements () beta_set = beta_set # define objective @@ -142,13 +147,15 @@ def _logSIR(m,i): """ Constraint function for the SIR disease model that represents the dynamics of infectious disease spread. This constraint is based on a logarithmic formulation of the SIR model. + The model enforces that the actual dynamics (captured in the expression expr) follow the specified dynamics of the SIR model for each biweekly period by setting the expression equal to zero. + This is the constraint of the differential function discretized. Args: m (Pyomo.ConcreteModel): SIR disease model using a low/high transmission parameter i (int): index of biweekly periods in the data set Returns: - A tuple (0.0, expr) where expr is the Pyomo expression for the SIR constraint at the i-th biweekly period. + A tuple (0.0, expr) where expr is the Pyomo expression for the SIR constraint at the i-th biweekly period. This represents the change in the logarithm of estimated infectious cases adjusted by the transmission rate, change in susceptibles, and the total population. """ @@ -223,12 +230,10 @@ def _disj(model, i): return model -""" -The original code inside build_model employs a disjunctive approach with integrated constraints. On the other hand, the commented code uses separate constraints for each scenario, applying the big-M Reformulation. -""" -# # disjuncts -# # high beta disjuncts + +# disjuncts +# high beta disjuncts # def highbeta_L(m,i): # """ # Args: @@ -265,7 +270,7 @@ def _disj(model, i): # return (None, expr, 0.0) # model.highbeta_U = Constraint(model.S_beta, rule=highbeta_U) -# # low beta disjuncts +# low beta disjuncts # def lowbeta_U(m,i): # """ # Args: From 14100d332476bb54aff8e869543b9d7febd119a1 Mon Sep 17 00:00:00 2001 From: Albert Lee Date: Tue, 23 Jan 2024 11:54:43 -0500 Subject: [PATCH 13/99] Add the results running the code via dantzig server --- gdplib/pyomo_examples/READMEdisease.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 gdplib/pyomo_examples/READMEdisease.md diff --git a/gdplib/pyomo_examples/READMEdisease.md b/gdplib/pyomo_examples/READMEdisease.md new file mode 100644 index 0000000..604baea --- /dev/null +++ b/gdplib/pyomo_examples/READMEdisease.md @@ -0,0 +1,17 @@ +# Results for the disease_model.py +The model is ran with the following software configuration: +Python 3.12.1 +Pyomo 6.7.0 +GAMS 44.3.0 (solver: BARON 23.6.22.) + +The hardware configuration is: +Dantzig Server +Processor: Linux cluster with 48 AMD EPYC 7643 2.3GHz CPUs +RAM: 1.0 TB + +Solution = 304.416210719832 found at node 1 +Best possible = 304.415906304 +Absolute gap = 0.000304415832090399 optca = 1E-9 +Relative gap = 9.99998756211332E-7 optcr = 1E-6 + +Total time = 2.08 seconds From 349c85c0fec5fcb4e171837682b1469c5055586f Mon Sep 17 00:00:00 2001 From: Carolina Tristan Date: Fri, 1 Mar 2024 12:03:42 -0500 Subject: [PATCH 14/99] Fix documentation comment in build_model function --- gdplib/pyomo_examples/jobshop.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gdplib/pyomo_examples/jobshop.py b/gdplib/pyomo_examples/jobshop.py index abc4bf0..d3eca97 100644 --- a/gdplib/pyomo_examples/jobshop.py +++ b/gdplib/pyomo_examples/jobshop.py @@ -108,11 +108,11 @@ def _feas(model, I): Args: model (Pyomo.Abstractmodel): jobshop scheduling model, which has a set of jobs which must be processed in sequence of stages but not all jobs require all stages. - A zero wait transfer policy is assumed between stages. + A zero wait transfer policy is assumed between stages. I (str): job index Returns: - expression: True if the makespan is greater than the sum of the start time of every job and that job's total duration. + bool: True if the makespan is greater than the sum of the start time of every job and that job's total duration. """ return model.ms >= model.t[I] + sum(model.tau[I, M] for M in model.STAGES) From ff9f3761d89349c958fc29f72d3ab6a891214d62 Mon Sep 17 00:00:00 2001 From: Carolina Tristan Date: Fri, 1 Mar 2024 12:16:34 -0500 Subject: [PATCH 15/99] Update return type in build_model function --- gdplib/pyomo_examples/jobshop.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gdplib/pyomo_examples/jobshop.py b/gdplib/pyomo_examples/jobshop.py index d3eca97..2d959aa 100644 --- a/gdplib/pyomo_examples/jobshop.py +++ b/gdplib/pyomo_examples/jobshop.py @@ -112,7 +112,7 @@ def _feas(model, I): I (str): job index Returns: - bool: True if the makespan is greater than the sum of the start time of every job and that job's total duration. + expression: True if the makespan is greater than the sum of the start time of every job and that job's total duration. """ return model.ms >= model.t[I] + sum(model.tau[I, M] for M in model.STAGES) From e38e88618bccf97b1ff0f434ff52e5d39e5229c8 Mon Sep 17 00:00:00 2001 From: Carolina Tristan Date: Wed, 27 Mar 2024 14:56:37 -0400 Subject: [PATCH 16/99] Refactor jobshop.py: Update function documentation for _feas() --- gdplib/pyomo_examples/jobshop.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gdplib/pyomo_examples/jobshop.py b/gdplib/pyomo_examples/jobshop.py index 2d959aa..02dd84d 100644 --- a/gdplib/pyomo_examples/jobshop.py +++ b/gdplib/pyomo_examples/jobshop.py @@ -104,7 +104,7 @@ def _L_filter(model, I, K, J): # Makespan is greater than the start time of every job + that job's # total duration def _feas(model, I): - """_summary_ + """This function creates a constraint that ensures the makespan is greater than the sum of the start time of every job and that job's total duration. Args: model (Pyomo.Abstractmodel): jobshop scheduling model, which has a set of jobs which must be processed in sequence of stages but not all jobs require all stages. From 7d8fe60a394150c5706be572f656c0559e5a8446 Mon Sep 17 00:00:00 2001 From: Carolina Tristan Date: Wed, 27 Mar 2024 15:02:22 -0400 Subject: [PATCH 17/99] Add jobshop scheduling model documentation --- gdplib/pyomo_examples/jobshop.py | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/gdplib/pyomo_examples/jobshop.py b/gdplib/pyomo_examples/jobshop.py index 02dd84d..81682d6 100644 --- a/gdplib/pyomo_examples/jobshop.py +++ b/gdplib/pyomo_examples/jobshop.py @@ -42,6 +42,8 @@ def build_model(): References: Raman & Grossmann, Modelling and computational techniques for logic based integer programming, Computers and Chemical Engineering 18, 7, p.563-578, 1994. Aldo Vecchietti, LogMIP User's Manual, http://www.logmip.ceride.gov.ar/, 2007 + Args: None + Returns: AbstractModel: jobshop scheduling model, which has a set of jobs which must be processed in sequence of stages but not all jobs require all stages. """ model = AbstractModel() @@ -104,17 +106,17 @@ def _L_filter(model, I, K, J): # Makespan is greater than the start time of every job + that job's # total duration def _feas(model, I): - """This function creates a constraint that ensures the makespan is greater than the sum of the start time of every job and that job's total duration. + """This function creates a constraint that ensures the makespan is greater than the sum of the start time of every job and that job's total duration. - Args: - model (Pyomo.Abstractmodel): jobshop scheduling model, which has a set of jobs which must be processed in sequence of stages but not all jobs require all stages. - A zero wait transfer policy is assumed between stages. - I (str): job index + Args: + model (Pyomo.Abstractmodel): jobshop scheduling model, which has a set of jobs which must be processed in sequence of stages but not all jobs require all stages. + A zero wait transfer policy is assumed between stages. + I (str): job index - Returns: - expression: True if the makespan is greater than the sum of the start time of every job and that job's total duration. - """ - return model.ms >= model.t[I] + sum(model.tau[I, M] for M in model.STAGES) + Returns: + expression: True if the makespan is greater than the sum of the start time of every job and that job's total duration. + """ + return model.ms >= model.t[I] + sum(model.tau[I, M] for M in model.STAGES) model.Feas = Constraint( model.JOBS, From 96835207a37fdacbd19f7adb300f141a0a708220 Mon Sep 17 00:00:00 2001 From: Carolina Tristan Date: Wed, 27 Mar 2024 15:18:10 -0400 Subject: [PATCH 18/99] Black formatted --- gdplib/pyomo_examples/jobshop.py | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/gdplib/pyomo_examples/jobshop.py b/gdplib/pyomo_examples/jobshop.py index 81682d6..8eb09eb 100644 --- a/gdplib/pyomo_examples/jobshop.py +++ b/gdplib/pyomo_examples/jobshop.py @@ -42,8 +42,11 @@ def build_model(): References: Raman & Grossmann, Modelling and computational techniques for logic based integer programming, Computers and Chemical Engineering 18, 7, p.563-578, 1994. Aldo Vecchietti, LogMIP User's Manual, http://www.logmip.ceride.gov.ar/, 2007 + Args: None - Returns: AbstractModel: jobshop scheduling model, which has a set of jobs which must be processed in sequence of stages but not all jobs require all stages. + + Returns: + AbstractModel: jobshop scheduling model, which has a set of jobs which must be processed in sequence of stages but not all jobs require all stages. """ model = AbstractModel() @@ -106,17 +109,18 @@ def _L_filter(model, I, K, J): # Makespan is greater than the start time of every job + that job's # total duration def _feas(model, I): - """This function creates a constraint that ensures the makespan is greater than the sum of the start time of every job and that job's total duration. + """ + This function creates a constraint that ensures the makespan is greater than the sum of the start time of every job and that job's total duration. - Args: - model (Pyomo.Abstractmodel): jobshop scheduling model, which has a set of jobs which must be processed in sequence of stages but not all jobs require all stages. - A zero wait transfer policy is assumed between stages. - I (str): job index + Args: + model (Pyomo.Abstractmodel): jobshop scheduling model, which has a set of jobs which must be processed in sequence of stages but not all jobs require all stages. + A zero wait transfer policy is assumed between stages. + I (str): job index - Returns: - expression: True if the makespan is greater than the sum of the start time of every job and that job's total duration. - """ - return model.ms >= model.t[I] + sum(model.tau[I, M] for M in model.STAGES) + Returns: + expression: True if the makespan is greater than the sum of the start time of every job and that job's total duration. + """ + return model.ms >= model.t[I] + sum(model.tau[I, M] for M in model.STAGES) model.Feas = Constraint( model.JOBS, From 252ee4e45c9695f29381d201247fca7178434f18 Mon Sep 17 00:00:00 2001 From: Carolina Tristan Date: Mon, 1 Apr 2024 18:54:50 -0400 Subject: [PATCH 19/99] Update copyright year in jobshop.py header --- gdplib/pyomo_examples/jobshop.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gdplib/pyomo_examples/jobshop.py b/gdplib/pyomo_examples/jobshop.py index 8eb09eb..369804a 100644 --- a/gdplib/pyomo_examples/jobshop.py +++ b/gdplib/pyomo_examples/jobshop.py @@ -1,7 +1,8 @@ # ___________________________________________________________________________ # # Pyomo: Python Optimization Modeling Objects -# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Copyright (c) 2008-2024 +# National Technology and Engineering Solutions of Sandia, LLC # Under the terms of Contract DE-NA0003525 with National Technology and # Engineering Solutions of Sandia, LLC, the U.S. Government retains certain # rights in this software. From 064199cb1ce9acb7c5e221845c8fcb71788198a5 Mon Sep 17 00:00:00 2001 From: Carolina Tristan Date: Mon, 1 Apr 2024 19:07:17 -0400 Subject: [PATCH 20/99] Update jobshop.py with reference DOI --- gdplib/pyomo_examples/jobshop.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gdplib/pyomo_examples/jobshop.py b/gdplib/pyomo_examples/jobshop.py index 369804a..78b50f8 100644 --- a/gdplib/pyomo_examples/jobshop.py +++ b/gdplib/pyomo_examples/jobshop.py @@ -27,7 +27,7 @@ # # References: # -# Raman & Grossmann, Modelling and computational techniques for logic based integer programming, Computers and Chemical Engineering 18, 7, p.563-578, 1994. +# Raman & Grossmann, Modelling and computational techniques for logic based integer programming, Computers and Chemical Engineering 18, 7, p.563-578, 1994. DOI: 10.1016/0098-1354(93)E0010-7. # # Aldo Vecchietti, LogMIP User's Manual, http://www.logmip.ceride.gov.ar/, 2007 # From 0e15d0b4eb020b5435066d81acd34270321b2c19 Mon Sep 17 00:00:00 2001 From: Albert Lee Date: Tue, 2 Apr 2024 17:46:23 -0400 Subject: [PATCH 21/99] Basic Documentation for Med Term Purchasing.py --- gdplib/pyomo_examples/med_term_purchasing.py | 886 +++++++++++++++++-- 1 file changed, 824 insertions(+), 62 deletions(-) diff --git a/gdplib/pyomo_examples/med_term_purchasing.py b/gdplib/pyomo_examples/med_term_purchasing.py index d83d8d2..81c2695 100644 --- a/gdplib/pyomo_examples/med_term_purchasing.py +++ b/gdplib/pyomo_examples/med_term_purchasing.py @@ -21,6 +21,26 @@ # most notably the process structure itself and the mass balance information. def build_model(): + """ + Build a Pyomo abstract model for the medium-term purchasing contracts problem. + + Returns + ------- + Pyomo.AbstractModel + Pyomo abstract model for medium-term purchasing contracts problem. + + Raises + ------ + RuntimeError + _description_ + RuntimeError + _description_ + + References + ---------- + [1] Vecchietti, Aldo, and I. Grossmann. "Computational experience with logmip solving linear and nonlinear disjunctive programming problems." In Proc. of FOCAPD, pp. 587-590. 2004. + [2] Park, M., Park, S., Mele, F. D., & Grossmann, I. E. (2006). Modeling of purchase and sales contracts in supply chain optimization. Industrial & Engineering Chemistry Research, 45(14), 5013-5026. DOI: https://doi.org/10.1021/ie0513144 + """ model = AbstractModel() # Constants (data that was hard-coded in GAMS model) @@ -38,33 +58,33 @@ def build_model(): # T # t in GAMS - model.TimePeriods = Set(ordered=True) + model.TimePeriods = Set(ordered=True, doc="Set of time periods") # Available length contracts # p in GAMS - model.Contracts_Length = Set() + model.Contracts_Length = Set(doc="Set of available length contracts") # JP # final(j) in GAMS # Finished products - model.Products = Set() + model.Products = Set(doc="Set of finished products") # JM # rawmat(J) in GAMS # Set of Raw Materials-- raw materials, intermediate products, and final products partition J - model.RawMaterials = Set() + model.RawMaterials = Set(doc="Set of raw materials, intermediate products, and final products") # C # c in GAMS - model.Contracts = Set() + model.Contracts = Set(doc='Set of available contracts') # I # i in GAMS - model.Processes = Set() + model.Processes = Set(doc='Set of processes in the network') # J # j in GAMS - model.Streams = Set() + model.Streams = Set(doc='Set of streams in the network') ################## @@ -73,33 +93,34 @@ def build_model(): # Q_it # excap(i) in GAMS - model.Capacity = Param(model.Processes) + model.Capacity = Param(model.Processes, doc='Capacity of process i') # u_ijt # cov(i) in GAMS - model.ProcessConstants = Param(model.Processes) + model.ProcessConstants = Param(model.Processes, doc='Process constants') # a_jt^U and d_jt^U # spdm(j,t) in GAMS - model.SupplyAndDemandUBs = Param(model.Streams, model.TimePeriods, default=0) + model.SupplyAndDemandUBs = Param(model.Streams, model.TimePeriods, default=0, doc='Supply and demand upper bounds') # d_jt^L # lbdm(j, t) in GAMS - model.DemandLB = Param(model.Streams, model.TimePeriods, default=0) + model.DemandLB = Param(model.Streams, model.TimePeriods, default=0, doc='Demand lower bounds') # delta_it # delta(i, t) in GAMS # operating cost of process i at time t - model.OperatingCosts = Param(model.Processes, model.TimePeriods) + model.OperatingCosts = Param(model.Processes, model.TimePeriods, doc='Operating cost of process i at time t') # prices of raw materials under FP contract and selling prices of products # pf(j, t) in GAMS # omega_jt and pf_jt - model.Prices = Param(model.Streams, model.TimePeriods, default=0) + model.Prices = Param(model.Streams, model.TimePeriods, default=0, doc='Prices of raw materials under FP contract and selling prices of products') # Price for quantities less than min amount under discount contract # pd1(j, t) in GAMS model.RegPrice_Discount = Param(model.Streams, model.TimePeriods) + # Discounted price for the quantity purchased exceeding the min amount # pd2(j,t0 in GAMS model.DiscountPrice_Discount = Param(model.Streams, model.TimePeriods) @@ -107,40 +128,41 @@ def build_model(): # Price for quantities below min amount # pb1(j,t) in GAMS model.RegPrice_Bulk = Param(model.Streams, model.TimePeriods) - # Price for quantities aboce min amount + + # Price for quantities above min amount # pb2(j, t) in GAMS - model.DiscountPrice_Bulk = Param(model.Streams, model.TimePeriods) + model.DiscountPrice_Bulk = Param(model.Streams, model.TimePeriods, doc='Price for quantities above minimum amount under bulk contract') # prices with length contract # pl(j, p, t) in GAMS - model.Prices_Length = Param(model.Streams, model.Contracts_Length, model.TimePeriods, default=0) + model.Prices_Length = Param(model.Streams, model.Contracts_Length, model.TimePeriods, default=0, doc='Prices with length contract') # sigmad_jt # sigmad(j, t) in GAMS # Minimum quantity of chemical j that must be bought before recieving a Discount under discount contract - model.MinAmount_Discount = Param(model.Streams, model.TimePeriods, default=0) + model.MinAmount_Discount = Param(model.Streams, model.TimePeriods, default=0, 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 # sigmab(j, t) in GAMS - model.MinAmount_Bulk = Param(model.Streams, model.TimePeriods, default=0) + model.MinAmount_Bulk = Param(model.Streams, model.TimePeriods, default=0, 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 # sigmal(j, p) in GAMS - model.MinAmount_Length = Param(model.Streams, model.Contracts_Length, default=0) + model.MinAmount_Length = Param(model.Streams, model.Contracts_Length, default=0, doc='Minimum quantity of chemical j that must be bought before receiving a Discount under length contract') # main products of process i # These are 1 (true) if stream j is the main product of process i, false otherwise. # jm(j, i) in GAMS - model.MainProducts = Param(model.Streams, model.Processes, default=0) + model.MainProducts = Param(model.Streams, model.Processes, default=0, doc='Main products of process i') # theta_jt # psf(j, t) in GAMS # Shortfall penalty of product j at time t - model.ShortfallPenalty = Param(model.Products, model.TimePeriods) + model.ShortfallPenalty = Param(model.Products, model.TimePeriods, doc='Shortfall penalty of product j at time t') # shortfall upper bound # sfub(j, t) in GAMS - model.ShortfallUB = Param(model.Products, model.TimePeriods, default=0) + model.ShortfallUB = Param(model.Products, model.TimePeriods, default=0, doc='Shortfall upper bound') # epsilon_jt # cinv(j, t) in GAMS @@ -156,9 +178,41 @@ def build_model(): # All of these upper bounds are hardcoded. So I am just leaving them that way. # This means they all have to be the same as each other right now. def getAmountUBs(model, j, t): + """_summary_ + + Parameters + ---------- + model : Pyomo.AbstractModel + Pyomo abstract model for medium-term purchasing contracts problem. + j : _type_ + _description_ + t : Index + Time period. + + Returns + ------- + _type_ + _description_ + """ return AMOUNT_UB def getCostUBs(model, j, t): + """_summary_ + + Parameters + ---------- + model : Pyomo.AbstractModel + Pyomo abstract model for medium-term purchasing contracts problem. + j : _type_ + _description_ + t : Index + Time period. + + Returns + ------- + _type_ + _description_ + """ return COST_UB model.AmountPurchasedUB_FP = Param(model.Streams, model.TimePeriods, @@ -186,16 +240,33 @@ def getCostUBs(model, j, t): # prof in GAMS # will be objective - model.Profit = Var() + model.Profit = Var(doc='Profit') # f(j, t) in GAMS # mass flow rates in tons per time interval t - model.FlowRate = Var(model.Streams, model.TimePeriods, within=NonNegativeReals) + model.FlowRate = Var(model.Streams, model.TimePeriods, within=NonNegativeReals, doc='Mass flow rates in tons per time interval t') # V_jt # inv(j, t) in GAMS # inventory level of chemical j at time period t def getInventoryBounds(model, i, j): + """ + Inventory level of chemical j at time period t + + Parameters + ---------- + model : Pyomo.AbstractModel + Pyomo abstract model for medium-term purchasing contracts problem. + i : _type_ + _description_ + j : _type_ + _description_ + + Returns + ------- + _type_ + _description_ + """ return (0, model.InventoryLevelUB[i,j]) model.InventoryLevel = Var(model.Streams, model.TimePeriods, bounds=getInventoryBounds) @@ -204,6 +275,17 @@ def getInventoryBounds(model, i, j): # sf(j, t) in GAMS # Shortfall of demand for chemical j at time period t def getShortfallBounds(model, i, j): + """_summary_ + + Parameters + ---------- + model : Pyomo.AbstractModel + _description_ + i : _type_ + _description_ + j : _type_ + _description_ + """ return(0, model.ShortfallUB[i,j]) model.Shortfall = Var(model.Products, model.TimePeriods, bounds=getShortfallBounds) @@ -214,12 +296,44 @@ def getShortfallBounds(model, i, j): # spf(j, t) in GAMS # Amount of raw material j bought under fixed price contract at time period t def get_FP_bounds(model, j, t): + """_summary_ + + Parameters + ---------- + model : Pyomo.AbstractModel + Pyomo abstract model for medium-term purchasing contracts problem. + j : _type_ + _description_ + t : Index + Time period. + + Returns + ------- + _type_ + _description_ + """ return (0, model.AmountPurchasedUB_FP[j,t]) model.AmountPurchased_FP = Var(model.Streams, model.TimePeriods, bounds=get_FP_bounds) # spd(j, t) in GAMS def get_Discount_Total_bounds(model, j, t): + """_summary_ + + Parameters + ---------- + model : Pyomo.AbstractModel + Pyomo abstract model for medium-term purchasing contracts problem. + j : _type_ + _description_ + t : Index + Time period. + + Returns + ------- + _type_ + _description_ + """ return (0, model.AmountPurchasedUB_Discount[j,t]) model.AmountPurchasedTotal_Discount = Var(model.Streams, model.TimePeriods, bounds=get_Discount_Total_bounds) @@ -227,6 +341,22 @@ def get_Discount_Total_bounds(model, j, t): # Amount purchased below min amount for discount under discount contract # spd1(j, t) in GAMS def get_Discount_BelowMin_bounds(model, j, t): + """_summary_ + + Parameters + ---------- + model : Pyomo.AbstractModel + Pyomo abstract model for medium-term purchasing contracts problem. + j : _type_ + _description_ + t : Index + Time period. + + Returns + ------- + _type_ + _description_ + """ return (0, model.AmountPurchasedBelowMinUB_Discount[j,t]) model.AmountPurchasedBelowMin_Discount = Var(model.Streams, model.TimePeriods, bounds=get_Discount_BelowMin_bounds) @@ -234,6 +364,22 @@ def get_Discount_BelowMin_bounds(model, j, t): # spd2(j, t) in GAMS # Amount purchased above min amount for discount under discount contract def get_Discount_AboveMin_bounds(model, j, t): + """_summary_ + + Parameters + ---------- + model : Pyomo.AbstractModel + Pyomo abstract model for medium-term purchasing contracts problem. + j : _type_ + _description_ + t : Index + Time period. + + Returns + ------- + _type_ + _description_ + """ return (0, model.AmountPurchasedBelowMinUB_Discount[j,t]) model.AmountPurchasedAboveMin_Discount = Var(model.Streams, model.TimePeriods, bounds=get_Discount_AboveMin_bounds) @@ -241,6 +387,22 @@ def get_Discount_AboveMin_bounds(model, j, t): # Amount purchased under bulk contract # spb(j, t) in GAMS def get_bulk_bounds(model, j, t): + """_summary_ + + Parameters + ---------- + model : Pyomo.AbstractModel + Pyomo abstract model for medium-term purchasing contracts problem. + j : _type_ + _description_ + t : Index + Time period. + + Returns + ------- + _type_ + _description_ + """ return (0, model.AmountPurchasedUB_Bulk[j,t]) model.AmountPurchased_Bulk = Var(model.Streams, model.TimePeriods, bounds=get_bulk_bounds) @@ -248,6 +410,22 @@ def get_bulk_bounds(model, j, t): # spl(j, t) in GAMS # Amount purchased under Fixed Duration contract def get_FD_bounds(model, j, t): + """_summary_ + + Parameters + ---------- + model : Pyomo.AbstractModel + Pyomo abstract model for medium-term purchasing contracts problem. + j : _type_ + _description_ + t : Index + Time period. + + Returns + ------- + _type_ + _description_ + """ return (0, model.AmountPurchasedUB_FD[j,t]) model.AmountPurchased_FD = Var(model.Streams, model.TimePeriods, bounds=get_FD_bounds) @@ -258,18 +436,66 @@ def get_FD_bounds(model, j, t): # costpl(j, t) in GAMS # cost of variable length contract def get_CostUBs_FD(model, j, t): + """_summary_ + + Parameters + ---------- + model : Pyomo.AbstractModel + _description_ + j : _type_ + _description_ + t : Index + Time period. + + Returns + ------- + _type_ + _description_ + """ return (0, model.CostUB_FD[j,t]) model.Cost_FD = Var(model.Streams, model.TimePeriods, bounds=get_CostUBs_FD) # costpf(j, t) in GAMS # cost of fixed duration contract def get_CostUBs_FP(model, j, t): + """_summary_ + + Parameters + ---------- + model : Pyomo.AbstractModel + Pyomo abstract model for medium-term purchasing contracts problem. + j : _type_ + _description_ + t : Index + Time period. + + Returns + ------- + _type_ + _description_ + """ return (0, model.CostUB_FP[j,t]) model.Cost_FP = Var(model.Streams, model.TimePeriods, bounds=get_CostUBs_FP) # costpd(j, t) in GAMS # cost of discount contract def get_CostUBs_Discount(model, j, t): + """_summary_ + + Parameters + ---------- + model : Pyomo.AbstractModel + Pyomo abstract model for medium-term purchasing contracts problem. + j : _type_ + _description_ + t : Index + _description_ + + Returns + ------- + _type_ + _description_ + """ return (0, model.CostUB_Discount[j,t]) model.Cost_Discount = Var(model.Streams, model.TimePeriods, bounds=get_CostUBs_Discount) @@ -277,6 +503,22 @@ def get_CostUBs_Discount(model, j, t): # costpb(j, t) in GAMS # cost of bulk contract def get_CostUBs_Bulk(model, j, t): + """_summary_ + + Parameters + ---------- + model : Pyomo.AbstractModel + Pyomo abstract model for medium-term purchasing contracts problem. + j : _type_ + _description_ + t : Index + Time period. + + Returns + ------- + _type_ + _description_ + """ return (0, model.CostUB_Bulk[j,t]) model.Cost_Bulk = Var(model.Streams, model.TimePeriods, bounds=get_CostUBs_Bulk) @@ -295,6 +537,19 @@ def get_CostUBs_Bulk(model, j, t): # Objective: maximize profit def profit_rule(model): + """ + Objective function: maximize profit + + Parameters + ---------- + model : Pyomo.AbstractModel + Pyomo abstract model for medium-term purchasing contracts problem. + + Returns + ------- + _type_ + _description_ + """ salesIncome = sum(model.Prices[j,t] * model.FlowRate[j,t] for j in model.Products for t in model.TimePeriods) purchaseCost = sum(model.Cost_FD[j,t] @@ -317,6 +572,22 @@ def profit_rule(model): # flow of raw materials is the total amount purchased (accross all contracts) def raw_material_flow_rule(model, j, t): + """_summary_ + + Parameters + ---------- + model : _type_ + _description_ + j : _type_ + _description_ + t : Index + Time period. + + Returns + ------- + _type_ + _description_ + """ return model.FlowRate[j,t] == model.AmountPurchased_FD[j,t] + \ model.AmountPurchased_FP[j,t] + model.AmountPurchased_Bulk[j,t] + \ model.AmountPurchasedTotal_Discount[j,t] @@ -324,6 +595,22 @@ def raw_material_flow_rule(model, j, t): rule=raw_material_flow_rule) def discount_amount_total_rule(model, j, t): + """_summary_ + + Parameters + ---------- + model : _type_ + _description_ + j : _type_ + _description_ + t : Index + Time period. + + Returns + ------- + _type_ + _description_ + """ return model.AmountPurchasedTotal_Discount[j,t] == \ model.AmountPurchasedBelowMin_Discount[j,t] + \ model.AmountPurchasedAboveMin_Discount[j,t] @@ -333,10 +620,38 @@ def discount_amount_total_rule(model, j, t): # mass balance equations for each node # these are specific to the process network in this example. def mass_balance_rule1(model, t): + """_summary_ + + Parameters + ---------- + model : _type_ + _description_ + t : Index + Time period. + + Returns + ------- + _type_ + _description_ + """ return model.FlowRate[1, t] == model.FlowRate[2, t] + model.FlowRate[3, t] model.mass_balance1 = Constraint(model.TimePeriods, rule=mass_balance_rule1) def mass_balance_rule2(model, t): + """_summary_ + + Parameters + ---------- + model : _type_ + _description_ + t : Index + Time period. + + Returns + ------- + _type_ + _description_ + """ return model.FlowRate[5, t] == model.FlowRate[4, t] + model.FlowRate[8,t] model.mass_balance2 = Constraint(model.TimePeriods, rule=mass_balance_rule2) @@ -345,26 +660,96 @@ def mass_balance_rule3(model, t): model.mass_balance3 = Constraint(model.TimePeriods, rule=mass_balance_rule3) def mass_balance_rule4(model, t): + """_summary_ + + Parameters + ---------- + model : _type_ + _description_ + t : Index + Time period. + + Returns + ------- + _type_ + _description_ + """ return model.FlowRate[3, t] == 10*model.FlowRate[5, t] model.mass_balance4 = Constraint(model.TimePeriods, rule=mass_balance_rule4) # process input/output constraints # these are also totally specific to the process network def process_balance_rule1(model, t): + """_summary_ + + Parameters + ---------- + model : _type_ + _description_ + t : Index + Time period. + + Returns + ------- + _type_ + _description_ + """ return model.FlowRate[9, t] == model.ProcessConstants[1] * model.FlowRate[2, t] model.process_balance1 = Constraint(model.TimePeriods, rule=process_balance_rule1) def process_balance_rule2(model, t): + """_summary_ + + Parameters + ---------- + model : _type_ + _description_ + t : Index + Time period. + + Returns + ------- + _type_ + _description_ + """ return model.FlowRate[10, t] == model.ProcessConstants[2] * \ (model.FlowRate[5, t] + model.FlowRate[3, t]) model.process_balance2 = Constraint(model.TimePeriods, rule=process_balance_rule2) def process_balance_rule3(model, t): + """_summary_ + + Parameters + ---------- + model : _type_ + _description_ + t : Index + Time period. + + Returns + ------- + _type_ + _description_ + """ return model.FlowRate[8, t] == RandomConst_Line264 * \ model.ProcessConstants[3] * model.FlowRate[7, t] model.process_balance3 = Constraint(model.TimePeriods, rule=process_balance_rule3) def process_balance_rule4(model, t): + """_summary_ + + Parameters + ---------- + model : _type_ + _description_ + t : Index + Time period. + + Returns + ------- + _type_ + _description_ + """ return model.FlowRate[11, t] == RandomConst_Line265 * \ model.ProcessConstants[3] * model.FlowRate[7, t] model.process_balance4 = Constraint(model.TimePeriods, rule=process_balance_rule4) @@ -372,14 +757,56 @@ def process_balance_rule4(model, t): # process capacity contraints # these are hardcoded based on the three processes and the process flow structure def process_capacity_rule1(model, t): + """_summary_ + + Parameters + ---------- + model : Pyomo.AbstractModel + _description_ + t : Index + Time period. + + Returns + ------- + _type_ + _description_ + """ return model.FlowRate[9, t] <= model.Capacity[1] model.process_capacity1 = Constraint(model.TimePeriods, rule=process_capacity_rule1) def process_capacity_rule2(model, t): + """_summary_ + + Parameters + ---------- + model : Pyomo.AbstractModel + _description_ + t : Index + Time period. + + Returns + ------- + _type_ + _description_ + """ return model.FlowRate[10, t] <= model.Capacity[2] model.process_capacity2 = Constraint(model.TimePeriods, rule=process_capacity_rule2) def process_capacity_rule3(model, t): + """_summary_ + + Parameters + ---------- + model : Pyomo.AbstractModel + _description_ + t : Index + Time period. + + Returns + ------- + _type_ + _description_ + """ return model.FlowRate[11, t] + model.FlowRate[8, t] <= model.Capacity[3] model.process_capacity3 = Constraint(model.TimePeriods, rule=process_capacity_rule3) @@ -387,11 +814,39 @@ def process_capacity_rule3(model, t): # again, these are hardcoded. def inventory_balance1(model, t): + """_summary_ + + Parameters + ---------- + model : Pyomo.AbstractModel + _description_ + t : Index + Time period. + + Returns + ------- + _type_ + _description_ + """ prev = 0 if t == min(model.TimePeriods) else model.InventoryLevel[12, t-1] return prev + model.FlowRate[9, t] == model.FlowRate[12, t] + model.InventoryLevel[12,t] model.inventory_balance1 = Constraint(model.TimePeriods, rule=inventory_balance1) def inventory_balance_rule2(model, t): + """_summary_ + + Parameters + ---------- + model : Pyomo.AbstractModel + _description_ + t : Index + Time period. + + Returns + ------- + _type_ + _description_ + """ if t != 1: return Constraint.Skip return model.FlowRate[10, t] + model.FlowRate[11, t] == \ @@ -399,6 +854,20 @@ def inventory_balance_rule2(model, t): model.inventory_balance2 = Constraint(model.TimePeriods, rule=inventory_balance_rule2) def inventory_balance_rule3(model, t): + """_summary_ + + Parameters + ---------- + model : Pyomo.AbstractModel + _description_ + t : Index + Time period. + + Returns + ------- + _type_ + _description_ + """ if t <= 1: return Constraint.Skip return model.InventoryLevel[13, t-1] + model.FlowRate[10, t] + \ @@ -407,30 +876,126 @@ def inventory_balance_rule3(model, t): # Max capacities of inventories def inventory_capacity_rule(model, j, t): + """_summary_ + + Parameters + ---------- + model : Pyomo.AbstractModel + _description_ + j : _type_ + _description_ + t : Index + Time period. + + Returns + ------- + _type_ + _description_ + """ return model.InventoryLevel[j,t] <= model.InventoryLevelUB[j,t] model.inventory_capacity_rule = Constraint(model.Products, model.TimePeriods, rule=inventory_capacity_rule) # Shortfall calculation def shortfall_rule(model, j, t): + """_summary_ + + Parameters + ---------- + model : Pyomo.AbstractModel + _description_ + j : _type_ + _description_ + t : Index + Time period. + + Returns + ------- + _type_ + _description_ + """ return model.Shortfall[j, t] == model.SupplyAndDemandUBs[j, t] - model.FlowRate[j,t] model.shortfall = Constraint(model.Products, model.TimePeriods, rule=shortfall_rule) # maximum shortfall allowed def shortfall_max_rule(model, j, t): + """_summary_ + + Parameters + ---------- + model : Pyomo.AbstractModel + _description_ + j : _type_ + _description_ + t : Index + Time period. + + Returns + ------- + _type_ + _description_ + """ return model.Shortfall[j, t] <= model.ShortfallUB[j, t] model.shortfall_max = Constraint(model.Products, model.TimePeriods, rule=shortfall_max_rule) # maxiumum capacities of suppliers def supplier_capacity_rule(model, j, t): + """_summary_ + + Parameters + ---------- + model : Pyomo.AbstractModel + _description_ + j : _type_ + _description_ + t : Index + Time period. + + Returns + ------- + _type_ + _description_ + """ return model.FlowRate[j, t] <= model.SupplyAndDemandUBs[j, t] model.supplier_capacity = Constraint(model.RawMaterials, model.TimePeriods, rule=supplier_capacity_rule) # demand upper bound def demand_UB_rule(model, j, t): + """_summary_ + + Parameters + ---------- + model : Pyomo.AbstractModel + _description_ + j : _type_ + _description_ + t : Index + Time period. + + Returns + ------- + _type_ + _description_ + """ return model.FlowRate[j, t] <= model.SupplyAndDemandUBs[j,t] model.demand_UB = Constraint(model.Products, model.TimePeriods, rule=demand_UB_rule) # demand lower bound def demand_LB_rule(model, j, t): + """_summary_ + + Parameters + ---------- + model : Pyomo.AbstractModel + _description_ + j : _type_ + _description_ + t : Index + Time period. + + Returns + ------- + _type_ + _description_ + """ return model.FlowRate[j, t] >= model.DemandLB[j,t] model.demand_LB = Constraint(model.Products, model.TimePeriods, rule=demand_LB_rule) @@ -439,6 +1004,19 @@ def demand_LB_rule(model, j, t): # Disjunction for Fixed Price contract buying options def FP_contract_disjunct_rule(disjunct, j, t, buy): + """_summary_ + + Parameters + ---------- + disjunct : Index + _description_ + j : _type_ + _description_ + t : Index + Time period. + buy : _type_ + _description_ + """ model = disjunct.model() if buy: disjunct.c = Constraint(expr=model.AmountPurchased_FP[j,t] <= MAX_AMOUNT_FP) @@ -449,12 +1027,44 @@ def FP_contract_disjunct_rule(disjunct, j, t, buy): # Fixed price disjunction def FP_contract_rule(model, j, t): + """_summary_ + + Parameters + ---------- + model : Pyomo.AbstractModel + _description_ + j : _type_ + _description_ + t : Index + Time period. + + Returns + ------- + _type_ + _description_ + """ return [model.FP_contract_disjunct[j,t,buy] for buy in model.BuyFPContract] model.FP_disjunction = Disjunction(model.RawMaterials, model.TimePeriods, rule=FP_contract_rule) - # cost constraint for fixed price contract (independent contraint) + # cost constraint for fixed price contract (independent constraint) def FP_contract_cost_rule(model, j, t): + """_summary_ + + Parameters + ---------- + model : Pyomo.AbstractModel + _description_ + j : _type_ + _description_ + t : Index + Time period. + + Returns + ------- + _type_ + _description_ + """ return model.Cost_FP[j,t] == model.AmountPurchased_FP[j,t] * \ model.Prices[j,t] model.FP_contract_cost = Constraint(model.RawMaterials, model.TimePeriods, @@ -465,6 +1075,24 @@ def FP_contract_cost_rule(model, j, t): # Disjunction for Discount contract def discount_contract_disjunct_rule(disjunct, j, t, buy): + """_summary_ + + Parameters + ---------- + disjunct : Index + _description_ + j : _type_ + _description_ + t : Index + _description_ + buy : _type_ + _description_ + + Raises + ------ + RuntimeError + _description_ + """ model = disjunct.model() if buy == 'BelowMin': disjunct.belowMin = Constraint( @@ -490,6 +1118,22 @@ def discount_contract_disjunct_rule(disjunct, j, t, buy): # Discount contract disjunction def discount_contract_rule(model, j, t): + """_summary_ + + Parameters + ---------- + model : Pyomo.AbstractModel + _description_ + j : _type_ + _description_ + t : Index + Time period. + + Returns + ------- + _type_ + _description_ + """ return [model.discount_contract_disjunct[j,t,buy] \ for buy in model.BuyDiscountContract] model.discount_contract = Disjunction(model.RawMaterials, model.TimePeriods, @@ -497,6 +1141,22 @@ def discount_contract_rule(model, j, t): # cost constraint for discount contract (independent constraint) def discount_cost_rule(model, j, t): + """_summary_ + + Parameters + ---------- + model : Pyomo.AbstractModel + _description_ + j : _type_ + _description_ + t : Index + Time period. + + Returns + ------- + _type_ + _description_ + """ return model.Cost_Discount[j,t] == model.RegPrice_Discount[j,t] * \ model.AmountPurchasedBelowMin_Discount[j,t] + \ model.DiscountPrice_Discount[j,t] * model.AmountPurchasedAboveMin_Discount[j,t] @@ -508,6 +1168,24 @@ def discount_cost_rule(model, j, t): # Bulk contract buying options disjunct def bulk_contract_disjunct_rule(disjunct, j, t, buy): + """_summary_ + + Parameters + ---------- + disjunct : Index + _description_ + j : _type_ + _description_ + t : Index + Time period. + buy : _type_ + _description_ + + Raises + ------ + RuntimeError + _description_ + """ model = disjunct.model() if buy == 'BelowMin': disjunct.amount = Constraint( @@ -531,6 +1209,22 @@ def bulk_contract_disjunct_rule(disjunct, j, t, buy): # Bulk contract disjunction def bulk_contract_rule(model, j, t): + """_summary_ + + Parameters + ---------- + model : Pyomo.AbstractModel + _description_ + j : _type_ + _description_ + t : Index + _description_ + + Returns + ------- + _type_ + _description_ + """ return [model.bulk_contract_disjunct[j,t,buy] for buy in model.BuyBulkContract] model.bulk_contract = Disjunction(model.RawMaterials, model.TimePeriods, rule=bulk_contract_rule) @@ -539,53 +1233,97 @@ def bulk_contract_rule(model, j, t): # FIXED DURATION CONTRACT def FD_1mo_contract(disjunct, j, t): - model = disjunct.model() - disjunct.amount1 = Constraint(expr=model.AmountPurchased_FD[j,t] >= \ - MIN_AMOUNT_FD_1MONTH) - disjunct.price1 = Constraint(expr=model.Cost_FD[j,t] == \ - model.Prices_Length[j,1,t] * model.AmountPurchased_FD[j,t]) + """_summary_ + + Parameters + ---------- + disjunct : Index + _description_ + j : _type_ + _description_ + t : Index + Time period. + """ + model = disjunct.model() + disjunct.amount1 = Constraint(expr=model.AmountPurchased_FD[j,t] >= \ + MIN_AMOUNT_FD_1MONTH) + disjunct.price1 = Constraint(expr=model.Cost_FD[j,t] == \ + model.Prices_Length[j,1,t] * model.AmountPurchased_FD[j,t]) model.FD_1mo_contract = Disjunct( model.RawMaterials, model.TimePeriods, rule=FD_1mo_contract) def FD_2mo_contract(disjunct, j, t): - model = disjunct.model() - disjunct.amount1 = Constraint(expr=model.AmountPurchased_FD[j,t] >= \ - model.MinAmount_Length[j,2]) - disjunct.price1 = Constraint(expr=model.Cost_FD[j,t] == \ - model.Prices_Length[j,2,t] * model.AmountPurchased_FD[j,t]) + """_summary_ + + Parameters + ---------- + disjunct : Index + _description_ + j : _type_ + _description_ + t : Index + Time period. + """ + model = disjunct.model() + disjunct.amount1 = Constraint(expr=model.AmountPurchased_FD[j,t] >= \ + model.MinAmount_Length[j,2]) + disjunct.price1 = Constraint(expr=model.Cost_FD[j,t] == \ + model.Prices_Length[j,2,t] * model.AmountPurchased_FD[j,t]) # only enforce these if we aren't in the last time period - if t < model.TimePeriods[-1]: - disjunct.amount2 = Constraint(expr=model.AmountPurchased_FD[j, t+1] >= \ - model.MinAmount_Length[j,2]) - disjunct.price2 = Constraint(expr=model.Cost_FD[j,t+1] == \ - model.Prices_Length[j,2,t] * model.AmountPurchased_FD[j, t+1]) + if t < model.TimePeriods[-1]: + disjunct.amount2 = Constraint(expr=model.AmountPurchased_FD[j, t+1] >= \ + model.MinAmount_Length[j,2]) + disjunct.price2 = Constraint(expr=model.Cost_FD[j,t+1] == \ + model.Prices_Length[j,2,t] * model.AmountPurchased_FD[j, t+1]) model.FD_2mo_contract = Disjunct( model.RawMaterials, model.TimePeriods, rule=FD_2mo_contract) def FD_3mo_contract(disjunct, j, t): - model = disjunct.model() - # NOTE: I think there is a mistake in the GAMS file in line 327. - # they use the bulk minamount rather than the length one. - #I am doing the same here for validation purposes. - disjunct.amount1 = Constraint(expr=model.AmountPurchased_FD[j,t] >= \ - model.MinAmount_Bulk[j,3]) - disjunct.cost1 = Constraint(expr=model.Cost_FD[j,t] == \ - model.Prices_Length[j,3,t] * model.AmountPurchased_FD[j,t]) - # check we aren't in one of the last two time periods - if t < model.TimePeriods[-1]: - disjunct.amount2 = Constraint(expr=model.AmountPurchased_FD[j,t+1] >= \ - model.MinAmount_Length[j,3]) - disjunct.cost2 = Constraint(expr=model.Cost_FD[j,t+1] == \ - model.Prices_Length[j,3,t] * model.AmountPurchased_FD[j,t+1]) - if t < model.TimePeriods[-2]: - disjunct.amount3 = Constraint(expr=model.AmountPurchased_FD[j,t+2] >= \ - model.MinAmount_Length[j,3]) - disjunct.cost3 = Constraint(expr=model.Cost_FD[j,t+2] == \ - model.Prices_Length[j,3,t] * model.AmountPurchased_FD[j,t+2]) + """_summary_ + + Parameters + ---------- + disjunct : Index + _description_ + j : _type_ + _description_ + t : Index + Time period. + """ + model = disjunct.model() + # NOTE: I think there is a mistake in the GAMS file in line 327. + # they use the bulk minamount rather than the length one. + #I am doing the same here for validation purposes. + disjunct.amount1 = Constraint(expr=model.AmountPurchased_FD[j,t] >= \ + model.MinAmount_Bulk[j,3]) + disjunct.cost1 = Constraint(expr=model.Cost_FD[j,t] == \ + model.Prices_Length[j,3,t] * model.AmountPurchased_FD[j,t]) + # check we aren't in one of the last two time periods + if t < model.TimePeriods[-1]: + disjunct.amount2 = Constraint(expr=model.AmountPurchased_FD[j,t+1] >= \ + model.MinAmount_Length[j,3]) + disjunct.cost2 = Constraint(expr=model.Cost_FD[j,t+1] == \ + model.Prices_Length[j,3,t] * model.AmountPurchased_FD[j,t+1]) + if t < model.TimePeriods[-2]: + disjunct.amount3 = Constraint(expr=model.AmountPurchased_FD[j,t+2] >= \ + model.MinAmount_Length[j,3]) + disjunct.cost3 = Constraint(expr=model.Cost_FD[j,t+2] == \ + model.Prices_Length[j,3,t] * model.AmountPurchased_FD[j,t+2]) model.FD_3mo_contract = Disjunct( - model.RawMaterials, model.TimePeriods, rule=FD_3mo_contract) + model.RawMaterials, model.TimePeriods, rule=FD_3mo_contract) def FD_no_contract(disjunct, j, t): + """_summary_ + + Parameters + ---------- + disjunct : Index + _description_ + j : _type_ + _description_ + t : Index + Time period. + """ model = disjunct.model() disjunct.amount1 = Constraint(expr=model.AmountPurchased_FD[j,t] == 0) disjunct.cost1 = Constraint(expr=model.Cost_FD[j,t] == 0) @@ -596,10 +1334,26 @@ def FD_no_contract(disjunct, j, t): disjunct.amount3 = Constraint(expr=model.AmountPurchased_FD[j,t+2] == 0) disjunct.cost3 = Constraint(expr=model.Cost_FD[j,t+2] == 0) model.FD_no_contract = Disjunct( - model.RawMaterials, model.TimePeriods, rule=FD_no_contract) + model.RawMaterials, model.TimePeriods, rule=FD_no_contract) def FD_contract(model, j, t): - return [ model.FD_1mo_contract[j,t], model.FD_2mo_contract[j,t], + """_summary_ + + Parameters + ---------- + model : Pyomo.AbstractModel + _description_ + j : _type_ + _description_ + t : Index + Time period. + + Returns + ------- + _type_ + _description_ + """ + return [ model.FD_1mo_contract[j,t], model.FD_2mo_contract[j,t], model.FD_3mo_contract[j,t], model.FD_no_contract[j,t], ] model.FD_contract = Disjunction(model.RawMaterials, model.TimePeriods, rule=FD_contract) @@ -608,6 +1362,14 @@ def FD_contract(model, j, t): def build_concrete(): + """ + Build a concrete model applying the data of the medium-term purchasing contracts problem on build_model(). + + Returns + ------- + Pyomo.ConcreteModel + A concrete model for the medium-term purchasing contracts problem. + """ return build_model().create_instance(join(this_file_dir(), 'med_term_purchasing.dat')) From cec65356b983a3d3c5094dc551cf82c6957c2573 Mon Sep 17 00:00:00 2001 From: Albert Lee Date: Thu, 4 Apr 2024 13:03:23 -0400 Subject: [PATCH 22/99] Redocumentation of the jobshop into Numpy Format --- gdplib/pyomo_examples/jobshop.py | 153 ++++++++++++++++++++----------- 1 file changed, 98 insertions(+), 55 deletions(-) diff --git a/gdplib/pyomo_examples/jobshop.py b/gdplib/pyomo_examples/jobshop.py index 78b50f8..d85268d 100644 --- a/gdplib/pyomo_examples/jobshop.py +++ b/gdplib/pyomo_examples/jobshop.py @@ -35,19 +35,26 @@ def build_model(): """ - Build the jobshop scheduling model, which has a set of jobs which must be processed in sequence of stages but not all jobs require all stages. - A zero wait transfer policy is assumed between stages. - To obtain a feasible solution it is necessary to eliminate all clashes between jobs. - It requires that no two jobs be performed at any stage at any time. The objective is to minimize the makespan, the time to complete all jobs. - - References: - Raman & Grossmann, Modelling and computational techniques for logic based integer programming, Computers and Chemical Engineering 18, 7, p.563-578, 1994. - Aldo Vecchietti, LogMIP User's Manual, http://www.logmip.ceride.gov.ar/, 2007 + Build and return a jobshop scheduling model. + + This function constructs a Pyomo abstract model for jobshop scheduling, aiming to minimize the makespan. + It includes sets of jobs and stages, with the assumption of a zero-wait policy between stages. + The model enforces constraints to avoid job clashes at any stage and minimizes the total completion time. + - Args: None + Parameters + ---------- + None - Returns: - AbstractModel: jobshop scheduling model, which has a set of jobs which must be processed in sequence of stages but not all jobs require all stages. + Returns + ------- + model : Pyomo.AbstractModel + The jobshop scheduling model, which has a set of jobs which must be processed in sequence of stages but not all jobs require all stages. + + References + ---------- + Raman & Grossmann, Modelling and computational techniques for logic based integer programming, Computers and Chemical Engineering 18, 7, p.563-578, 1994. + Aldo Vecchietti, LogMIP User's Manual, http://www.logmip.ceride.gov.ar/, 2007 """ model = AbstractModel() @@ -66,13 +73,18 @@ def t_bounds(model, I): """ Calculate the time bounds for the start time of each job in a scheduling model. - Args: - model (Pyomo.Abstractmodel): job shop scheduling model, which has a set of jobs which must be processed in sequence of stages but not all jobs require all stages. - A zero wait transfer policy is assumed between stages. - I (str): job index + Parameters + ---------- + model : Pyomo.Abstractmodel + The job shop scheduling model, which has a set of jobs which must be processed in sequence of stages but not all jobs require all stages. + A zero wait transfer policy is assumed between stages. + I : str + The index of the job index - Returns: - tuple: (lower bound, upper bound) for the start time of each job in a scheduling model. + Returns + ------- + tuple + A tuple containing the lower and upper bounds for the start time of the job. """ return (0, sum(value(model.tau[idx]) for idx in model.tau)) @@ -86,17 +98,25 @@ def t_bounds(model, I): # Auto-generate the L set (potential collisions between 2 jobs at any stage. def _L_filter(model, I, K, J): """ - Filter for the L set (potential collisions between 2 jobs at any stage. - - Args: - model (Pyomo.Abstractmodel): jobshop scheduling model, which has a set of jobs which must be processed in sequence of stages but not all jobs require all stages. - A zero wait transfer policy is assumed between stages. - I (str): job index - K (str): job index that is greater than I (After I) - J (int): stage index + Filter for the L set (potential collisions between 2 jobs at any stage). - Returns: - expression: True if I < K and the parameters, model.tau[I,J] and model.tau[K,J] + Parameters + ---------- + model : Pyomo.Abstractmodel + The jobshop scheduling model, which has a set of jobs which must be processed in sequence of stages but not all jobs require all stages. + A zero wait transfer policy is assumed between stages. + I : str + job index + K : str + job index that is greater than I (After I) + J : int + stage index + + Returns + ------- + bool + Returns `True` if job `I` precedes job `K` and both jobs require processing at stage `J`, indicating a potential scheduling clash. + 'False' otherwise. """ return I < K and model.tau[I, J] and model.tau[K, J] @@ -113,13 +133,18 @@ def _feas(model, I): """ This function creates a constraint that ensures the makespan is greater than the sum of the start time of every job and that job's total duration. - Args: - model (Pyomo.Abstractmodel): jobshop scheduling model, which has a set of jobs which must be processed in sequence of stages but not all jobs require all stages. + Parameters + ---------- + model : Pyomo.Abstractmodel + The jobshop scheduling model, which has a set of jobs which must be processed in sequence of stages but not all jobs require all stages. A zero wait transfer policy is assumed between stages. - I (str): job index + I : str + job index - Returns: - expression: True if the makespan is greater than the sum of the start time of every job and that job's total duration. + Returns + ------- + Pyomo.Constraint.Expression + A constraint expression that ensures the makespan is greater than or equal to the sum of the start time and total duration for the job. """ return model.ms >= model.t[I] + sum(model.tau[I, M] for M in model.STAGES) @@ -137,15 +162,23 @@ def _NoClash(disjunct, I, K, J, IthenK): """ Disjunctions to prevent clashes at a stage: This creates a set of disjunct pairs: one if job I occurs before job K and the other if job K occurs before job I. - Args: - model (Pyomo.Disjunction): The disjunction of the model. - I (str): job index - K (str): job index that is greater than I (After I) - J (int): stage index - IthenK (bool): True if I occurs before K, False if K occurs before I - - Returns: - None, but creates a disjunction to prevent clashes at a stage. + Parameters + ---------- + model : Pyomo.Disjunct + The disjunction of the model. + I : str + job index + K : str + job index that is greater than I (After I) + J : int + stage index + IthenK : bool + A boolean flag indicating if job I is scheduled before job K (`True`) or vice versa (`False`). + + Returns + ------- + None + However, a constraint is added to the disjunction to prevent clashes at a stage. """ model = disjunct.model() lhs = model.t[I] + sum([M < J and model.tau[I, M] or 0 for M in model.STAGES]) @@ -167,14 +200,21 @@ def _disj(model, I, K, J): """ Define the disjunctions: either job I occurs before K or K before I - Args: - model (Pyomo.Abstractmodel): jobshop scheduling model, which has a set of jobs which must be processed in sequence of stages but not all jobs require all stages. - I (str): job index - K (str): job index that is greater than I (After I) - J (int): stage index - - Returns: - list: list of disjunctions to prevent clashes at a stage. + Parameters + ---------- + model : Pyomo.Abstractmodel + jobshop scheduling model, which has a set of jobs which must be processed in sequence of stages but not all jobs require all stages. + I : str + job index + K : str + job index that is greater than I (After I) + J : int + stage index + + Returns + ------- + list of Pyomo.Disjunct + A list of disjunctions for the given jobs and stage, enforcing that one job must precede the other to avoid clashes. """ return [model.NoClash[I, K, J, IthenK] for IthenK in model.I_BEFORE_K] @@ -194,14 +234,17 @@ def _disj(model, I, K, J): def build_small_concrete(): """ Build a small jobshop scheduling model for testing purposes. - The AbstractModel is instantiated with the data in the file jobshop-small.dat turning it into a ConcreteModel. + The AbstractModel is instantiated with the data from 'jobshop-small.dat' turning it into a ConcreteModel. - Args: - None, but the data file jobshop-small.dat must be in the same directory as this file. + Parameters + ---------- + None + However the data file jobshop-small.dat must be in the same directory as this file. - Returns: - ConcreteModel: jobshop scheduling model, which has a set of jobs which must be processed in sequence of stages but not all jobs require all stages. - A zero wait transfer policy is assumed between stages. + Returns + ------- + ConcreteModel : Pyomo.ConcreteModel + A concrete instance of the jobshop scheduling model populated with data from 'jobshop-small.dat', ready for optimization. """ return build_model().create_instance(join(this_file_dir(), 'jobshop-small.dat')) From 58228c6e46fdafc157f4a492ee1e1892bddabb94 Mon Sep 17 00:00:00 2001 From: Albert Lee Date: Thu, 4 Apr 2024 13:04:48 -0400 Subject: [PATCH 23/99] Black Formatting. --- gdplib/pyomo_examples/jobshop.py | 36 ++++++++++++++++---------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/gdplib/pyomo_examples/jobshop.py b/gdplib/pyomo_examples/jobshop.py index d85268d..25ef303 100644 --- a/gdplib/pyomo_examples/jobshop.py +++ b/gdplib/pyomo_examples/jobshop.py @@ -37,15 +37,15 @@ def build_model(): """ Build and return a jobshop scheduling model. - This function constructs a Pyomo abstract model for jobshop scheduling, aiming to minimize the makespan. - It includes sets of jobs and stages, with the assumption of a zero-wait policy between stages. + This function constructs a Pyomo abstract model for jobshop scheduling, aiming to minimize the makespan. + It includes sets of jobs and stages, with the assumption of a zero-wait policy between stages. The model enforces constraints to avoid job clashes at any stage and minimizes the total completion time. - + Parameters ---------- None - + Returns ------- model : Pyomo.AbstractModel @@ -75,7 +75,7 @@ def t_bounds(model, I): Parameters ---------- - model : Pyomo.Abstractmodel + model : Pyomo.Abstractmodel The job shop scheduling model, which has a set of jobs which must be processed in sequence of stages but not all jobs require all stages. A zero wait transfer policy is assumed between stages. I : str @@ -102,12 +102,12 @@ def _L_filter(model, I, K, J): Parameters ---------- - model : Pyomo.Abstractmodel + model : Pyomo.Abstractmodel The jobshop scheduling model, which has a set of jobs which must be processed in sequence of stages but not all jobs require all stages. A zero wait transfer policy is assumed between stages. I : str job index - K : str + K : str job index that is greater than I (After I) J : int stage index @@ -115,7 +115,7 @@ def _L_filter(model, I, K, J): Returns ------- bool - Returns `True` if job `I` precedes job `K` and both jobs require processing at stage `J`, indicating a potential scheduling clash. + Returns `True` if job `I` precedes job `K` and both jobs require processing at stage `J`, indicating a potential scheduling clash. 'False' otherwise. """ return I < K and model.tau[I, J] and model.tau[K, J] @@ -138,7 +138,7 @@ def _feas(model, I): model : Pyomo.Abstractmodel The jobshop scheduling model, which has a set of jobs which must be processed in sequence of stages but not all jobs require all stages. A zero wait transfer policy is assumed between stages. - I : str + I : str job index Returns @@ -164,11 +164,11 @@ def _NoClash(disjunct, I, K, J, IthenK): Parameters ---------- - model : Pyomo.Disjunct + model : Pyomo.Disjunct The disjunction of the model. - I : str + I : str job index - K : str + K : str job index that is greater than I (After I) J : int stage index @@ -202,18 +202,18 @@ def _disj(model, I, K, J): Parameters ---------- - model : Pyomo.Abstractmodel + model : Pyomo.Abstractmodel jobshop scheduling model, which has a set of jobs which must be processed in sequence of stages but not all jobs require all stages. - I : str + I : str job index - K : str + K : str job index that is greater than I (After I) - J : int + J : int stage index Returns ------- - list of Pyomo.Disjunct + list of Pyomo.Disjunct A list of disjunctions for the given jobs and stage, enforcing that one job must precede the other to avoid clashes. """ return [model.NoClash[I, K, J, IthenK] for IthenK in model.I_BEFORE_K] @@ -243,7 +243,7 @@ def build_small_concrete(): Returns ------- - ConcreteModel : Pyomo.ConcreteModel + ConcreteModel : Pyomo.ConcreteModel A concrete instance of the jobshop scheduling model populated with data from 'jobshop-small.dat', ready for optimization. """ return build_model().create_instance(join(this_file_dir(), 'jobshop-small.dat')) From 3daa9cb2519e6e6be4ffed58d4449a75675bf787 Mon Sep 17 00:00:00 2001 From: Albert Lee Date: Thu, 4 Apr 2024 18:30:23 -0400 Subject: [PATCH 24/99] Manage Comments; Fixed Documentation; Fixed into Numpy Format. --- gdplib/pyomo_examples/disease_model.py | 1926 ++++++++++++++++++++++-- 1 file changed, 1792 insertions(+), 134 deletions(-) diff --git a/gdplib/pyomo_examples/disease_model.py b/gdplib/pyomo_examples/disease_model.py index 65aad88..269b69c 100644 --- a/gdplib/pyomo_examples/disease_model.py +++ b/gdplib/pyomo_examples/disease_model.py @@ -20,28 +20,1598 @@ from pyomo.gdp import * import math + def build_model(): """ - Builds the model for the SIR disease model using a low/high transmission parameter + Builds the model for the SIR disease model using a low/high transmission parameter. - Arg: - None + The model simulates the spread of an infectious disease over a series of bi-weekly periods, using a disjunctive programming approach to account for variations in disease transmission rates. + + Parameters + ---------- + None - Returns: - model(Pyomo.ConcreteModel): Pyomo model object that represent the SIR disease model using a low/high transmission parameter. + Returns + ------- + model : Pyomo.ConcreteModel + Pyomo model object that represent the SIR disease model using a low/high transmission parameter. """ # import data # Population Data - pop = [ 15.881351, 15.881339, 15.881320, 15.881294, 15.881261, 15.881223, 15.881180, 15.881132, 15.881079, 15.881022, 15.880961, 15.880898, 15.880832, 15.880764, 15.880695, 15.880624, 15.880553, 15.880480, 15.880409, 15.880340, 15.880270, 15.880203, 15.880138, 15.880076, 15.880016, 15.879960, 15.879907, 15.879852, 15.879799, 15.879746, 15.879693, 15.879638, 15.879585, 15.879531, 15.879477, 15.879423, 15.879370, 15.879315, 15.879262, 15.879209, 15.879155, 15.879101, 15.879048, 15.878994, 15.878940, 15.878886, 15.878833, 15.878778, 15.878725, 15.878672, 15.878618, 15.878564, 15.878510, 15.878457, 15.878402, 15.878349, 15.878295, 15.878242, 15.878187, 15.878134, 15.878081, 15.878026, 15.877973, 15.877919, 15.877864, 15.877811, 15.877758, 15.877704, 15.877650, 15.877596, 15.877543, 15.877488, 15.877435, 15.877381, 15.877326, 15.877273, 15.877220, 15.877166, 15.877111, 15.877058, 15.877005, 15.876950, 15.876896, 15.876843, 15.876789, 15.876735, 15.876681, 15.876628, 15.876573, 15.876520, 15.876466, 15.876411, 15.876358, 15.876304, 15.876251, 15.876196, 15.876143, 15.876089, 15.876034, 15.875981, 15.875927, 15.875872, 15.875819, 15.875765, 15.875712, 15.875657, 15.875604, 15.875550, 15.875495, 15.875442, 15.875388, 15.875335, 15.875280, 15.875226, 15.875173, 15.875118, 15.875064, 15.875011, 15.874956, 15.874902, 15.874849, 15.874795, 15.874740, 15.874687, 15.874633, 15.874578, 15.874525, 15.874471, 15.874416, 15.874363, 15.874309, 15.874256, 15.874201, 15.874147, 15.874094, 15.874039, 15.873985, 15.873931, 15.873878, 15.873823, 15.873769, 15.873716, 15.873661, 15.873607, 15.873554, 15.873499, 15.873445, 15.873391, 15.873338, 15.873283, 15.873229, 15.873175, 15.873121, 15.873067, 15.873013, 15.872960, 15.872905, 15.872851, 15.872797, 15.872742, 15.872689, 15.872635, 15.872580, 15.872526, 15.872473, 15.872419, 15.872364, 15.872310, 15.872256, 15.872202, 15.872148, 15.872094, 15.872039, 15.871985, 15.871932, 15.871878, 15.871823, 15.871769, 15.871715, 15.871660, 15.871607, 15.871553, 15.871499, 15.871444, 15.871390, 15.871337, 15.871282, 15.871228, 15.871174, 15.871119, 15.871065, 15.871012, 15.870958, 15.870903, 15.870849, 15.870795, 15.870740, 15.870686, 15.870633, 15.870577, 15.870524, 15.870470, 15.870416, 15.870361, 15.870307, 15.870253, 15.870198, 15.870144, 15.870091, 15.870037, 15.869982, 15.869928, 15.869874, 15.869819, 15.869765, 15.869711, 15.869656, 15.869602, 15.869548, 15.869495, 15.869439, 15.869386, 15.869332, 15.869277, 15.869223, 15.869169, 15.869114, 15.869060, 15.869006, 15.868952, 15.868897, 15.868843, 15.868789, 15.868734, 15.868679, 15.868618, 15.868556, 15.868489, 15.868421, 15.868351, 15.868280, 15.868208, 15.868134, 15.868063, 15.867991, 15.867921, 15.867852, 15.867785, 15.867721, 15.867659, 15.867601, 15.867549, 15.867499, 15.867455, 15.867416, 15.867383, 15.867357, 15.867338, 15.867327, 15.867321, 15.867327, 15.867338, 15.867359, 15.867386, 15.867419, 15.867459, 15.867505, 15.867555, 15.867610, 15.867671, 15.867734, 15.867801, 15.867869, 15.867941, 15.868012, 15.868087, 15.868161, 15.868236, 15.868310, 15.868384, 15.868457, 15.868527, 15.868595, 15.868661, 15.868722, 15.868780, 15.868837, 15.868892, 15.868948, 15.869005, 15.869061, 15.869116, 15.869173, 15.869229, 15.869284, 15.869341, 15.869397, 15.869452, 15.869509, 15.869565, 15.869620, 15.869677, 15.869733, 15.869788, 15.869845, 15.869901, 15.869956, 15.870012, 15.870069, 15.870124, 15.870180, 15.870237, 15.870292, 15.870348, 15.870405, 15.870461, 15.870516, 15.870572, 15.870629, 15.870684, 15.870740, 15.870796, 15.870851, 15.870908, 15.870964, 15.871019, 15.871076, 15.871132, 15.871187, 15.871243, 15.871300, 15.871355, 15.871411, 15.871467, 15.871522, 15.871579, 15.871635, 15.871691, 15.871746, 15.871802, 15.871859, 15.871914, 15.871970, 15.872026, 15.872081, 15.872138, 15.872194, 15.872249, 15.872305, 15.872361, 15.872416, 15.872473, 15.872529, 15.872584, 15.872640, 15.872696, 15.872751, 15.872807, 15.872864, 15.872919, 15.872975, 15.873031, 15.873087, 15.873142, 15.873198, 15.873255, 15.873310, 15.873366, 15.873422, 15.873477, 15.873533, 15.873589, 15.873644, 15.873700, 15.873757, 15.873811, 15.873868, 15.873924, 15.873979, 15.874035, 15.874091, 15.874146, 15.874202, 15.874258, 15.874313, 15.874369, 15.874425, 15.874481, 15.874536, 15.874592] + pop = [ + 15.881351, + 15.881339, + 15.881320, + 15.881294, + 15.881261, + 15.881223, + 15.881180, + 15.881132, + 15.881079, + 15.881022, + 15.880961, + 15.880898, + 15.880832, + 15.880764, + 15.880695, + 15.880624, + 15.880553, + 15.880480, + 15.880409, + 15.880340, + 15.880270, + 15.880203, + 15.880138, + 15.880076, + 15.880016, + 15.879960, + 15.879907, + 15.879852, + 15.879799, + 15.879746, + 15.879693, + 15.879638, + 15.879585, + 15.879531, + 15.879477, + 15.879423, + 15.879370, + 15.879315, + 15.879262, + 15.879209, + 15.879155, + 15.879101, + 15.879048, + 15.878994, + 15.878940, + 15.878886, + 15.878833, + 15.878778, + 15.878725, + 15.878672, + 15.878618, + 15.878564, + 15.878510, + 15.878457, + 15.878402, + 15.878349, + 15.878295, + 15.878242, + 15.878187, + 15.878134, + 15.878081, + 15.878026, + 15.877973, + 15.877919, + 15.877864, + 15.877811, + 15.877758, + 15.877704, + 15.877650, + 15.877596, + 15.877543, + 15.877488, + 15.877435, + 15.877381, + 15.877326, + 15.877273, + 15.877220, + 15.877166, + 15.877111, + 15.877058, + 15.877005, + 15.876950, + 15.876896, + 15.876843, + 15.876789, + 15.876735, + 15.876681, + 15.876628, + 15.876573, + 15.876520, + 15.876466, + 15.876411, + 15.876358, + 15.876304, + 15.876251, + 15.876196, + 15.876143, + 15.876089, + 15.876034, + 15.875981, + 15.875927, + 15.875872, + 15.875819, + 15.875765, + 15.875712, + 15.875657, + 15.875604, + 15.875550, + 15.875495, + 15.875442, + 15.875388, + 15.875335, + 15.875280, + 15.875226, + 15.875173, + 15.875118, + 15.875064, + 15.875011, + 15.874956, + 15.874902, + 15.874849, + 15.874795, + 15.874740, + 15.874687, + 15.874633, + 15.874578, + 15.874525, + 15.874471, + 15.874416, + 15.874363, + 15.874309, + 15.874256, + 15.874201, + 15.874147, + 15.874094, + 15.874039, + 15.873985, + 15.873931, + 15.873878, + 15.873823, + 15.873769, + 15.873716, + 15.873661, + 15.873607, + 15.873554, + 15.873499, + 15.873445, + 15.873391, + 15.873338, + 15.873283, + 15.873229, + 15.873175, + 15.873121, + 15.873067, + 15.873013, + 15.872960, + 15.872905, + 15.872851, + 15.872797, + 15.872742, + 15.872689, + 15.872635, + 15.872580, + 15.872526, + 15.872473, + 15.872419, + 15.872364, + 15.872310, + 15.872256, + 15.872202, + 15.872148, + 15.872094, + 15.872039, + 15.871985, + 15.871932, + 15.871878, + 15.871823, + 15.871769, + 15.871715, + 15.871660, + 15.871607, + 15.871553, + 15.871499, + 15.871444, + 15.871390, + 15.871337, + 15.871282, + 15.871228, + 15.871174, + 15.871119, + 15.871065, + 15.871012, + 15.870958, + 15.870903, + 15.870849, + 15.870795, + 15.870740, + 15.870686, + 15.870633, + 15.870577, + 15.870524, + 15.870470, + 15.870416, + 15.870361, + 15.870307, + 15.870253, + 15.870198, + 15.870144, + 15.870091, + 15.870037, + 15.869982, + 15.869928, + 15.869874, + 15.869819, + 15.869765, + 15.869711, + 15.869656, + 15.869602, + 15.869548, + 15.869495, + 15.869439, + 15.869386, + 15.869332, + 15.869277, + 15.869223, + 15.869169, + 15.869114, + 15.869060, + 15.869006, + 15.868952, + 15.868897, + 15.868843, + 15.868789, + 15.868734, + 15.868679, + 15.868618, + 15.868556, + 15.868489, + 15.868421, + 15.868351, + 15.868280, + 15.868208, + 15.868134, + 15.868063, + 15.867991, + 15.867921, + 15.867852, + 15.867785, + 15.867721, + 15.867659, + 15.867601, + 15.867549, + 15.867499, + 15.867455, + 15.867416, + 15.867383, + 15.867357, + 15.867338, + 15.867327, + 15.867321, + 15.867327, + 15.867338, + 15.867359, + 15.867386, + 15.867419, + 15.867459, + 15.867505, + 15.867555, + 15.867610, + 15.867671, + 15.867734, + 15.867801, + 15.867869, + 15.867941, + 15.868012, + 15.868087, + 15.868161, + 15.868236, + 15.868310, + 15.868384, + 15.868457, + 15.868527, + 15.868595, + 15.868661, + 15.868722, + 15.868780, + 15.868837, + 15.868892, + 15.868948, + 15.869005, + 15.869061, + 15.869116, + 15.869173, + 15.869229, + 15.869284, + 15.869341, + 15.869397, + 15.869452, + 15.869509, + 15.869565, + 15.869620, + 15.869677, + 15.869733, + 15.869788, + 15.869845, + 15.869901, + 15.869956, + 15.870012, + 15.870069, + 15.870124, + 15.870180, + 15.870237, + 15.870292, + 15.870348, + 15.870405, + 15.870461, + 15.870516, + 15.870572, + 15.870629, + 15.870684, + 15.870740, + 15.870796, + 15.870851, + 15.870908, + 15.870964, + 15.871019, + 15.871076, + 15.871132, + 15.871187, + 15.871243, + 15.871300, + 15.871355, + 15.871411, + 15.871467, + 15.871522, + 15.871579, + 15.871635, + 15.871691, + 15.871746, + 15.871802, + 15.871859, + 15.871914, + 15.871970, + 15.872026, + 15.872081, + 15.872138, + 15.872194, + 15.872249, + 15.872305, + 15.872361, + 15.872416, + 15.872473, + 15.872529, + 15.872584, + 15.872640, + 15.872696, + 15.872751, + 15.872807, + 15.872864, + 15.872919, + 15.872975, + 15.873031, + 15.873087, + 15.873142, + 15.873198, + 15.873255, + 15.873310, + 15.873366, + 15.873422, + 15.873477, + 15.873533, + 15.873589, + 15.873644, + 15.873700, + 15.873757, + 15.873811, + 15.873868, + 15.873924, + 15.873979, + 15.874035, + 15.874091, + 15.874146, + 15.874202, + 15.874258, + 15.874313, + 15.874369, + 15.874425, + 15.874481, + 15.874536, + 15.874592, + ] # Logarithm of Infected Data - logIstar = [7.943245, 8.269994, 8.517212, 8.814208, 9.151740, 9.478472, 9.559847, 9.664087, 9.735378, 9.852583, 9.692265, 9.498807, 9.097634, 8.388878, 7.870516, 7.012956, 6.484941, 5.825368, 5.346815, 5.548361, 5.706732, 5.712617, 5.709714, 5.696888, 5.530087, 5.826563, 6.643563, 7.004292, 7.044663, 7.190259, 7.335926, 7.516861, 7.831779, 8.188895, 8.450204, 8.801436, 8.818379, 8.787658, 8.601685, 8.258338, 7.943364, 7.425585, 7.062834, 6.658307, 6.339600, 6.526984, 6.679178, 6.988758, 7.367331, 7.746694, 8.260558, 8.676522, 9.235582, 9.607778, 9.841917, 10.081571, 10.216090, 10.350366, 10.289668, 10.248842, 10.039504, 9.846343, 9.510392, 9.190923, 8.662465, 7.743221, 7.128458, 5.967898, 5.373883, 5.097497, 4.836570, 5.203345, 5.544798, 5.443047, 5.181152, 5.508669, 6.144130, 6.413744, 6.610423, 6.748885, 6.729511, 6.789841, 6.941034, 7.093516, 7.307039, 7.541077, 7.644803, 7.769145, 7.760187, 7.708017, 7.656795, 7.664983, 7.483828, 6.887324, 6.551093, 6.457449, 6.346064, 6.486300, 6.612378, 6.778753, 6.909477, 7.360570, 8.150303, 8.549044, 8.897572, 9.239323, 9.538751, 9.876531, 10.260911, 10.613536, 10.621510, 10.661115, 10.392899, 10.065536, 9.920090, 9.933097, 9.561691, 8.807713, 8.263463, 7.252184, 6.669083, 5.877763, 5.331878, 5.356563, 5.328469, 5.631146, 6.027497, 6.250717, 6.453919, 6.718444, 7.071636, 7.348905, 7.531528, 7.798226, 8.197941, 8.578809, 8.722964, 8.901152, 8.904370, 8.889865, 8.881902, 8.958903, 8.721281, 8.211509, 7.810624, 7.164607, 6.733688, 6.268503, 5.905983, 5.900432, 5.846547, 6.245427, 6.786271, 7.088480, 7.474295, 7.650063, 7.636703, 7.830990, 8.231516, 8.584816, 8.886908, 9.225216, 9.472778, 9.765505, 9.928623, 10.153033, 10.048574, 9.892620, 9.538818, 8.896100, 8.437584, 7.819738, 7.362598, 6.505880, 5.914972, 6.264584, 6.555019, 6.589319, 6.552029, 6.809771, 7.187616, 7.513918, 8.017712, 8.224957, 8.084474, 8.079148, 8.180991, 8.274269, 8.413748, 8.559599, 8.756090, 9.017927, 9.032720, 9.047983, 8.826873, 8.366489, 8.011876, 7.500830, 7.140406, 6.812626, 6.538719, 6.552218, 6.540129, 6.659927, 6.728530, 7.179692, 7.989210, 8.399173, 8.781128, 9.122303, 9.396378, 9.698512, 9.990104, 10.276543, 10.357284, 10.465869, 10.253833, 10.018503, 9.738407, 9.484367, 9.087025, 8.526409, 8.041126, 7.147168, 6.626706, 6.209446, 5.867231, 5.697439, 5.536769, 5.421413, 5.238297, 5.470136, 5.863007, 6.183083, 6.603569, 6.906278, 7.092324, 7.326612, 7.576052, 7.823430, 7.922775, 8.041677, 8.063403, 8.073229, 8.099726, 8.168522, 8.099041, 8.011404, 7.753147, 6.945211, 6.524244, 6.557723, 6.497742, 6.256247, 5.988794, 6.268093, 6.583316, 7.106842, 8.053929, 8.508237, 8.938915, 9.311863, 9.619753, 9.931745, 10.182361, 10.420978, 10.390829, 10.389230, 10.079342, 9.741479, 9.444561, 9.237448, 8.777687, 7.976436, 7.451502, 6.742856, 6.271545, 5.782289, 5.403089, 5.341954, 5.243509, 5.522993, 5.897001, 6.047042, 6.100738, 6.361727, 6.849562, 7.112544, 7.185346, 7.309412, 7.423746, 7.532142, 7.510318, 7.480175, 7.726362, 8.061117, 8.127072, 8.206166, 8.029634, 7.592953, 7.304869, 7.005394, 6.750019, 6.461377, 6.226432, 6.287047, 6.306452, 6.783694, 7.450957, 7.861692, 8.441530, 8.739626, 8.921994, 9.168961, 9.428077, 9.711664, 10.032714, 10.349937, 10.483985, 10.647475, 10.574038, 10.522431, 10.192246, 9.756246, 9.342511, 8.872072, 8.414189, 7.606582, 7.084701, 6.149903, 5.517257, 5.839429, 6.098090, 6.268935, 6.475965, 6.560543, 6.598942, 6.693938, 6.802531, 6.934345, 7.078370, 7.267736, 7.569640, 7.872204, 8.083603, 8.331226, 8.527144, 8.773523, 8.836599, 8.894303, 8.808326, 8.641717, 8.397901, 7.849034, 7.482899, 7.050252, 6.714103, 6.900603, 7.050765, 7.322905, 7.637986, 8.024340, 8.614505, 8.933591, 9.244008, 9.427410, 9.401385, 9.457744, 9.585068, 9.699673, 9.785478, 9.884559, 9.769732, 9.655075, 9.423071, 9.210198, 8.786654, 8.061787, 7.560976, 6.855829, 6.390707, 5.904006, 5.526631, 5.712303, 5.867027, 5.768367, 5.523352, 5.909118, 6.745543, 6.859218 ] + logIstar = [ + 7.943245, + 8.269994, + 8.517212, + 8.814208, + 9.151740, + 9.478472, + 9.559847, + 9.664087, + 9.735378, + 9.852583, + 9.692265, + 9.498807, + 9.097634, + 8.388878, + 7.870516, + 7.012956, + 6.484941, + 5.825368, + 5.346815, + 5.548361, + 5.706732, + 5.712617, + 5.709714, + 5.696888, + 5.530087, + 5.826563, + 6.643563, + 7.004292, + 7.044663, + 7.190259, + 7.335926, + 7.516861, + 7.831779, + 8.188895, + 8.450204, + 8.801436, + 8.818379, + 8.787658, + 8.601685, + 8.258338, + 7.943364, + 7.425585, + 7.062834, + 6.658307, + 6.339600, + 6.526984, + 6.679178, + 6.988758, + 7.367331, + 7.746694, + 8.260558, + 8.676522, + 9.235582, + 9.607778, + 9.841917, + 10.081571, + 10.216090, + 10.350366, + 10.289668, + 10.248842, + 10.039504, + 9.846343, + 9.510392, + 9.190923, + 8.662465, + 7.743221, + 7.128458, + 5.967898, + 5.373883, + 5.097497, + 4.836570, + 5.203345, + 5.544798, + 5.443047, + 5.181152, + 5.508669, + 6.144130, + 6.413744, + 6.610423, + 6.748885, + 6.729511, + 6.789841, + 6.941034, + 7.093516, + 7.307039, + 7.541077, + 7.644803, + 7.769145, + 7.760187, + 7.708017, + 7.656795, + 7.664983, + 7.483828, + 6.887324, + 6.551093, + 6.457449, + 6.346064, + 6.486300, + 6.612378, + 6.778753, + 6.909477, + 7.360570, + 8.150303, + 8.549044, + 8.897572, + 9.239323, + 9.538751, + 9.876531, + 10.260911, + 10.613536, + 10.621510, + 10.661115, + 10.392899, + 10.065536, + 9.920090, + 9.933097, + 9.561691, + 8.807713, + 8.263463, + 7.252184, + 6.669083, + 5.877763, + 5.331878, + 5.356563, + 5.328469, + 5.631146, + 6.027497, + 6.250717, + 6.453919, + 6.718444, + 7.071636, + 7.348905, + 7.531528, + 7.798226, + 8.197941, + 8.578809, + 8.722964, + 8.901152, + 8.904370, + 8.889865, + 8.881902, + 8.958903, + 8.721281, + 8.211509, + 7.810624, + 7.164607, + 6.733688, + 6.268503, + 5.905983, + 5.900432, + 5.846547, + 6.245427, + 6.786271, + 7.088480, + 7.474295, + 7.650063, + 7.636703, + 7.830990, + 8.231516, + 8.584816, + 8.886908, + 9.225216, + 9.472778, + 9.765505, + 9.928623, + 10.153033, + 10.048574, + 9.892620, + 9.538818, + 8.896100, + 8.437584, + 7.819738, + 7.362598, + 6.505880, + 5.914972, + 6.264584, + 6.555019, + 6.589319, + 6.552029, + 6.809771, + 7.187616, + 7.513918, + 8.017712, + 8.224957, + 8.084474, + 8.079148, + 8.180991, + 8.274269, + 8.413748, + 8.559599, + 8.756090, + 9.017927, + 9.032720, + 9.047983, + 8.826873, + 8.366489, + 8.011876, + 7.500830, + 7.140406, + 6.812626, + 6.538719, + 6.552218, + 6.540129, + 6.659927, + 6.728530, + 7.179692, + 7.989210, + 8.399173, + 8.781128, + 9.122303, + 9.396378, + 9.698512, + 9.990104, + 10.276543, + 10.357284, + 10.465869, + 10.253833, + 10.018503, + 9.738407, + 9.484367, + 9.087025, + 8.526409, + 8.041126, + 7.147168, + 6.626706, + 6.209446, + 5.867231, + 5.697439, + 5.536769, + 5.421413, + 5.238297, + 5.470136, + 5.863007, + 6.183083, + 6.603569, + 6.906278, + 7.092324, + 7.326612, + 7.576052, + 7.823430, + 7.922775, + 8.041677, + 8.063403, + 8.073229, + 8.099726, + 8.168522, + 8.099041, + 8.011404, + 7.753147, + 6.945211, + 6.524244, + 6.557723, + 6.497742, + 6.256247, + 5.988794, + 6.268093, + 6.583316, + 7.106842, + 8.053929, + 8.508237, + 8.938915, + 9.311863, + 9.619753, + 9.931745, + 10.182361, + 10.420978, + 10.390829, + 10.389230, + 10.079342, + 9.741479, + 9.444561, + 9.237448, + 8.777687, + 7.976436, + 7.451502, + 6.742856, + 6.271545, + 5.782289, + 5.403089, + 5.341954, + 5.243509, + 5.522993, + 5.897001, + 6.047042, + 6.100738, + 6.361727, + 6.849562, + 7.112544, + 7.185346, + 7.309412, + 7.423746, + 7.532142, + 7.510318, + 7.480175, + 7.726362, + 8.061117, + 8.127072, + 8.206166, + 8.029634, + 7.592953, + 7.304869, + 7.005394, + 6.750019, + 6.461377, + 6.226432, + 6.287047, + 6.306452, + 6.783694, + 7.450957, + 7.861692, + 8.441530, + 8.739626, + 8.921994, + 9.168961, + 9.428077, + 9.711664, + 10.032714, + 10.349937, + 10.483985, + 10.647475, + 10.574038, + 10.522431, + 10.192246, + 9.756246, + 9.342511, + 8.872072, + 8.414189, + 7.606582, + 7.084701, + 6.149903, + 5.517257, + 5.839429, + 6.098090, + 6.268935, + 6.475965, + 6.560543, + 6.598942, + 6.693938, + 6.802531, + 6.934345, + 7.078370, + 7.267736, + 7.569640, + 7.872204, + 8.083603, + 8.331226, + 8.527144, + 8.773523, + 8.836599, + 8.894303, + 8.808326, + 8.641717, + 8.397901, + 7.849034, + 7.482899, + 7.050252, + 6.714103, + 6.900603, + 7.050765, + 7.322905, + 7.637986, + 8.024340, + 8.614505, + 8.933591, + 9.244008, + 9.427410, + 9.401385, + 9.457744, + 9.585068, + 9.699673, + 9.785478, + 9.884559, + 9.769732, + 9.655075, + 9.423071, + 9.210198, + 8.786654, + 8.061787, + 7.560976, + 6.855829, + 6.390707, + 5.904006, + 5.526631, + 5.712303, + 5.867027, + 5.768367, + 5.523352, + 5.909118, + 6.745543, + 6.859218, + ] # Change in Susceptible Population - deltaS = [ 9916.490263 ,12014.263380 ,13019.275755 ,12296.373612 ,8870.995603 ,1797.354574 ,-6392.880771 ,-16150.825387 ,-27083.245106 ,-40130.421462 ,-50377.169958 ,-57787.717468 ,-60797.223427 ,-59274.041897 ,-55970.213230 ,-51154.650927 ,-45877.841034 ,-40278.553775 ,-34543.967175 ,-28849.633641 ,-23192.776605 ,-17531.130740 ,-11862.021829 ,-6182.456792 ,-450.481090 ,5201.184400 ,10450.773882 ,15373.018272 ,20255.699431 ,24964.431669 ,29470.745887 ,33678.079947 ,37209.808930 ,39664.432393 ,41046.735479 ,40462.982011 ,39765.070209 ,39270.815830 ,39888.077002 ,42087.276604 ,45332.012929 ,49719.128772 ,54622.190928 ,59919.718626 ,65436.341097 ,70842.911460 ,76143.747430 ,81162.358574 ,85688.102884 ,89488.917734 ,91740.108470 ,91998.787916 ,87875.986012 ,79123.877908 ,66435.611045 ,48639.250610 ,27380.282817 ,2166.538464 ,-21236.428084 ,-43490.803535 ,-60436.624080 ,-73378.401966 ,-80946.278268 ,-84831.969493 ,-84696.627286 ,-81085.365407 ,-76410.847049 ,-70874.415387 ,-65156.276464 ,-59379.086883 ,-53557.267619 ,-47784.164830 ,-42078.001172 ,-36340.061427 ,-30541.788202 ,-24805.281435 ,-19280.817165 ,-13893.690606 ,-8444.172221 ,-3098.160839 ,2270.908649 ,7594.679295 ,12780.079247 ,17801.722109 ,22543.091206 ,26897.369814 ,31051.285734 ,34933.809557 ,38842.402859 ,42875.230152 ,47024.395356 ,51161.516122 ,55657.298307 ,60958.155424 ,66545.635029 ,72202.930397 ,77934.761905 ,83588.207792 ,89160.874522 ,94606.115027 ,99935.754968 ,104701.404975 ,107581.670606 ,108768.440311 ,107905.700480 ,104062.148863 ,96620.281684 ,83588.443029 ,61415.088182 ,27124.031692 ,-7537.285321 ,-43900.451653 ,-70274.062783 ,-87573.481475 ,-101712.148408 ,-116135.719087 ,-124187.225446 ,-124725.278371 ,-122458.145590 ,-117719.918256 ,-112352.138605 ,-106546.806030 ,-100583.803012 ,-94618.253238 ,-88639.090897 ,-82725.009842 ,-76938.910669 ,-71248.957807 ,-65668.352795 ,-60272.761991 ,-55179.538428 ,-50456.021161 ,-46037.728058 ,-42183.912670 ,-39522.184006 ,-38541.255303 ,-38383.665728 ,-39423.998130 ,-40489.466130 ,-41450.406768 ,-42355.156592 ,-43837.562085 ,-43677.262972 ,-41067.896944 ,-37238.628465 ,-32230.392026 ,-26762.766062 ,-20975.163308 ,-15019.218554 ,-9053.105545 ,-3059.663132 ,2772.399618 ,8242.538397 ,13407.752291 ,18016.047539 ,22292.125752 ,26616.583347 ,30502.564253 ,33153.890890 ,34216.684448 ,33394.220786 ,29657.417791 ,23064.375405 ,12040.831532 ,-2084.921068 ,-21390.235970 ,-38176.615985 ,-51647.714482 ,-59242.564959 ,-60263.150854 ,-58599.245165 ,-54804.972560 ,-50092.112608 ,-44465.812552 ,-38533.096297 ,-32747.104307 ,-27130.082610 ,-21529.632955 ,-15894.611939 ,-10457.566933 ,-5429.042583 ,-903.757828 ,2481.947589 ,5173.789976 ,8358.768202 ,11565.584635 ,14431.147931 ,16951.619820 ,18888.807708 ,20120.884465 ,20222.141242 ,18423.168124 ,16498.668271 ,14442.624242 ,14070.038273 ,16211.370808 ,19639.815904 ,24280.360465 ,29475.380079 ,35030.793540 ,40812.325095 ,46593.082382 ,52390.906885 ,58109.310860 ,63780.896094 ,68984.456561 ,72559.442320 ,74645.487900 ,74695.219755 ,72098.143876 ,66609.929889 ,56864.971296 ,41589.295266 ,19057.032104 ,-5951.329863 ,-34608.796853 ,-56603.801584 ,-72678.838057 ,-83297.070856 ,-90127.593511 ,-92656.040614 ,-91394.995510 ,-88192.056842 ,-83148.833075 ,-77582.587173 ,-71750.440823 ,-65765.369857 ,-59716.101820 ,-53613.430067 ,-47473.832358 ,-41287.031890 ,-35139.919259 ,-29097.671507 ,-23178.836760 ,-17486.807388 ,-12046.775779 ,-6802.483422 ,-1867.556171 ,2644.380534 ,6615.829501 ,10332.557518 ,13706.737038 ,17017.991307 ,20303.136670 ,23507.386461 ,26482.194102 ,29698.585356 ,33196.305757 ,37385.914179 ,42872.996212 ,48725.617879 ,54564.488527 ,60453.841604 ,66495.146265 ,72668.620416 ,78723.644870 ,84593.136677 ,89974.936239 ,93439.798630 ,95101.207834 ,94028.126381 ,89507.925620 ,80989.846001 ,66944.274744 ,47016.422041 ,19932.783790 ,-6198.433172 ,-32320.379400 ,-49822.852084 ,-60517.553414 ,-66860.548269 ,-70849.714105 ,-71058.721556 ,-67691.947812 ,-63130.703822 ,-57687.607311 ,-51916.952488 ,-45932.054982 ,-39834.909941 ,-33714.535713 ,-27564.443333 ,-21465.186188 ,-15469.326408 ,-9522.358787 ,-3588.742161 ,2221.802073 ,7758.244339 ,13020.269708 ,18198.562827 ,23211.338588 ,28051.699645 ,32708.577247 ,37413.795242 ,42181.401920 ,46462.499633 ,49849.582315 ,53026.578940 ,55930.600705 ,59432.642178 ,64027.356857 ,69126.843653 ,74620.328837 ,80372.056070 ,86348.152766 ,92468.907239 ,98568.998246 ,104669.511588 ,110445.790143 ,115394.348973 ,119477.553152 ,121528.574511 ,121973.674087 ,121048.017786 ,118021.473181 ,112151.993711 ,102195.999157 ,85972.731130 ,61224.719621 ,31949.279603 ,-3726.022971 ,-36485.298619 ,-67336.469799 ,-87799.366129 ,-98865.713558 ,-104103.651120 ,-105068.402300 ,-103415.820781 ,-99261.356633 ,-94281.850081 ,-88568.701325 ,-82625.711921 ,-76766.776770 ,-70998.803524 ,-65303.404499 ,-59719.198305 ,-54182.230439 ,-48662.904657 ,-43206.731668 ,-37732.701095 ,-32375.478519 ,-27167.508567 ,-22197.211891 ,-17722.869502 ,-13925.135219 ,-10737.893027 ,-8455.327914 ,-7067.008358 ,-7086.991191 ,-7527.693561 ,-8378.025732 ,-8629.383998 ,-7854.586079 ,-5853.040657 ,-1973.225485 ,2699.850783 ,8006.098287 ,13651.734934 ,19139.318072 ,24476.645420 ,29463.480336 ,33899.078820 ,37364.528796 ,38380.214949 ,37326.585649 ,33428.470616 ,27441.000494 ,21761.126583 ,15368.408081 ,7224.234078 ,-2702.217396 ,-14109.682505 ,-27390.915614 ,-38569.562393 ,-47875.155339 ,-53969.121872 ,-57703.473001 ,-57993.198171 ,-54908.391840 ,-50568.410328 ,-45247.622563 ,-39563.224328 ,-33637.786521 ,-27585.345413 ,-21572.074797 ,-15597.363909 ,-9577.429076 ,-3475.770622 ,2520.378408 ,8046.881775 ,13482.345595 ] + deltaS = [ + 9916.490263, + 12014.263380, + 13019.275755, + 12296.373612, + 8870.995603, + 1797.354574, + -6392.880771, + -16150.825387, + -27083.245106, + -40130.421462, + -50377.169958, + -57787.717468, + -60797.223427, + -59274.041897, + -55970.213230, + -51154.650927, + -45877.841034, + -40278.553775, + -34543.967175, + -28849.633641, + -23192.776605, + -17531.130740, + -11862.021829, + -6182.456792, + -450.481090, + 5201.184400, + 10450.773882, + 15373.018272, + 20255.699431, + 24964.431669, + 29470.745887, + 33678.079947, + 37209.808930, + 39664.432393, + 41046.735479, + 40462.982011, + 39765.070209, + 39270.815830, + 39888.077002, + 42087.276604, + 45332.012929, + 49719.128772, + 54622.190928, + 59919.718626, + 65436.341097, + 70842.911460, + 76143.747430, + 81162.358574, + 85688.102884, + 89488.917734, + 91740.108470, + 91998.787916, + 87875.986012, + 79123.877908, + 66435.611045, + 48639.250610, + 27380.282817, + 2166.538464, + -21236.428084, + -43490.803535, + -60436.624080, + -73378.401966, + -80946.278268, + -84831.969493, + -84696.627286, + -81085.365407, + -76410.847049, + -70874.415387, + -65156.276464, + -59379.086883, + -53557.267619, + -47784.164830, + -42078.001172, + -36340.061427, + -30541.788202, + -24805.281435, + -19280.817165, + -13893.690606, + -8444.172221, + -3098.160839, + 2270.908649, + 7594.679295, + 12780.079247, + 17801.722109, + 22543.091206, + 26897.369814, + 31051.285734, + 34933.809557, + 38842.402859, + 42875.230152, + 47024.395356, + 51161.516122, + 55657.298307, + 60958.155424, + 66545.635029, + 72202.930397, + 77934.761905, + 83588.207792, + 89160.874522, + 94606.115027, + 99935.754968, + 104701.404975, + 107581.670606, + 108768.440311, + 107905.700480, + 104062.148863, + 96620.281684, + 83588.443029, + 61415.088182, + 27124.031692, + -7537.285321, + -43900.451653, + -70274.062783, + -87573.481475, + -101712.148408, + -116135.719087, + -124187.225446, + -124725.278371, + -122458.145590, + -117719.918256, + -112352.138605, + -106546.806030, + -100583.803012, + -94618.253238, + -88639.090897, + -82725.009842, + -76938.910669, + -71248.957807, + -65668.352795, + -60272.761991, + -55179.538428, + -50456.021161, + -46037.728058, + -42183.912670, + -39522.184006, + -38541.255303, + -38383.665728, + -39423.998130, + -40489.466130, + -41450.406768, + -42355.156592, + -43837.562085, + -43677.262972, + -41067.896944, + -37238.628465, + -32230.392026, + -26762.766062, + -20975.163308, + -15019.218554, + -9053.105545, + -3059.663132, + 2772.399618, + 8242.538397, + 13407.752291, + 18016.047539, + 22292.125752, + 26616.583347, + 30502.564253, + 33153.890890, + 34216.684448, + 33394.220786, + 29657.417791, + 23064.375405, + 12040.831532, + -2084.921068, + -21390.235970, + -38176.615985, + -51647.714482, + -59242.564959, + -60263.150854, + -58599.245165, + -54804.972560, + -50092.112608, + -44465.812552, + -38533.096297, + -32747.104307, + -27130.082610, + -21529.632955, + -15894.611939, + -10457.566933, + -5429.042583, + -903.757828, + 2481.947589, + 5173.789976, + 8358.768202, + 11565.584635, + 14431.147931, + 16951.619820, + 18888.807708, + 20120.884465, + 20222.141242, + 18423.168124, + 16498.668271, + 14442.624242, + 14070.038273, + 16211.370808, + 19639.815904, + 24280.360465, + 29475.380079, + 35030.793540, + 40812.325095, + 46593.082382, + 52390.906885, + 58109.310860, + 63780.896094, + 68984.456561, + 72559.442320, + 74645.487900, + 74695.219755, + 72098.143876, + 66609.929889, + 56864.971296, + 41589.295266, + 19057.032104, + -5951.329863, + -34608.796853, + -56603.801584, + -72678.838057, + -83297.070856, + -90127.593511, + -92656.040614, + -91394.995510, + -88192.056842, + -83148.833075, + -77582.587173, + -71750.440823, + -65765.369857, + -59716.101820, + -53613.430067, + -47473.832358, + -41287.031890, + -35139.919259, + -29097.671507, + -23178.836760, + -17486.807388, + -12046.775779, + -6802.483422, + -1867.556171, + 2644.380534, + 6615.829501, + 10332.557518, + 13706.737038, + 17017.991307, + 20303.136670, + 23507.386461, + 26482.194102, + 29698.585356, + 33196.305757, + 37385.914179, + 42872.996212, + 48725.617879, + 54564.488527, + 60453.841604, + 66495.146265, + 72668.620416, + 78723.644870, + 84593.136677, + 89974.936239, + 93439.798630, + 95101.207834, + 94028.126381, + 89507.925620, + 80989.846001, + 66944.274744, + 47016.422041, + 19932.783790, + -6198.433172, + -32320.379400, + -49822.852084, + -60517.553414, + -66860.548269, + -70849.714105, + -71058.721556, + -67691.947812, + -63130.703822, + -57687.607311, + -51916.952488, + -45932.054982, + -39834.909941, + -33714.535713, + -27564.443333, + -21465.186188, + -15469.326408, + -9522.358787, + -3588.742161, + 2221.802073, + 7758.244339, + 13020.269708, + 18198.562827, + 23211.338588, + 28051.699645, + 32708.577247, + 37413.795242, + 42181.401920, + 46462.499633, + 49849.582315, + 53026.578940, + 55930.600705, + 59432.642178, + 64027.356857, + 69126.843653, + 74620.328837, + 80372.056070, + 86348.152766, + 92468.907239, + 98568.998246, + 104669.511588, + 110445.790143, + 115394.348973, + 119477.553152, + 121528.574511, + 121973.674087, + 121048.017786, + 118021.473181, + 112151.993711, + 102195.999157, + 85972.731130, + 61224.719621, + 31949.279603, + -3726.022971, + -36485.298619, + -67336.469799, + -87799.366129, + -98865.713558, + -104103.651120, + -105068.402300, + -103415.820781, + -99261.356633, + -94281.850081, + -88568.701325, + -82625.711921, + -76766.776770, + -70998.803524, + -65303.404499, + -59719.198305, + -54182.230439, + -48662.904657, + -43206.731668, + -37732.701095, + -32375.478519, + -27167.508567, + -22197.211891, + -17722.869502, + -13925.135219, + -10737.893027, + -8455.327914, + -7067.008358, + -7086.991191, + -7527.693561, + -8378.025732, + -8629.383998, + -7854.586079, + -5853.040657, + -1973.225485, + 2699.850783, + 8006.098287, + 13651.734934, + 19139.318072, + 24476.645420, + 29463.480336, + 33899.078820, + 37364.528796, + 38380.214949, + 37326.585649, + 33428.470616, + 27441.000494, + 21761.126583, + 15368.408081, + 7224.234078, + -2702.217396, + -14109.682505, + -27390.915614, + -38569.562393, + -47875.155339, + -53969.121872, + -57703.473001, + -57993.198171, + -54908.391840, + -50568.410328, + -45247.622563, + -39563.224328, + -33637.786521, + -27585.345413, + -21572.074797, + -15597.363909, + -9577.429076, + -3475.770622, + 2520.378408, + 8046.881775, + 13482.345595, + ] # Transmission Rate Set - beta_set = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26] + beta_set = [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + ] # from new_data_set import * # Uncomment this line to use new data set @@ -49,62 +1619,71 @@ def build_model(): model = ConcreteModel() # declare constants - bpy = 26 # biweeks per year - years = 15 # years of data - bigM = 50.0 # big M for disjunction constraints + bpy = 26 # biweeks per year + years = 15 # years of data + bigM = 50.0 # big M for disjunction constraints # declare sets - model.S_meas = RangeSet(1,bpy*years) - model.S_meas_small = RangeSet(1,bpy*years-1) - model.S_beta = RangeSet(1,bpy) + model.S_meas = RangeSet(1, bpy * years) + model.S_meas_small = RangeSet(1, bpy * years - 1) + model.S_beta = RangeSet(1, bpy) # define variable bounds - def _gt_zero(m,i): + def _gt_zero(m, i): """ - Boundary greater than zero + Defines boundary constraints ensuring variables remain greater than zero. - Args: - m (Pyomo.ConcreteModel): SIR disease model using a low/high transmission parameter - i (int): index of biweekly periods in the data set + Parameters + ---------- + m : Pyomo.ConcreteModel + SIR disease model using a low/high transmission parameter + i : int + index of biweekly periods in the data set - Returns: - None, but set up the boundary greater than zero. + Returns + ------- + tuple + A tuple representing the lower and upper bounds for the variable, ensuring it remains positive. """ - return (0.0,1e7) - + return (0.0, 1e7) + def _beta_bounds(m): """ - Set up the Transition Parameters Bounds + Sets the bounds for the transmission parameter beta within the model. - Args: - m (Pyomo.ConcreteModel): SIR disease model using a low/high transmission parameter + Parameters + ---------- + m : Pyomo.ConcreteModel + SIR disease model using a low/high transmission parameter - Returns: - None, but set up the transition parameters bounds. + Returns + ------- + tuple + A tuple representing the lower and upper bounds for the beta variable. """ - return (None,5.0) + return (None, 5.0) - # define variables + # Define variables + # Log of estimated cases; All the variables are represented as common logarithm which the log base is 10. + # The original code inside build_model employs a disjunctive approach with integrated constraints. + # On the other hand, the commented code uses separate constraints for each scenario, applying the Big-M Reformulation. + # Binary variables (model.y) are defined inside on the build_model() function. The disjuncts for the Big-M Reformulation is written outside of the code. - # log of estimated cases - """ - The original code inside build_model employs a disjunctive approach with integrated constraints. - On the other hand, the commented code uses separate constraints for each scenario, applying the Big-M Reformulation. - Binary variables (model.y) are defined inside on the build_model() function. The disjuncts for the Big-M Reformulation is written outside of the code. - """ # model.logI = Var(model.S_meas, bounds=_gt_zero, doc='log of estimated cases') - model.logI = Var(model.S_meas, bounds=(0.001,1e7), doc='log of estimated cases') + model.logI = Var(model.S_meas, bounds=(0.001, 1e7), doc='log of estimated cases') # log of transmission parameter beta # model.logbeta = Var(model.S_beta, bounds=_gt_zero, doc='log of transmission parameter beta') - model.logbeta = Var(model.S_beta, bounds=(0.0001,5), doc='log of transmission parameter beta') + model.logbeta = Var( + model.S_beta, bounds=(0.0001, 5), doc='log of transmission parameter beta' + ) # binary variable y over all betas # model.y = Var(model.S_beta, within=Binary, doc='binary variable y over all betas') # low value of beta # model.logbeta_low = Var(bounds=_beta_bounds, doc='low value of beta') - model.logbeta_low = Var(bounds=(0.0001,5)) + model.logbeta_low = Var(bounds=(0.0001, 5)) # high value of beta # model.logbeta_high = Var(bounds=_beta_bounds, doc='high value of beta') - model.logbeta_high = Var(bounds=(0.0001,5), doc='high value of beta') + model.logbeta_high = Var(bounds=(0.0001, 5), doc='high value of beta') # dummy variables model.p = Var(model.S_meas, bounds=_gt_zero, doc='dummy variable p') model.n = Var(model.S_meas, bounds=_gt_zero, doc='dummy variable n') @@ -117,136 +1696,199 @@ def _beta_bounds(m): deltaS = deltaS # mean susceptibles (Number of Population) # meanS = 1.04e6 - meanS = 8.65e5 - # log of measured population (Number of Population(log scale)) - logN = pop + meanS = 8.65e5 # Number of Population(people) + logN = pop # log of measured population (Number of Population(log scale with log base 10)) # define index for beta over all measurements () beta_set = beta_set # define objective def _obj_rule(m): """ - Minimize the total discrepancy or error in the SIR disease model model over time. - These variables ('p' and 'n') are likely to represent the overestimation and underestimation errors, respectively, - in the model's estimation of infectious cases compared to the observed data. - By minimizing their sum, the model seeks to closely align its estimations with the actual observed data. + Objective function for the SIR disease model, aiming to minimize the total discrepancy between estimated and observed infectious cases. + + Parameters + ---------- + m : Pyomo.ConcreteModel + SIR disease model using a low/high transmission parameter - Args: - m (Pyomo.ConcreteModel): SIR disease model using a low/high transmission parameter + Returns + ------- + Pyomo.Expression + The expression for the objective function, which is the sum of overestimation and underestimation errors across all time periods. - Returns: - expr (Pyomo.Expression): The objective function expression, which is the sum of the variables 'p' and 'n' for each time period. - This expression represents the total error (both overestimation and underestimation) across all time periods in the model. + Notes + ----- + These variables ('p' and 'n') are likely to represent the overestimation and underestimation errors, respectively, + in the model's estimation of infectious cases compared to the observed data. + By minimizing their sum, the model seeks to closely align its estimations with the actual observed data. """ expr = sum(m.p[i] + m.n[i] for i in m.S_meas) return expr + model.obj = Objective(rule=_obj_rule, sense=minimize, doc='objective function') # define constraints - def _logSIR(m,i): + def _logSIR(m, i): """ - Constraint function for the SIR disease model that represents the dynamics of infectious disease spread. - This constraint is based on a logarithmic formulation of the SIR model. - The model enforces that the actual dynamics (captured in the expression expr) follow the specified dynamics of the SIR model for each biweekly period by setting the expression equal to zero. - This is the constraint of the differential function discretized. - - Args: - m (Pyomo.ConcreteModel): SIR disease model using a low/high transmission parameter - i (int): index of biweekly periods in the data set - - Returns: - A tuple (0.0, expr) where expr is the Pyomo expression for the SIR constraint at the i-th biweekly period. - This represents the change in the logarithm of estimated infectious cases adjusted by the transmission rate, - change in susceptibles, and the total population. + SIR model constraint capturing the dynamics of infectious disease spread using a logarithmic formulation. + + Parameters + ---------- + m : Pyomo.ConcreteModel + SIR disease model using a low/high transmission parameter + i : int + index of biweekly periods in the data set + + Returns + ------- + tuple + A tuple containing the constraint expression for the SIR dynamics at the i-th bi-weekly period. + + Notes + ----- + This constraint is based on the differential equations of the SIR model, discretized and transformed into a logarithmic scale. + The 0.0 in (0.0, expr) enforces expr to be zero, defining an equality constraint essential for the SIR model to accurately capture the exact dynamics of disease transmission between time steps. """ - expr = m.logI[i+1] - ( m.logbeta[beta_set[i-1]] + m.logI[i] + math.log(deltaS[i-1] + meanS) - logN[i-1] ) + expr = m.logI[i + 1] - ( + m.logbeta[beta_set[i - 1]] + + m.logI[i] + + math.log(deltaS[i - 1] + meanS) + - logN[i - 1] + ) return (0.0, expr) - model.logSIR = Constraint(model.S_meas_small, rule=_logSIR, doc='log of SIR disease model') + + model.logSIR = Constraint( + model.S_meas_small, rule=_logSIR, doc='log of SIR disease model' + ) # objective function constraint - def _p_n_const(m,i): + def _p_n_const(m, i): """ - Defines a constraint for the SIR disease model that relates the model's estimated infectious cases to observed data. - This constraint is essential for ensuring the model's estimates are consistent with actual data, adjusted for overestimation and underestimation. - The constraint is formulated to account for the difference between the logarithm of observed infectious cases (logIstar) and the model's logarithm of estimated infectious cases (m.logI). + Defines a constraint relating the model's estimated infectious cases to observed data, adjusted for overestimation and underestimation. + It includes the variables 'p' and 'n', which represent the overestimation and underestimation errors, respectively. - Args: - m (Pyomo.ConcreteModel): SIR disease model using a low/high transmission parameter - i (int): index of biweekly periods in the data set - - Returns: - A tuple (0.0, expr) where expr is the Pyomo expression for the constraint at the i-th biweekly period. - This represents the adjustment of the model's estimated cases by accounting for overestimation and underestimation errors compared to the observed data. + Parameters + ---------- + m : Pyomo.ConcreteModel + SIR disease model using a low/high transmission parameter + i : int + index of biweekly periods in the data set + + Returns + ------- + tuple + A tuple containing the constraint expression for adjusting the model's estimated cases at the i-th bi-weekly period. + + Notes + ----- + The constraint is formulated to account for the difference between the logarithm of observed infectious cases (logIstar) and the model's logarithm of estimated infectious cases (m.logI). + The 'p' and 'n' variables in the model represent overestimation and underestimation errors, respectively, and this constraint helps in aligning the model's estimates with actual observed data. """ - expr = logIstar[i-1] - m.logI[i] - m.p[i] + m.n[i] + expr = logIstar[i - 1] - m.logI[i] - m.p[i] + m.n[i] return (0.0, expr) - model.p_n_const = Constraint(model.S_meas,rule=_p_n_const, doc='constraint for p and n') + + model.p_n_const = Constraint( + model.S_meas, rule=_p_n_const, doc='constraint for p and n' + ) # disjuncts model.BigM = Suffix() - model.y = RangeSet(0,1) + model.y = RangeSet(0, 1) + def _high_low(disjunct, i, y): """ - Disjunct for the high and low beta values + Disjunct function for setting high and low beta values based on the binary variable. + + Parameters + ---------- + disjunct : Pyomo.Disjunction + The disjunct block being defined. + i : int + Index of biweekly periods in the data set. + y : int + Binary variable indicating whether the high or low beta value is used. - Args: - disjunct (Pyomo.Disjunction): _description_ - i (int): index of biweekly periods in the data set - y (int): binary variable + Returns + ------- + None + Modifies the disjunct block to include the appropriate constraint based on the value of y. - Returns: - None, but sets up the disjuncts for the high and low beta values. + Notes + ----- + This function contributes to the disjunctive formulation of the model, allowing for the selection between high and low transmission rates for the disease. """ model = disjunct.model() if y: - disjunct.c = Constraint(expr=model.logbeta_high - model.logbeta[i]== 0.0) + disjunct.c = Constraint(expr=model.logbeta_high - model.logbeta[i] == 0.0) else: disjunct.c = Constraint(expr=model.logbeta[i] - model.logbeta_low == 0.0) model.BigM[disjunct.c] = bigM - model.high_low = Disjunct(model.S_beta, model.y, rule=_high_low, doc='disjunct for high and low beta values') + + model.high_low = Disjunct( + model.S_beta, + model.y, + rule=_high_low, + doc='disjunct for high and low beta values', + ) # disjunctions def _disj(model, i): """ - Disjunction for the high and low beta values + Defines a disjunction for each beta value to choose between high and low transmission rates. - Args: - model (Pyomo.Disjunction): disjunction for the high and low beta values - i (int): index of biweekly periods in the data set + Parameters + ---------- + model : Pyomo.Disjunction + disjunction for the high and low beta values + i : int + Index of biweekly periods in the data set - Returns: - A list of disjuncts (model.high_low[i, j]) for each binary state 'j' in 'model.y'. - This list represents the two possible states for each biweekly period 'i': - - model.high_low[i, 0]: The set of constraints when the low beta value is active. - - model.high_low[i, 1]: The set of constraints when the high beta value is active. + Returns + ------- + list + A list of disjuncts for the i-th biweekly period, enabling the model to choose between + high and low beta values. + Each disjunct represents a set of constraints that are activated + based on the binary decision variables. - This list is used by Pyomo to create a disjunction for each biweekly period, allowing the model to choose between the high or low beta value constraints based on the optimization process. - The defined disjunctions are integral to the model, enabling it to adaptively select the appropriate beta value for each time period, reflecting changes in disease transmission dynamics. + Notes + ----- + This list is used by Pyomo to create a disjunction for each biweekly period, allowing the model to choose between the high or low beta value constraints based on the optimization process. + The defined disjunctions are integral to the model, enabling it to adaptively select the appropriate beta value for each time period, reflecting changes in disease transmission dynamics. """ - return [model.high_low[i,j] for j in model.y] - model.disj = Disjunction(model.S_beta, rule=_disj, doc='disjunction for high and low beta values') + return [model.high_low[i, j] for j in model.y] - return model + model.disj = Disjunction( + model.S_beta, rule=_disj, doc='disjunction for high and low beta values' + ) + return model -# disjuncts -# high beta disjuncts +# # disjuncts +# # high beta disjuncts # def highbeta_L(m,i): # """ -# Args: -# m (Pyomo.ConcreteModel): SIR disease model using a low/high transmission parameter -# i (int): index of biweekly periods in the data set +# Defines the lower bound constraint for the high transmission parameter beta in the SIR model. -# Returns: -# A tuple (0.0, expr, None) where expr is the Pyomo expression for the lower bound of the high beta disjunct at the i-th biweekly period. +# Parameters +# ---------- +# m : Pyomo.ConcreteModel +# SIR disease model using a low/high transmission parameter +# i : int +# index of biweekly periods in the data set + +# Returns +# ------- +# A tuple (0.0, expr, None) where expr is the Pyomo expression for the lower bound of the high beta disjunct at the i-th biweekly period. # This represents the lower bound of the high beta disjunct at the i-th biweekly period. -# Note: -# The given function is given as the expression that the disjunctions are converted by big-M reformulation. -# The binary variable m.y[i] is commented inside the model function. +# Notes +# ----- +# The given function is given as the expression that the disjunctions are converted by big-M reformulation. +# The binary variable m.y[i] is commented inside the model function. # """ # expr = m.logbeta[i] - m.logbeta_high + bigM*(1-m.y[i]) # return (0.0, expr, None) @@ -254,15 +1896,20 @@ def _disj(model, i): # def highbeta_U(m,i): # """ -# Args: -# m (Pyomo.ConcreteModel): SIR disease model using a low/high transmission parameter -# i (int): index of biweekly periods in the data set +# Defines the upper bound constraint for the high transmission parameter beta in the SIR model. + +# Parameters +# ---------- +# m : Pyomo.ConcreteModel +# SIR disease model using a low/high transmission parameter +# i : int +# Index of biweekly periods in the data set. # Returns: -# A tuple (None, expr, 0.0) where expr is the Pyomo expression for the upper bound of the high beta disjunct at the i-th biweekly period. +# A tuple (None, expr, 0.0) where expr is the Pyomo expression for the upper bound of the high beta disjunct at the i-th biweekly period. # This represents the upper bound of the high beta disjunct at the i-th biweekly period. -# Note: +# Notes # The given function is given as the expression that the disjunctions are converted by big-M reformulation. # The binary variable m.y[i] is commented inside the model function. # """ @@ -270,20 +1917,24 @@ def _disj(model, i): # return (None, expr, 0.0) # model.highbeta_U = Constraint(model.S_beta, rule=highbeta_U) -# low beta disjuncts +# # low beta disjuncts # def lowbeta_U(m,i): # """ -# Args: +# Defines the upper bound constraint for the low transmission parameter beta in the SIR model. + +# Parameters +# ---------- # m (Pyomo.ConcreteModel): SIR disease model using a low/high transmission parameter # i (int): index of biweekly periods in the data set # Returns: -# A tuple (None, expr, 0.0) where expr is the Pyomo expression for the upper bound of the low beta disjunct at the i-th biweekly period. +# A tuple (None, expr, 0.0) where expr is the Pyomo expression for the upper bound of the low beta disjunct at the i-th biweekly period. # This represents the upper bound of the low beta disjunct at the i-th biweekly period. -# Note: -# The given function is given as the expression that the disjunctions are converted by big-M reformulation. -# The binary variable m.y[i] is commented inside the model function. +# Notes +# ----- +# The given function is given as the expression that the disjunctions are converted by big-M reformulation. +# The binary variable m.y[i] is commented inside the model function. # """ # expr = m.logbeta[i] - m.logbeta_low - bigM*(m.y[i]) # return (None, expr, 0.0) @@ -291,17 +1942,22 @@ def _disj(model, i): # def lowbeta_L(m,i): # """ -# Args: +# Defines the lower bound constraint for the low transmission parameter beta in the SIR model. + +# Parameters +# ---------- # m (Pyomo.ConcreteModel): SIR disease model using a low/high transmission parameter # i (int): index of biweekly periods in the data set -# Returns: -# A tuple (0.0, expr, None) where expr is the Pyomo expression for the lower bound of the low beta disjunct at the i-th biweekly period. +# Returns +# ------- +# A tuple (0.0, expr, None) where expr is the Pyomo expression for the lower bound of the low beta disjunct at the i-th biweekly period. # This represents the lower bound of the low beta disjunct at the i-th biweekly period. -# Note: -# The given function is given as the expression that the disjunctions are converted by big-M reformulation. -# The binary variable m.y[i] is commented inside the model function. +# Notes +# ----- +# This lower bound constraint is part of the model's disjunctive framework, allowing for the differentiation between low and high transmission rates. +# The big-M method integrates this constraint into the model based on the state of the binary decision variable `m.y[i]`. # """ # expr = m.logbeta[i] - m.logbeta_low # return (0.0, expr, None) @@ -311,5 +1967,7 @@ def _disj(model, i): if __name__ == "__main__": m = build_model() TransformationFactory('gdp.bigm').apply_to(m) - SolverFactory('gams').solve(m, solver='baron', tee=True, add_options=['option optcr=1e-6;']) + SolverFactory('gams').solve( + m, solver='baron', tee=True, add_options=['option optcr=1e-6;'] + ) m.obj.display() From e384ef014ca700a14a90a4a281c0192328214531 Mon Sep 17 00:00:00 2001 From: Albert Lee Date: Thu, 4 Apr 2024 18:47:06 -0400 Subject: [PATCH 25/99] Added docstring on the function definition. --- gdplib/pyomo_examples/batch_processing.py | 278 +++++++++++++++++++++- 1 file changed, 277 insertions(+), 1 deletion(-) diff --git a/gdplib/pyomo_examples/batch_processing.py b/gdplib/pyomo_examples/batch_processing.py index 00daaea..c6f660b 100644 --- a/gdplib/pyomo_examples/batch_processing.py +++ b/gdplib/pyomo_examples/batch_processing.py @@ -24,7 +24,13 @@ def build_model(): + """_summary_ + Returns + ------- + _type_ + _description_ + """ model = AbstractModel() # TODO: it looks like they set a bigM for each j. Which I need to look up how to do... @@ -33,7 +39,7 @@ def build_model(): # Constants from GAMS - StorageTankSizeFactor = 2*5 # btw, I know 2*5 is 10... I don't know why it's written this way in GAMS? + StorageTankSizeFactor = 10 StorageTankSizeFactorByProd = 3 MinFlow = -log(10000) VolumeLB = log(300) @@ -53,6 +59,20 @@ def build_model(): # TODO: this seems like an over-complicated way to accomplish this task... def filter_out_last(model, j): + """_summary_ + + Parameters + ---------- + model : _type_ + _description_ + j : _type_ + _description_ + + Returns + ------- + _type_ + _description_ + """ return j != model.STAGES.last() model.STAGESExceptLast = Set(initialize=model.STAGES, filter=filter_out_last) @@ -120,17 +140,73 @@ def get_log_coeffs(model, k): # GAMS never un-logs them, I don't think. And I think the GAMs ones # must be the log ones. def get_volume_bounds(model, j): + """_summary_ + + Parameters + ---------- + model : _type_ + _description_ + j : _type_ + _description_ + + Returns + ------- + _type_ + _description_ + """ return (model.volumeLB[j], model.volumeUB[j]) model.volume_log = Var(model.STAGES, bounds=get_volume_bounds) model.batchSize_log = Var(model.PRODUCTS, model.STAGES) model.cycleTime_log = Var(model.PRODUCTS) def get_unitsOutOfPhase_bounds(model, j): + """_summary_ + + Parameters + ---------- + model : _type_ + _description_ + j : _type_ + _description_ + + Returns + ------- + _type_ + _description_ + """ return (0, model.unitsOutOfPhaseUB[j]) model.unitsOutOfPhase_log = Var(model.STAGES, bounds=get_unitsOutOfPhase_bounds) def get_unitsInPhase_bounds(model, j): + """_summary_ + + Parameters + ---------- + model : _type_ + _description_ + j : _type_ + _description_ + + Returns + ------- + _type_ + _description_ + """ return (0, model.unitsInPhaseUB[j]) model.unitsInPhase_log = Var(model.STAGES, bounds=get_unitsInPhase_bounds) def get_storageTankSize_bounds(model, j): + """_summary_ + + Parameters + ---------- + model : _type_ + _description_ + j : _type_ + _description_ + + Returns + ------- + _type_ + _description_ + """ return (model.storageTankSizeLB[j], model.storageTankSizeUB[j]) # TODO: these bounds make it infeasible... model.storageTankSize_log = Var(model.STAGES, bounds=get_storageTankSize_bounds) @@ -142,6 +218,18 @@ def get_storageTankSize_bounds(model, j): # Objective def get_cost_rule(model): + """_summary_ + + Parameters + ---------- + model : _type_ + _description_ + + Returns + ------- + _type_ + _description_ + """ return model.Alpha1 * sum(exp(model.unitsInPhase_log[j] + model.unitsOutOfPhase_log[j] + \ model.Beta1 * model.volume_log[j]) for j in model.STAGES) +\ model.Alpha2 * sum(exp(model.Beta2 * model.storageTankSize_log[j]) for j in model.STAGESExceptLast) @@ -149,16 +237,60 @@ def get_cost_rule(model): # Constraints def processing_capacity_rule(model, j, i): + """_summary_ + + Parameters + ---------- + model : _type_ + _description_ + j : _type_ + _description_ + i : _type_ + _description_ + + Returns + ------- + _type_ + _description_ + """ return model.volume_log[j] >= log(model.ProductSizeFactor[i, j]) + model.batchSize_log[i, j] - \ model.unitsInPhase_log[j] model.processing_capacity = Constraint(model.STAGES, model.PRODUCTS, rule=processing_capacity_rule) def processing_time_rule(model, j, i): + """_summary_ + + Parameters + ---------- + model : _type_ + _description_ + j : _type_ + _description_ + i : _type_ + _description_ + + Returns + ------- + _type_ + _description_ + """ return model.cycleTime_log[i] >= log(model.ProcessingTime[i, j]) - model.batchSize_log[i, j] - \ model.unitsOutOfPhase_log[j] model.processing_time = Constraint(model.STAGES, model.PRODUCTS, rule=processing_time_rule) def finish_in_time_rule(model): + """_summary_ + + Parameters + ---------- + model : _type_ + _description_ + + Returns + ------- + _type_ + _description_ + """ return model.HorizonTime >= sum(model.ProductionAmount[i]*exp(model.cycleTime_log[i]) \ for i in model.PRODUCTS) model.finish_in_time = Constraint(rule=finish_in_time_rule) @@ -167,18 +299,92 @@ def finish_in_time_rule(model): # Disjunctions def storage_tank_selection_disjunct_rule(disjunct, selectStorageTank, j): + """_summary_ + + Parameters + ---------- + disjunct : _type_ + _description_ + selectStorageTank : _type_ + _description_ + j : _type_ + _description_ + + Returns + ------- + _type_ + _description_ + """ model = disjunct.model() def volume_stage_j_rule(disjunct, i): + """_summary_ + + Parameters + ---------- + disjunct : _type_ + _description_ + i : _type_ + _description_ + + Returns + ------- + _type_ + _description_ + """ return model.storageTankSize_log[j] >= log(model.StorageTankSizeFactor[j]) + \ model.batchSize_log[i, j] + def volume_stage_jPlus1_rule(disjunct, i): + """_summary_ + + Parameters + ---------- + disjunct : _type_ + _description_ + i : _type_ + _description_ + + Returns + ------- + _type_ + _description_ + """ return model.storageTankSize_log[j] >= log(model.StorageTankSizeFactor[j]) + \ model.batchSize_log[i, j+1] + def batch_size_rule(disjunct, i): + """_summary_ + + Parameters + ---------- + disjunct : _type_ + _description_ + i : _type_ + _description_ + + Returns + ------- + _type_ + _description_ + """ return inequality(-log(model.StorageTankSizeFactorByProd[i,j]), model.batchSize_log[i,j] - model.batchSize_log[i, j+1], log(model.StorageTankSizeFactorByProd[i,j])) def no_batch_rule(disjunct, i): + """_summary_ + + Parameters + ---------- + disjunct : _type_ + _description_ + i : _type_ + _description_ + + Returns + ------- + _type_ + _description_ + """ return model.batchSize_log[i,j] - model.batchSize_log[i,j+1] == 0 if selectStorageTank: @@ -195,27 +401,97 @@ def no_batch_rule(disjunct, i): rule=storage_tank_selection_disjunct_rule) def select_storage_tanks_rule(model, j): + """_summary_ + + Parameters + ---------- + model : _type_ + _description_ + j : _type_ + _description_ + + Returns + ------- + _type_ + _description_ + """ return [model.storage_tank_selection_disjunct[selectTank, j] for selectTank in [0,1]] model.select_storage_tanks = Disjunction(model.STAGESExceptLast, rule=select_storage_tanks_rule) # though this is a disjunction in the GAMs model, it is more efficiently formulated this way: # TODO: what on earth is k? def units_out_of_phase_rule(model, j): + """_summary_ + + Parameters + ---------- + model : _type_ + _description_ + j : _type_ + _description_ + + Returns + ------- + _type_ + _description_ + """ return model.unitsOutOfPhase_log[j] == sum(model.LogCoeffs[k] * model.outOfPhase[j,k] \ for k in model.PARALLELUNITS) model.units_out_of_phase = Constraint(model.STAGES, rule=units_out_of_phase_rule) def units_in_phase_rule(model, j): + """_summary_ + + Parameters + ---------- + model : _type_ + _description_ + j : _type_ + _description_ + + Returns + ------- + _type_ + _description_ + """ return model.unitsInPhase_log[j] == sum(model.LogCoeffs[k] * model.inPhase[j,k] \ for k in model.PARALLELUNITS) model.units_in_phase = Constraint(model.STAGES, rule=units_in_phase_rule) # and since I didn't do the disjunction as a disjunction, we need the XORs: def units_out_of_phase_xor_rule(model, j): + """_summary_ + + Parameters + ---------- + model : _type_ + _description_ + j : _type_ + _description_ + + Returns + ------- + _type_ + _description_ + """ return sum(model.outOfPhase[j,k] for k in model.PARALLELUNITS) == 1 model.units_out_of_phase_xor = Constraint(model.STAGES, rule=units_out_of_phase_xor_rule) def units_in_phase_xor_rule(model, j): + """_summary_ + + Parameters + ---------- + model : _type_ + _description_ + j : _type_ + _description_ + + Returns + ------- + _type_ + _description_ + """ return sum(model.inPhase[j,k] for k in model.PARALLELUNITS) == 1 model.units_in_phase_xor = Constraint(model.STAGES, rule=units_in_phase_xor_rule) From 94e811821ed8d9c969d4534f876baa0e1a94d008 Mon Sep 17 00:00:00 2001 From: Albert Lee Date: Thu, 4 Apr 2024 21:54:59 -0400 Subject: [PATCH 26/99] Intermedia commit for merge --- gdplib/pyomo_examples/batch_processing.py | 207 +++++++++++++--------- 1 file changed, 120 insertions(+), 87 deletions(-) diff --git a/gdplib/pyomo_examples/batch_processing.py b/gdplib/pyomo_examples/batch_processing.py index c6f660b..40d2f09 100644 --- a/gdplib/pyomo_examples/batch_processing.py +++ b/gdplib/pyomo_examples/batch_processing.py @@ -1,12 +1,3 @@ -""" -batch_processing.py -This problem seeks to minimize the investment cost in the design of a plant with multiple units in parallel and intermediate storage tanks [1]. - -Reference: - [1] Ravemark, E. Optimization models for design and operation of chemical batch processes. Ph.D. Thesis, ETH Zurich, 1995. - [2] Vecchietti, A.; Grossmann, I. E. LOGMIP: a disjunctive 0-1 non-linear optimizer for process system models. Computers and Chemical Engineering 1994, 23, 555–565. - -""" from os.path import join from pyomo.common.fileutils import this_file_dir @@ -24,12 +15,28 @@ def build_model(): - """_summary_ + """ + Constructs and initializes a Pyomo model for the batch processing problem. + + The model is designed to minimize the total cost associated with the design and operation of a plant consisting of multiple + parallel processing units with intermediate storage tanks. + It involves determining the optimal number and sizes of processing units, batch sizes for different products at various stages, and sizes and + placements of storage tanks to ensure operational efficiency while meeting production requirements within a specified time horizon. + + Parameters + ---------- + None Returns ------- - _type_ - _description_ + Pyomo.ConcreteModel + An instance of the Pyomo ConcreteModel class representing the batch processing optimization model, + ready to be solved with an appropriate solver. + + References + ---------- + Ravemark, E. Optimization models for design and operation of chemical batch processes. Ph.D. Thesis, ETH Zurich, 1995. + Vecchietti, A., & Grossmann, I. E. (1999). LOGMIP: a disjunctive 0–1 non-linear optimizer for process system models. Computers & chemical engineering, 23(4-5), 555-565. """ model = AbstractModel() @@ -53,9 +60,9 @@ def build_model(): # Sets - model.PRODUCTS = Set() - model.STAGES = Set(ordered=True) - model.PARALLELUNITS = Set(ordered=True) + model.PRODUCTS = Set(doc='Set of Products') + model.STAGES = Set(doc='Set of Stages', ordered=True) + model.PARALLELUNITS = Set(doc='Set of Parallel Units', ordered=True) # TODO: this seems like an over-complicated way to accomplish this task... def filter_out_last(model, j): @@ -63,10 +70,11 @@ def filter_out_last(model, j): Parameters ---------- - model : _type_ - _description_ - j : _type_ - _description_ + model : Pyomo.ConcreteModel + The Pyomo model for the batch processing optimization problem. + j : int + The index representing the stage in the processing sequence. Stages are ordered and include various + processing steps required for product completion. Returns ------- @@ -102,6 +110,20 @@ def filter_out_last(model, j): # I made PRODUCTS ordered so I could do this... Is that bad? And it does index # from 1, right? def get_log_coeffs(model, k): + """_summary_ + + Parameters + ---------- + model : Pyomo.ConcreteModel + The Pyomo model for the batch processing optimization problem. + k : _type_ + _description_ + + Returns + ------- + _type_ + _description_ + """ return log(model.PARALLELUNITS.ord(k)) model.LogCoeffs = Param(model.PARALLELUNITS, initialize=get_log_coeffs) @@ -144,10 +166,10 @@ def get_volume_bounds(model, j): Parameters ---------- - model : _type_ - _description_ - j : _type_ - _description_ + model : Pyomo.ConcreteModel + The Pyomo model for the batch processing optimization problem. + j : int + Index for the processing stages in the plant. Stages are ordered and include various processing steps. Returns ------- @@ -158,15 +180,16 @@ def get_volume_bounds(model, j): model.volume_log = Var(model.STAGES, bounds=get_volume_bounds) model.batchSize_log = Var(model.PRODUCTS, model.STAGES) model.cycleTime_log = Var(model.PRODUCTS) + def get_unitsOutOfPhase_bounds(model, j): """_summary_ Parameters ---------- - model : _type_ - _description_ - j : _type_ - _description_ + model : Pyomo.ConcreteModel + The Pyomo model for the batch processing optimization problem. + j : int + Index for the processing stages in the plant. Stages are ordered and include various processing steps. Returns ------- @@ -180,10 +203,10 @@ def get_unitsInPhase_bounds(model, j): Parameters ---------- - model : _type_ - _description_ - j : _type_ - _description_ + model : Pyomo.ConcreteModel + The Pyomo model for the batch processing optimization problem. + j : int + Index for the processing stages in the plant. Stages are ordered and include various processing steps. Returns ------- @@ -192,15 +215,16 @@ def get_unitsInPhase_bounds(model, j): """ return (0, model.unitsInPhaseUB[j]) model.unitsInPhase_log = Var(model.STAGES, bounds=get_unitsInPhase_bounds) + def get_storageTankSize_bounds(model, j): """_summary_ Parameters ---------- - model : _type_ - _description_ - j : _type_ - _description_ + model : Pyomo.ConcreteModel + The Pyomo model for the batch processing optimization problem. + j : int + Index for the processing stages in the plant. Stages are ordered and include various processing steps. Returns ------- @@ -222,8 +246,8 @@ def get_cost_rule(model): Parameters ---------- - model : _type_ - _description_ + model : Pyomo.ConcreteModel + The Pyomo model for the batch processing optimization problem. Returns ------- @@ -241,17 +265,18 @@ def processing_capacity_rule(model, j, i): Parameters ---------- - model : _type_ - _description_ - j : _type_ - _description_ - i : _type_ - _description_ + model : Pyomo.ConcreteModel + The Pyomo model for the batch processing optimization problem. + j : int + Index for the processing stages in the plant. Stages are ordered and include various processing steps. + i : int + The index representing a specific product. Products have unique processing requirements, including + batch sizes and processing times, that vary by stage. Returns ------- - _type_ - _description_ + Pyomo.Constraint.Expression + A Pyomo expression that defines the processing capacity constraint for product `i` at stage `j`. """ return model.volume_log[j] >= log(model.ProductSizeFactor[i, j]) + model.batchSize_log[i, j] - \ model.unitsInPhase_log[j] @@ -262,17 +287,19 @@ def processing_time_rule(model, j, i): Parameters ---------- - model : _type_ - _description_ - j : _type_ - _description_ - i : _type_ - _description_ + model : Pyomo.ConcreteModel + The Pyomo model for the batch processing optimization problem. + j : int + Index for the processing stages in the plant. Stages are ordered and include various processing steps. + i : int + Product index, representing different products being processed in the plant, each with its own set of + processing times across various stages. Returns ------- - _type_ - _description_ + Pyomo.Constraint.Expression + A Pyomo expression defining the constraint that the cycle time for processing product `i` at stage `j` + must not exceed the maximum allowed, considering the batch size and the units out of phase at this stage. """ return model.cycleTime_log[i] >= log(model.ProcessingTime[i, j]) - model.batchSize_log[i, j] - \ model.unitsOutOfPhase_log[j] @@ -283,8 +310,8 @@ def finish_in_time_rule(model): Parameters ---------- - model : _type_ - _description_ + model : Pyomo.ConcreteModel + The Pyomo model for the batch processing optimization problem. Returns ------- @@ -307,8 +334,8 @@ def storage_tank_selection_disjunct_rule(disjunct, selectStorageTank, j): _description_ selectStorageTank : _type_ _description_ - j : _type_ - _description_ + j : int + Index for the processing stages in the plant. Stages are ordered and include various processing steps. Returns ------- @@ -317,14 +344,16 @@ def storage_tank_selection_disjunct_rule(disjunct, selectStorageTank, j): """ model = disjunct.model() def volume_stage_j_rule(disjunct, i): - """_summary_ + """ + Parameters ---------- - disjunct : _type_ - _description_ - i : _type_ + disjunct : _description_ + i : int + Product index, representing different products being processed in the plant, each with its own set of + processing times across various stages. Returns ------- @@ -341,8 +370,9 @@ def volume_stage_jPlus1_rule(disjunct, i): ---------- disjunct : _type_ _description_ - i : _type_ - _description_ + i : int + Product index, representing different products being processed in the plant, each with its own set of + processing times across various stages. Returns ------- @@ -359,8 +389,9 @@ def batch_size_rule(disjunct, i): ---------- disjunct : _type_ _description_ - i : _type_ - _description_ + i : int + Product index, representing different products being processed in the plant, each with its own set of + processing times across various stages. Returns ------- @@ -370,6 +401,7 @@ def batch_size_rule(disjunct, i): return inequality(-log(model.StorageTankSizeFactorByProd[i,j]), model.batchSize_log[i,j] - model.batchSize_log[i, j+1], log(model.StorageTankSizeFactorByProd[i,j])) + def no_batch_rule(disjunct, i): """_summary_ @@ -377,8 +409,9 @@ def no_batch_rule(disjunct, i): ---------- disjunct : _type_ _description_ - i : _type_ - _description_ + i : int + Product index, representing different products being processed in the plant, each with its own set of + processing times across various stages. Returns ------- @@ -405,10 +438,10 @@ def select_storage_tanks_rule(model, j): Parameters ---------- - model : _type_ - _description_ - j : _type_ - _description_ + model : Pyomo.ConcreteModel + The Pyomo model for the batch processing optimization problem. + j : int + Index for the processing stages in the plant. Stages are ordered and include various processing steps. Returns ------- @@ -425,10 +458,10 @@ def units_out_of_phase_rule(model, j): Parameters ---------- - model : _type_ - _description_ - j : _type_ - _description_ + model : Pyomo.ConcreteModel + The Pyomo model for the batch processing optimization problem. + j : int + Index for the processing stages in the plant. Stages are ordered and include various processing steps. Returns ------- @@ -444,10 +477,10 @@ def units_in_phase_rule(model, j): Parameters ---------- - model : _type_ - _description_ - j : _type_ - _description_ + model : Pyomo.ConcreteModel + The Pyomo model for the batch processing optimization problem. + j : int + Index for the processing stages in the plant. Stages are ordered and include various processing steps. Returns ------- @@ -464,10 +497,10 @@ def units_out_of_phase_xor_rule(model, j): Parameters ---------- - model : _type_ - _description_ - j : _type_ - _description_ + model : Pyomo.ConcreteModel + The Pyomo model for the batch processing optimization problem. + j : int + Index for the processing stages in the plant. Stages are ordered and include various processing steps. Returns ------- @@ -482,10 +515,10 @@ def units_in_phase_xor_rule(model, j): Parameters ---------- - model : _type_ - _description_ - j : _type_ - _description_ + model : Pyomo.ConcreteModel + The Pyomo model for the batch processing optimization problem. + j : int + Index for the processing stages in the plant. Stages are ordered and include various processing steps. Returns ------- From 2e1a9c84f7c39a9c6a306ef38886d4dd0ab5a538 Mon Sep 17 00:00:00 2001 From: Albert Lee Date: Thu, 4 Apr 2024 23:07:07 -0400 Subject: [PATCH 27/99] Added the documentation of batch processing py without disjunction part --- gdplib/pyomo_examples/batch_processing.py | 125 +++++++++++++--------- 1 file changed, 73 insertions(+), 52 deletions(-) diff --git a/gdplib/pyomo_examples/batch_processing.py b/gdplib/pyomo_examples/batch_processing.py index 40d2f09..c03eda3 100644 --- a/gdplib/pyomo_examples/batch_processing.py +++ b/gdplib/pyomo_examples/batch_processing.py @@ -4,14 +4,14 @@ from pyomo.environ import * from pyomo.gdp import * -'''Problem from http://www.minlp.org/library/problem/index.php?i=172&lib=GDP -We are minimizing the cost of a design of a plant with parallel processing units and storage tanks -in between. We decide the number and volume of units, and the volume and location of the storage -tanks. The problem is convexified and has a nonlinear objective and global constraints +# Problem from http://www.minlp.org/library/problem/index.php?i=172&lib=GDP +# We are minimizing the cost of a design of a plant with parallel processing units and storage tanks +# in between. We decide the number and volume of units, and the volume and location of the storage +# tanks. The problem is convexified and has a nonlinear objective and global constraints -NOTE: When I refer to 'gams' in the comments, that is Batch101006_BM.gms for now. It's confusing -because the _opt file is different (It has hard-coded bigM parameters so that each constraint -has the "optimal" bigM).''' +# NOTE: When I refer to 'gams' in the comments, that is Batch101006_BM.gms for now. It's confusing +# because the _opt file is different (It has hard-coded bigM parameters so that each constraint +# has the "optimal" bigM). def build_model(): @@ -66,7 +66,9 @@ def build_model(): # TODO: this seems like an over-complicated way to accomplish this task... def filter_out_last(model, j): - """_summary_ + """ + Filters out the last stage from the set of stages to avoid considering it in certain constraints + or disjunctions where the next stage would be required but doesn't exist. Parameters ---------- @@ -78,8 +80,8 @@ def filter_out_last(model, j): Returns ------- - _type_ - _description_ + bool + Returns True if the stage is not the last one in the set, False otherwise. """ return j != model.STAGES.last() model.STAGESExceptLast = Set(initialize=model.STAGES, filter=filter_out_last) @@ -90,11 +92,11 @@ def filter_out_last(model, j): # Parameters - model.HorizonTime = Param() - model.Alpha1 = Param() - model.Alpha2 = Param() - model.Beta1 = Param() - model.Beta2 = Param() + model.HorizonTime = Param(doc='Horizon Time') + model.Alpha1 = Param(doc='Cost Parameter of the units') + model.Alpha2 = Param(doc='Cost Parameter of the intermediate storage tanks') + model.Beta1 = Param(doc='Exponent Parameter of the units') + model.Beta2 = Param(doc='Exponent Parameter of the intermediate storage tanks') model.ProductionAmount = Param(model.PRODUCTS) model.ProductSizeFactor = Param(model.PRODUCTS, model.STAGES) @@ -110,27 +112,29 @@ def filter_out_last(model, j): # I made PRODUCTS ordered so I could do this... Is that bad? And it does index # from 1, right? def get_log_coeffs(model, k): - """_summary_ + """ + Calculates the logarithmic coefficients used in the model, typically for transforming linear + relationships into logarithmic form for optimization purposes. Parameters ---------- model : Pyomo.ConcreteModel The Pyomo model for the batch processing optimization problem. - k : _type_ - _description_ + k : int + The index representing a parallel unit. Returns ------- - _type_ - _description_ + float + The logarithm of the position of the parallel unit within its set, used as a coefficient in the model. """ return log(model.PARALLELUNITS.ord(k)) model.LogCoeffs = Param(model.PARALLELUNITS, initialize=get_log_coeffs) # bounds - model.volumeLB = Param(model.STAGES, default=VolumeLB) - model.volumeUB = Param(model.STAGES, default=VolumeUB) + model.volumeLB = Param(model.STAGES, default=VolumeLB, doc='Lower Bound of Volume of the Units') + model.volumeUB = Param(model.STAGES, default=VolumeUB, doc='Upper Bound of Volume of the Units') model.storageTankSizeLB = Param(model.STAGES, default=StorageTankSizeLB) model.storageTankSizeUB = Param(model.STAGES, default=StorageTankSizeUB) model.unitsInPhaseUB = Param(model.STAGES, default=UnitsInPhaseUB) @@ -162,7 +166,8 @@ def get_log_coeffs(model, k): # GAMS never un-logs them, I don't think. And I think the GAMs ones # must be the log ones. def get_volume_bounds(model, j): - """_summary_ + """ + Defines the bounds for the volume of processing units at each stage. Parameters ---------- @@ -173,8 +178,8 @@ def get_volume_bounds(model, j): Returns ------- - _type_ - _description_ + tuple + A tuple containing the lower and upper bounds for the volume of processing units at stage j.. """ return (model.volumeLB[j], model.volumeUB[j]) model.volume_log = Var(model.STAGES, bounds=get_volume_bounds) @@ -182,7 +187,8 @@ def get_volume_bounds(model, j): model.cycleTime_log = Var(model.PRODUCTS) def get_unitsOutOfPhase_bounds(model, j): - """_summary_ + """ + Defines the bounds for the logarithmic representation of the number of units out of phase at each stage. Parameters ---------- @@ -193,13 +199,15 @@ def get_unitsOutOfPhase_bounds(model, j): Returns ------- - _type_ - _description_ + tuple + A tuple containing the lower and upper bounds for the logarithmic representation of the number of units out of phase at stage j. """ return (0, model.unitsOutOfPhaseUB[j]) model.unitsOutOfPhase_log = Var(model.STAGES, bounds=get_unitsOutOfPhase_bounds) + def get_unitsInPhase_bounds(model, j): - """_summary_ + """ + Defines the allowable bounds for the logarithmic number of processing units operating in phase at a given stage in the manufacturing process. Parameters ---------- @@ -210,14 +218,15 @@ def get_unitsInPhase_bounds(model, j): Returns ------- - _type_ - _description_ + tuple + A tuple containing the minimum and maximum bounds for the logarithmic number of units in phase at stage j, ensuring model constraints are met. """ return (0, model.unitsInPhaseUB[j]) model.unitsInPhase_log = Var(model.STAGES, bounds=get_unitsInPhase_bounds) def get_storageTankSize_bounds(model, j): - """_summary_ + """ + Determines the lower and upper bounds for the logarithmic representation of the storage tank size between stages j and j+1. Parameters ---------- @@ -228,8 +237,8 @@ def get_storageTankSize_bounds(model, j): Returns ------- - _type_ - _description_ + tuple + A tuple containing the lower and upper bounds for the storage tank size at the specified stage. """ return (model.storageTankSizeLB[j], model.storageTankSizeUB[j]) # TODO: these bounds make it infeasible... @@ -242,7 +251,8 @@ def get_storageTankSize_bounds(model, j): # Objective def get_cost_rule(model): - """_summary_ + """ + Defines the objective function for the model, representing the total cost of the plant design. Parameters ---------- @@ -251,8 +261,13 @@ def get_cost_rule(model): Returns ------- - _type_ - _description_ + Pyomo.Expression + A Pyomo expression representing the total cost of the plant design. + + Notes + ----- + The cost is a function of the volume of processing units and the size of storage tanks, each scaled by respective cost + parameters and exponentiated to reflect non-linear cost relationships. """ return model.Alpha1 * sum(exp(model.unitsInPhase_log[j] + model.unitsOutOfPhase_log[j] + \ model.Beta1 * model.volume_log[j]) for j in model.STAGES) +\ @@ -261,7 +276,9 @@ def get_cost_rule(model): # Constraints def processing_capacity_rule(model, j, i): - """_summary_ + """ + Ensures that the volume of each processing unit at stage j is sufficient to accommodate the batch size of product i, + taking into account the size factor of the product and the number of units in phase at that stage. Parameters ---------- @@ -275,7 +292,7 @@ def processing_capacity_rule(model, j, i): Returns ------- - Pyomo.Constraint.Expression + Pyomo.Expression A Pyomo expression that defines the processing capacity constraint for product `i` at stage `j`. """ return model.volume_log[j] >= log(model.ProductSizeFactor[i, j]) + model.batchSize_log[i, j] - \ @@ -283,7 +300,8 @@ def processing_capacity_rule(model, j, i): model.processing_capacity = Constraint(model.STAGES, model.PRODUCTS, rule=processing_capacity_rule) def processing_time_rule(model, j, i): - """_summary_ + """ + Ensures that the cycle time for product i at stage j, adjusted for the number of out-of-phase units, meets the required processing time. Parameters ---------- @@ -297,7 +315,7 @@ def processing_time_rule(model, j, i): Returns ------- - Pyomo.Constraint.Expression + Pyomo.Expression A Pyomo expression defining the constraint that the cycle time for processing product `i` at stage `j` must not exceed the maximum allowed, considering the batch size and the units out of phase at this stage. """ @@ -306,7 +324,8 @@ def processing_time_rule(model, j, i): model.processing_time = Constraint(model.STAGES, model.PRODUCTS, rule=processing_time_rule) def finish_in_time_rule(model): - """_summary_ + """ + Ensures that the total production time across all products does not exceed the defined time horizon for the process. Parameters ---------- @@ -315,8 +334,8 @@ def finish_in_time_rule(model): Returns ------- - _type_ - _description_ + Pyomo.Expression + A Pyomo constraint expression ensuring the total production time does not exceed the time horizon for the plant. """ return model.HorizonTime >= sum(model.ProductionAmount[i]*exp(model.cycleTime_log[i]) \ for i in model.PRODUCTS) @@ -345,7 +364,7 @@ def storage_tank_selection_disjunct_rule(disjunct, selectStorageTank, j): model = disjunct.model() def volume_stage_j_rule(disjunct, i): """ - + Parameters ---------- @@ -434,7 +453,8 @@ def no_batch_rule(disjunct, i): rule=storage_tank_selection_disjunct_rule) def select_storage_tanks_rule(model, j): - """_summary_ + """ + Defines a disjunction for the model to choose between including or not including a storage tank between stages j and j+1. Parameters ---------- @@ -445,8 +465,8 @@ def select_storage_tanks_rule(model, j): Returns ------- - _type_ - _description_ + list + A list of disjuncts representing the choices for including or not including a storage tank between stages j and j+1. """ return [model.storage_tank_selection_disjunct[selectTank, j] for selectTank in [0,1]] model.select_storage_tanks = Disjunction(model.STAGESExceptLast, rule=select_storage_tanks_rule) @@ -505,13 +525,14 @@ def units_out_of_phase_xor_rule(model, j): Returns ------- _type_ - _description_ + A Pyomo constraint expression calculating the logarithmic representation of the number of units out of phase at stage j """ return sum(model.outOfPhase[j,k] for k in model.PARALLELUNITS) == 1 model.units_out_of_phase_xor = Constraint(model.STAGES, rule=units_out_of_phase_xor_rule) def units_in_phase_xor_rule(model, j): - """_summary_ + """ + Enforces an exclusive OR (XOR) constraint ensuring that exactly one configuration for the number of units out of phase is selected at stage j. Parameters ---------- @@ -522,8 +543,8 @@ def units_in_phase_xor_rule(model, j): Returns ------- - _type_ - _description_ + Pyomo.Constraint + A Pyomo constraint expression enforcing the XOR condition for units out of phase at stage j. """ return sum(model.inPhase[j,k] for k in model.PARALLELUNITS) == 1 model.units_in_phase_xor = Constraint(model.STAGES, rule=units_in_phase_xor_rule) From 7925ef0dc04da783a8ce1dc93e56eccfe8437b02 Mon Sep 17 00:00:00 2001 From: Albert Lee Date: Fri, 5 Apr 2024 10:52:11 -0400 Subject: [PATCH 28/99] Reverted the change of the data --- gdplib/pyomo_examples/disease_model.py | 1583 +----------------------- 1 file changed, 9 insertions(+), 1574 deletions(-) diff --git a/gdplib/pyomo_examples/disease_model.py b/gdplib/pyomo_examples/disease_model.py index 269b69c..67b2347 100644 --- a/gdplib/pyomo_examples/disease_model.py +++ b/gdplib/pyomo_examples/disease_model.py @@ -38,1580 +38,15 @@ def build_model(): """ # import data # Population Data - pop = [ - 15.881351, - 15.881339, - 15.881320, - 15.881294, - 15.881261, - 15.881223, - 15.881180, - 15.881132, - 15.881079, - 15.881022, - 15.880961, - 15.880898, - 15.880832, - 15.880764, - 15.880695, - 15.880624, - 15.880553, - 15.880480, - 15.880409, - 15.880340, - 15.880270, - 15.880203, - 15.880138, - 15.880076, - 15.880016, - 15.879960, - 15.879907, - 15.879852, - 15.879799, - 15.879746, - 15.879693, - 15.879638, - 15.879585, - 15.879531, - 15.879477, - 15.879423, - 15.879370, - 15.879315, - 15.879262, - 15.879209, - 15.879155, - 15.879101, - 15.879048, - 15.878994, - 15.878940, - 15.878886, - 15.878833, - 15.878778, - 15.878725, - 15.878672, - 15.878618, - 15.878564, - 15.878510, - 15.878457, - 15.878402, - 15.878349, - 15.878295, - 15.878242, - 15.878187, - 15.878134, - 15.878081, - 15.878026, - 15.877973, - 15.877919, - 15.877864, - 15.877811, - 15.877758, - 15.877704, - 15.877650, - 15.877596, - 15.877543, - 15.877488, - 15.877435, - 15.877381, - 15.877326, - 15.877273, - 15.877220, - 15.877166, - 15.877111, - 15.877058, - 15.877005, - 15.876950, - 15.876896, - 15.876843, - 15.876789, - 15.876735, - 15.876681, - 15.876628, - 15.876573, - 15.876520, - 15.876466, - 15.876411, - 15.876358, - 15.876304, - 15.876251, - 15.876196, - 15.876143, - 15.876089, - 15.876034, - 15.875981, - 15.875927, - 15.875872, - 15.875819, - 15.875765, - 15.875712, - 15.875657, - 15.875604, - 15.875550, - 15.875495, - 15.875442, - 15.875388, - 15.875335, - 15.875280, - 15.875226, - 15.875173, - 15.875118, - 15.875064, - 15.875011, - 15.874956, - 15.874902, - 15.874849, - 15.874795, - 15.874740, - 15.874687, - 15.874633, - 15.874578, - 15.874525, - 15.874471, - 15.874416, - 15.874363, - 15.874309, - 15.874256, - 15.874201, - 15.874147, - 15.874094, - 15.874039, - 15.873985, - 15.873931, - 15.873878, - 15.873823, - 15.873769, - 15.873716, - 15.873661, - 15.873607, - 15.873554, - 15.873499, - 15.873445, - 15.873391, - 15.873338, - 15.873283, - 15.873229, - 15.873175, - 15.873121, - 15.873067, - 15.873013, - 15.872960, - 15.872905, - 15.872851, - 15.872797, - 15.872742, - 15.872689, - 15.872635, - 15.872580, - 15.872526, - 15.872473, - 15.872419, - 15.872364, - 15.872310, - 15.872256, - 15.872202, - 15.872148, - 15.872094, - 15.872039, - 15.871985, - 15.871932, - 15.871878, - 15.871823, - 15.871769, - 15.871715, - 15.871660, - 15.871607, - 15.871553, - 15.871499, - 15.871444, - 15.871390, - 15.871337, - 15.871282, - 15.871228, - 15.871174, - 15.871119, - 15.871065, - 15.871012, - 15.870958, - 15.870903, - 15.870849, - 15.870795, - 15.870740, - 15.870686, - 15.870633, - 15.870577, - 15.870524, - 15.870470, - 15.870416, - 15.870361, - 15.870307, - 15.870253, - 15.870198, - 15.870144, - 15.870091, - 15.870037, - 15.869982, - 15.869928, - 15.869874, - 15.869819, - 15.869765, - 15.869711, - 15.869656, - 15.869602, - 15.869548, - 15.869495, - 15.869439, - 15.869386, - 15.869332, - 15.869277, - 15.869223, - 15.869169, - 15.869114, - 15.869060, - 15.869006, - 15.868952, - 15.868897, - 15.868843, - 15.868789, - 15.868734, - 15.868679, - 15.868618, - 15.868556, - 15.868489, - 15.868421, - 15.868351, - 15.868280, - 15.868208, - 15.868134, - 15.868063, - 15.867991, - 15.867921, - 15.867852, - 15.867785, - 15.867721, - 15.867659, - 15.867601, - 15.867549, - 15.867499, - 15.867455, - 15.867416, - 15.867383, - 15.867357, - 15.867338, - 15.867327, - 15.867321, - 15.867327, - 15.867338, - 15.867359, - 15.867386, - 15.867419, - 15.867459, - 15.867505, - 15.867555, - 15.867610, - 15.867671, - 15.867734, - 15.867801, - 15.867869, - 15.867941, - 15.868012, - 15.868087, - 15.868161, - 15.868236, - 15.868310, - 15.868384, - 15.868457, - 15.868527, - 15.868595, - 15.868661, - 15.868722, - 15.868780, - 15.868837, - 15.868892, - 15.868948, - 15.869005, - 15.869061, - 15.869116, - 15.869173, - 15.869229, - 15.869284, - 15.869341, - 15.869397, - 15.869452, - 15.869509, - 15.869565, - 15.869620, - 15.869677, - 15.869733, - 15.869788, - 15.869845, - 15.869901, - 15.869956, - 15.870012, - 15.870069, - 15.870124, - 15.870180, - 15.870237, - 15.870292, - 15.870348, - 15.870405, - 15.870461, - 15.870516, - 15.870572, - 15.870629, - 15.870684, - 15.870740, - 15.870796, - 15.870851, - 15.870908, - 15.870964, - 15.871019, - 15.871076, - 15.871132, - 15.871187, - 15.871243, - 15.871300, - 15.871355, - 15.871411, - 15.871467, - 15.871522, - 15.871579, - 15.871635, - 15.871691, - 15.871746, - 15.871802, - 15.871859, - 15.871914, - 15.871970, - 15.872026, - 15.872081, - 15.872138, - 15.872194, - 15.872249, - 15.872305, - 15.872361, - 15.872416, - 15.872473, - 15.872529, - 15.872584, - 15.872640, - 15.872696, - 15.872751, - 15.872807, - 15.872864, - 15.872919, - 15.872975, - 15.873031, - 15.873087, - 15.873142, - 15.873198, - 15.873255, - 15.873310, - 15.873366, - 15.873422, - 15.873477, - 15.873533, - 15.873589, - 15.873644, - 15.873700, - 15.873757, - 15.873811, - 15.873868, - 15.873924, - 15.873979, - 15.874035, - 15.874091, - 15.874146, - 15.874202, - 15.874258, - 15.874313, - 15.874369, - 15.874425, - 15.874481, - 15.874536, - 15.874592, - ] - - # Logarithm of Infected Data - logIstar = [ - 7.943245, - 8.269994, - 8.517212, - 8.814208, - 9.151740, - 9.478472, - 9.559847, - 9.664087, - 9.735378, - 9.852583, - 9.692265, - 9.498807, - 9.097634, - 8.388878, - 7.870516, - 7.012956, - 6.484941, - 5.825368, - 5.346815, - 5.548361, - 5.706732, - 5.712617, - 5.709714, - 5.696888, - 5.530087, - 5.826563, - 6.643563, - 7.004292, - 7.044663, - 7.190259, - 7.335926, - 7.516861, - 7.831779, - 8.188895, - 8.450204, - 8.801436, - 8.818379, - 8.787658, - 8.601685, - 8.258338, - 7.943364, - 7.425585, - 7.062834, - 6.658307, - 6.339600, - 6.526984, - 6.679178, - 6.988758, - 7.367331, - 7.746694, - 8.260558, - 8.676522, - 9.235582, - 9.607778, - 9.841917, - 10.081571, - 10.216090, - 10.350366, - 10.289668, - 10.248842, - 10.039504, - 9.846343, - 9.510392, - 9.190923, - 8.662465, - 7.743221, - 7.128458, - 5.967898, - 5.373883, - 5.097497, - 4.836570, - 5.203345, - 5.544798, - 5.443047, - 5.181152, - 5.508669, - 6.144130, - 6.413744, - 6.610423, - 6.748885, - 6.729511, - 6.789841, - 6.941034, - 7.093516, - 7.307039, - 7.541077, - 7.644803, - 7.769145, - 7.760187, - 7.708017, - 7.656795, - 7.664983, - 7.483828, - 6.887324, - 6.551093, - 6.457449, - 6.346064, - 6.486300, - 6.612378, - 6.778753, - 6.909477, - 7.360570, - 8.150303, - 8.549044, - 8.897572, - 9.239323, - 9.538751, - 9.876531, - 10.260911, - 10.613536, - 10.621510, - 10.661115, - 10.392899, - 10.065536, - 9.920090, - 9.933097, - 9.561691, - 8.807713, - 8.263463, - 7.252184, - 6.669083, - 5.877763, - 5.331878, - 5.356563, - 5.328469, - 5.631146, - 6.027497, - 6.250717, - 6.453919, - 6.718444, - 7.071636, - 7.348905, - 7.531528, - 7.798226, - 8.197941, - 8.578809, - 8.722964, - 8.901152, - 8.904370, - 8.889865, - 8.881902, - 8.958903, - 8.721281, - 8.211509, - 7.810624, - 7.164607, - 6.733688, - 6.268503, - 5.905983, - 5.900432, - 5.846547, - 6.245427, - 6.786271, - 7.088480, - 7.474295, - 7.650063, - 7.636703, - 7.830990, - 8.231516, - 8.584816, - 8.886908, - 9.225216, - 9.472778, - 9.765505, - 9.928623, - 10.153033, - 10.048574, - 9.892620, - 9.538818, - 8.896100, - 8.437584, - 7.819738, - 7.362598, - 6.505880, - 5.914972, - 6.264584, - 6.555019, - 6.589319, - 6.552029, - 6.809771, - 7.187616, - 7.513918, - 8.017712, - 8.224957, - 8.084474, - 8.079148, - 8.180991, - 8.274269, - 8.413748, - 8.559599, - 8.756090, - 9.017927, - 9.032720, - 9.047983, - 8.826873, - 8.366489, - 8.011876, - 7.500830, - 7.140406, - 6.812626, - 6.538719, - 6.552218, - 6.540129, - 6.659927, - 6.728530, - 7.179692, - 7.989210, - 8.399173, - 8.781128, - 9.122303, - 9.396378, - 9.698512, - 9.990104, - 10.276543, - 10.357284, - 10.465869, - 10.253833, - 10.018503, - 9.738407, - 9.484367, - 9.087025, - 8.526409, - 8.041126, - 7.147168, - 6.626706, - 6.209446, - 5.867231, - 5.697439, - 5.536769, - 5.421413, - 5.238297, - 5.470136, - 5.863007, - 6.183083, - 6.603569, - 6.906278, - 7.092324, - 7.326612, - 7.576052, - 7.823430, - 7.922775, - 8.041677, - 8.063403, - 8.073229, - 8.099726, - 8.168522, - 8.099041, - 8.011404, - 7.753147, - 6.945211, - 6.524244, - 6.557723, - 6.497742, - 6.256247, - 5.988794, - 6.268093, - 6.583316, - 7.106842, - 8.053929, - 8.508237, - 8.938915, - 9.311863, - 9.619753, - 9.931745, - 10.182361, - 10.420978, - 10.390829, - 10.389230, - 10.079342, - 9.741479, - 9.444561, - 9.237448, - 8.777687, - 7.976436, - 7.451502, - 6.742856, - 6.271545, - 5.782289, - 5.403089, - 5.341954, - 5.243509, - 5.522993, - 5.897001, - 6.047042, - 6.100738, - 6.361727, - 6.849562, - 7.112544, - 7.185346, - 7.309412, - 7.423746, - 7.532142, - 7.510318, - 7.480175, - 7.726362, - 8.061117, - 8.127072, - 8.206166, - 8.029634, - 7.592953, - 7.304869, - 7.005394, - 6.750019, - 6.461377, - 6.226432, - 6.287047, - 6.306452, - 6.783694, - 7.450957, - 7.861692, - 8.441530, - 8.739626, - 8.921994, - 9.168961, - 9.428077, - 9.711664, - 10.032714, - 10.349937, - 10.483985, - 10.647475, - 10.574038, - 10.522431, - 10.192246, - 9.756246, - 9.342511, - 8.872072, - 8.414189, - 7.606582, - 7.084701, - 6.149903, - 5.517257, - 5.839429, - 6.098090, - 6.268935, - 6.475965, - 6.560543, - 6.598942, - 6.693938, - 6.802531, - 6.934345, - 7.078370, - 7.267736, - 7.569640, - 7.872204, - 8.083603, - 8.331226, - 8.527144, - 8.773523, - 8.836599, - 8.894303, - 8.808326, - 8.641717, - 8.397901, - 7.849034, - 7.482899, - 7.050252, - 6.714103, - 6.900603, - 7.050765, - 7.322905, - 7.637986, - 8.024340, - 8.614505, - 8.933591, - 9.244008, - 9.427410, - 9.401385, - 9.457744, - 9.585068, - 9.699673, - 9.785478, - 9.884559, - 9.769732, - 9.655075, - 9.423071, - 9.210198, - 8.786654, - 8.061787, - 7.560976, - 6.855829, - 6.390707, - 5.904006, - 5.526631, - 5.712303, - 5.867027, - 5.768367, - 5.523352, - 5.909118, - 6.745543, - 6.859218, - ] - - # Change in Susceptible Population - deltaS = [ - 9916.490263, - 12014.263380, - 13019.275755, - 12296.373612, - 8870.995603, - 1797.354574, - -6392.880771, - -16150.825387, - -27083.245106, - -40130.421462, - -50377.169958, - -57787.717468, - -60797.223427, - -59274.041897, - -55970.213230, - -51154.650927, - -45877.841034, - -40278.553775, - -34543.967175, - -28849.633641, - -23192.776605, - -17531.130740, - -11862.021829, - -6182.456792, - -450.481090, - 5201.184400, - 10450.773882, - 15373.018272, - 20255.699431, - 24964.431669, - 29470.745887, - 33678.079947, - 37209.808930, - 39664.432393, - 41046.735479, - 40462.982011, - 39765.070209, - 39270.815830, - 39888.077002, - 42087.276604, - 45332.012929, - 49719.128772, - 54622.190928, - 59919.718626, - 65436.341097, - 70842.911460, - 76143.747430, - 81162.358574, - 85688.102884, - 89488.917734, - 91740.108470, - 91998.787916, - 87875.986012, - 79123.877908, - 66435.611045, - 48639.250610, - 27380.282817, - 2166.538464, - -21236.428084, - -43490.803535, - -60436.624080, - -73378.401966, - -80946.278268, - -84831.969493, - -84696.627286, - -81085.365407, - -76410.847049, - -70874.415387, - -65156.276464, - -59379.086883, - -53557.267619, - -47784.164830, - -42078.001172, - -36340.061427, - -30541.788202, - -24805.281435, - -19280.817165, - -13893.690606, - -8444.172221, - -3098.160839, - 2270.908649, - 7594.679295, - 12780.079247, - 17801.722109, - 22543.091206, - 26897.369814, - 31051.285734, - 34933.809557, - 38842.402859, - 42875.230152, - 47024.395356, - 51161.516122, - 55657.298307, - 60958.155424, - 66545.635029, - 72202.930397, - 77934.761905, - 83588.207792, - 89160.874522, - 94606.115027, - 99935.754968, - 104701.404975, - 107581.670606, - 108768.440311, - 107905.700480, - 104062.148863, - 96620.281684, - 83588.443029, - 61415.088182, - 27124.031692, - -7537.285321, - -43900.451653, - -70274.062783, - -87573.481475, - -101712.148408, - -116135.719087, - -124187.225446, - -124725.278371, - -122458.145590, - -117719.918256, - -112352.138605, - -106546.806030, - -100583.803012, - -94618.253238, - -88639.090897, - -82725.009842, - -76938.910669, - -71248.957807, - -65668.352795, - -60272.761991, - -55179.538428, - -50456.021161, - -46037.728058, - -42183.912670, - -39522.184006, - -38541.255303, - -38383.665728, - -39423.998130, - -40489.466130, - -41450.406768, - -42355.156592, - -43837.562085, - -43677.262972, - -41067.896944, - -37238.628465, - -32230.392026, - -26762.766062, - -20975.163308, - -15019.218554, - -9053.105545, - -3059.663132, - 2772.399618, - 8242.538397, - 13407.752291, - 18016.047539, - 22292.125752, - 26616.583347, - 30502.564253, - 33153.890890, - 34216.684448, - 33394.220786, - 29657.417791, - 23064.375405, - 12040.831532, - -2084.921068, - -21390.235970, - -38176.615985, - -51647.714482, - -59242.564959, - -60263.150854, - -58599.245165, - -54804.972560, - -50092.112608, - -44465.812552, - -38533.096297, - -32747.104307, - -27130.082610, - -21529.632955, - -15894.611939, - -10457.566933, - -5429.042583, - -903.757828, - 2481.947589, - 5173.789976, - 8358.768202, - 11565.584635, - 14431.147931, - 16951.619820, - 18888.807708, - 20120.884465, - 20222.141242, - 18423.168124, - 16498.668271, - 14442.624242, - 14070.038273, - 16211.370808, - 19639.815904, - 24280.360465, - 29475.380079, - 35030.793540, - 40812.325095, - 46593.082382, - 52390.906885, - 58109.310860, - 63780.896094, - 68984.456561, - 72559.442320, - 74645.487900, - 74695.219755, - 72098.143876, - 66609.929889, - 56864.971296, - 41589.295266, - 19057.032104, - -5951.329863, - -34608.796853, - -56603.801584, - -72678.838057, - -83297.070856, - -90127.593511, - -92656.040614, - -91394.995510, - -88192.056842, - -83148.833075, - -77582.587173, - -71750.440823, - -65765.369857, - -59716.101820, - -53613.430067, - -47473.832358, - -41287.031890, - -35139.919259, - -29097.671507, - -23178.836760, - -17486.807388, - -12046.775779, - -6802.483422, - -1867.556171, - 2644.380534, - 6615.829501, - 10332.557518, - 13706.737038, - 17017.991307, - 20303.136670, - 23507.386461, - 26482.194102, - 29698.585356, - 33196.305757, - 37385.914179, - 42872.996212, - 48725.617879, - 54564.488527, - 60453.841604, - 66495.146265, - 72668.620416, - 78723.644870, - 84593.136677, - 89974.936239, - 93439.798630, - 95101.207834, - 94028.126381, - 89507.925620, - 80989.846001, - 66944.274744, - 47016.422041, - 19932.783790, - -6198.433172, - -32320.379400, - -49822.852084, - -60517.553414, - -66860.548269, - -70849.714105, - -71058.721556, - -67691.947812, - -63130.703822, - -57687.607311, - -51916.952488, - -45932.054982, - -39834.909941, - -33714.535713, - -27564.443333, - -21465.186188, - -15469.326408, - -9522.358787, - -3588.742161, - 2221.802073, - 7758.244339, - 13020.269708, - 18198.562827, - 23211.338588, - 28051.699645, - 32708.577247, - 37413.795242, - 42181.401920, - 46462.499633, - 49849.582315, - 53026.578940, - 55930.600705, - 59432.642178, - 64027.356857, - 69126.843653, - 74620.328837, - 80372.056070, - 86348.152766, - 92468.907239, - 98568.998246, - 104669.511588, - 110445.790143, - 115394.348973, - 119477.553152, - 121528.574511, - 121973.674087, - 121048.017786, - 118021.473181, - 112151.993711, - 102195.999157, - 85972.731130, - 61224.719621, - 31949.279603, - -3726.022971, - -36485.298619, - -67336.469799, - -87799.366129, - -98865.713558, - -104103.651120, - -105068.402300, - -103415.820781, - -99261.356633, - -94281.850081, - -88568.701325, - -82625.711921, - -76766.776770, - -70998.803524, - -65303.404499, - -59719.198305, - -54182.230439, - -48662.904657, - -43206.731668, - -37732.701095, - -32375.478519, - -27167.508567, - -22197.211891, - -17722.869502, - -13925.135219, - -10737.893027, - -8455.327914, - -7067.008358, - -7086.991191, - -7527.693561, - -8378.025732, - -8629.383998, - -7854.586079, - -5853.040657, - -1973.225485, - 2699.850783, - 8006.098287, - 13651.734934, - 19139.318072, - 24476.645420, - 29463.480336, - 33899.078820, - 37364.528796, - 38380.214949, - 37326.585649, - 33428.470616, - 27441.000494, - 21761.126583, - 15368.408081, - 7224.234078, - -2702.217396, - -14109.682505, - -27390.915614, - -38569.562393, - -47875.155339, - -53969.121872, - -57703.473001, - -57993.198171, - -54908.391840, - -50568.410328, - -45247.622563, - -39563.224328, - -33637.786521, - -27585.345413, - -21572.074797, - -15597.363909, - -9577.429076, - -3475.770622, - 2520.378408, - 8046.881775, - 13482.345595, - ] - - # Transmission Rate Set - beta_set = [ - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 18, - 19, - 20, - 21, - 22, - 23, - 24, - 25, - 26, - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 18, - 19, - 20, - 21, - 22, - 23, - 24, - 25, - 26, - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 18, - 19, - 20, - 21, - 22, - 23, - 24, - 25, - 26, - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 18, - 19, - 20, - 21, - 22, - 23, - 24, - 25, - 26, - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 18, - 19, - 20, - 21, - 22, - 23, - 24, - 25, - 26, - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 18, - 19, - 20, - 21, - 22, - 23, - 24, - 25, - 26, - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 18, - 19, - 20, - 21, - 22, - 23, - 24, - 25, - 26, - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 18, - 19, - 20, - 21, - 22, - 23, - 24, - 25, - 26, - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 18, - 19, - 20, - 21, - 22, - 23, - 24, - 25, - 26, - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 18, - 19, - 20, - 21, - 22, - 23, - 24, - 25, - 26, - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 18, - 19, - 20, - 21, - 22, - 23, - 24, - 25, - 26, - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 18, - 19, - 20, - 21, - 22, - 23, - 24, - 25, - 26, - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 18, - 19, - 20, - 21, - 22, - 23, - 24, - 25, - 26, - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 18, - 19, - 20, - 21, - 22, - 23, - 24, - 25, - 26, - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 18, - 19, - 20, - 21, - 22, - 23, - 24, - 25, - 26, - ] + + pop = [ 15.881351, 15.881339, 15.881320, 15.881294, 15.881261, 15.881223, 15.881180, 15.881132, 15.881079, 15.881022, 15.880961, 15.880898, 15.880832, 15.880764, 15.880695, 15.880624, 15.880553, 15.880480, 15.880409, 15.880340, 15.880270, 15.880203, 15.880138, 15.880076, 15.880016, 15.879960, 15.879907, 15.879852, 15.879799, 15.879746, 15.879693, 15.879638, 15.879585, 15.879531, 15.879477, 15.879423, 15.879370, 15.879315, 15.879262, 15.879209, 15.879155, 15.879101, 15.879048, 15.878994, 15.878940, 15.878886, 15.878833, 15.878778, 15.878725, 15.878672, 15.878618, 15.878564, 15.878510, 15.878457, 15.878402, 15.878349, 15.878295, 15.878242, 15.878187, 15.878134, 15.878081, 15.878026, 15.877973, 15.877919, 15.877864, 15.877811, 15.877758, 15.877704, 15.877650, 15.877596, 15.877543, 15.877488, 15.877435, 15.877381, 15.877326, 15.877273, 15.877220, 15.877166, 15.877111, 15.877058, 15.877005, 15.876950, 15.876896, 15.876843, 15.876789, 15.876735, 15.876681, 15.876628, 15.876573, 15.876520, 15.876466, 15.876411, 15.876358, 15.876304, 15.876251, 15.876196, 15.876143, 15.876089, 15.876034, 15.875981, 15.875927, 15.875872, 15.875819, 15.875765, 15.875712, 15.875657, 15.875604, 15.875550, 15.875495, 15.875442, 15.875388, 15.875335, 15.875280, 15.875226, 15.875173, 15.875118, 15.875064, 15.875011, 15.874956, 15.874902, 15.874849, 15.874795, 15.874740, 15.874687, 15.874633, 15.874578, 15.874525, 15.874471, 15.874416, 15.874363, 15.874309, 15.874256, 15.874201, 15.874147, 15.874094, 15.874039, 15.873985, 15.873931, 15.873878, 15.873823, 15.873769, 15.873716, 15.873661, 15.873607, 15.873554, 15.873499, 15.873445, 15.873391, 15.873338, 15.873283, 15.873229, 15.873175, 15.873121, 15.873067, 15.873013, 15.872960, 15.872905, 15.872851, 15.872797, 15.872742, 15.872689, 15.872635, 15.872580, 15.872526, 15.872473, 15.872419, 15.872364, 15.872310, 15.872256, 15.872202, 15.872148, 15.872094, 15.872039, 15.871985, 15.871932, 15.871878, 15.871823, 15.871769, 15.871715, 15.871660, 15.871607, 15.871553, 15.871499, 15.871444, 15.871390, 15.871337, 15.871282, 15.871228, 15.871174, 15.871119, 15.871065, 15.871012, 15.870958, 15.870903, 15.870849, 15.870795, 15.870740, 15.870686, 15.870633, 15.870577, 15.870524, 15.870470, 15.870416, 15.870361, 15.870307, 15.870253, 15.870198, 15.870144, 15.870091, 15.870037, 15.869982, 15.869928, 15.869874, 15.869819, 15.869765, 15.869711, 15.869656, 15.869602, 15.869548, 15.869495, 15.869439, 15.869386, 15.869332, 15.869277, 15.869223, 15.869169, 15.869114, 15.869060, 15.869006, 15.868952, 15.868897, 15.868843, 15.868789, 15.868734, 15.868679, 15.868618, 15.868556, 15.868489, 15.868421, 15.868351, 15.868280, 15.868208, 15.868134, 15.868063, 15.867991, 15.867921, 15.867852, 15.867785, 15.867721, 15.867659, 15.867601, 15.867549, 15.867499, 15.867455, 15.867416, 15.867383, 15.867357, 15.867338, 15.867327, 15.867321, 15.867327, 15.867338, 15.867359, 15.867386, 15.867419, 15.867459, 15.867505, 15.867555, 15.867610, 15.867671, 15.867734, 15.867801, 15.867869, 15.867941, 15.868012, 15.868087, 15.868161, 15.868236, 15.868310, 15.868384, 15.868457, 15.868527, 15.868595, 15.868661, 15.868722, 15.868780, 15.868837, 15.868892, 15.868948, 15.869005, 15.869061, 15.869116, 15.869173, 15.869229, 15.869284, 15.869341, 15.869397, 15.869452, 15.869509, 15.869565, 15.869620, 15.869677, 15.869733, 15.869788, 15.869845, 15.869901, 15.869956, 15.870012, 15.870069, 15.870124, 15.870180, 15.870237, 15.870292, 15.870348, 15.870405, 15.870461, 15.870516, 15.870572, 15.870629, 15.870684, 15.870740, 15.870796, 15.870851, 15.870908, 15.870964, 15.871019, 15.871076, 15.871132, 15.871187, 15.871243, 15.871300, 15.871355, 15.871411, 15.871467, 15.871522, 15.871579, 15.871635, 15.871691, 15.871746, 15.871802, 15.871859, 15.871914, 15.871970, 15.872026, 15.872081, 15.872138, 15.872194, 15.872249, 15.872305, 15.872361, 15.872416, 15.872473, 15.872529, 15.872584, 15.872640, 15.872696, 15.872751, 15.872807, 15.872864, 15.872919, 15.872975, 15.873031, 15.873087, 15.873142, 15.873198, 15.873255, 15.873310, 15.873366, 15.873422, 15.873477, 15.873533, 15.873589, 15.873644, 15.873700, 15.873757, 15.873811, 15.873868, 15.873924, 15.873979, 15.874035, 15.874091, 15.874146, 15.874202, 15.874258, 15.874313, 15.874369, 15.874425, 15.874481, 15.874536, 15.874592] + + logIstar = [7.943245, 8.269994, 8.517212, 8.814208, 9.151740, 9.478472, 9.559847, 9.664087, 9.735378, 9.852583, 9.692265, 9.498807, 9.097634, 8.388878, 7.870516, 7.012956, 6.484941, 5.825368, 5.346815, 5.548361, 5.706732, 5.712617, 5.709714, 5.696888, 5.530087, 5.826563, 6.643563, 7.004292, 7.044663, 7.190259, 7.335926, 7.516861, 7.831779, 8.188895, 8.450204, 8.801436, 8.818379, 8.787658, 8.601685, 8.258338, 7.943364, 7.425585, 7.062834, 6.658307, 6.339600, 6.526984, 6.679178, 6.988758, 7.367331, 7.746694, 8.260558, 8.676522, 9.235582, 9.607778, 9.841917, 10.081571, 10.216090, 10.350366, 10.289668, 10.248842, 10.039504, 9.846343, 9.510392, 9.190923, 8.662465, 7.743221, 7.128458, 5.967898, 5.373883, 5.097497, 4.836570, 5.203345, 5.544798, 5.443047, 5.181152, 5.508669, 6.144130, 6.413744, 6.610423, 6.748885, 6.729511, 6.789841, 6.941034, 7.093516, 7.307039, 7.541077, 7.644803, 7.769145, 7.760187, 7.708017, 7.656795, 7.664983, 7.483828, 6.887324, 6.551093, 6.457449, 6.346064, 6.486300, 6.612378, 6.778753, 6.909477, 7.360570, 8.150303, 8.549044, 8.897572, 9.239323, 9.538751, 9.876531, 10.260911, 10.613536, 10.621510, 10.661115, 10.392899, 10.065536, 9.920090, 9.933097, 9.561691, 8.807713, 8.263463, 7.252184, 6.669083, 5.877763, 5.331878, 5.356563, 5.328469, 5.631146, 6.027497, 6.250717, 6.453919, 6.718444, 7.071636, 7.348905, 7.531528, 7.798226, 8.197941, 8.578809, 8.722964, 8.901152, 8.904370, 8.889865, 8.881902, 8.958903, 8.721281, 8.211509, 7.810624, 7.164607, 6.733688, 6.268503, 5.905983, 5.900432, 5.846547, 6.245427, 6.786271, 7.088480, 7.474295, 7.650063, 7.636703, 7.830990, 8.231516, 8.584816, 8.886908, 9.225216, 9.472778, 9.765505, 9.928623, 10.153033, 10.048574, 9.892620, 9.538818, 8.896100, 8.437584, 7.819738, 7.362598, 6.505880, 5.914972, 6.264584, 6.555019, 6.589319, 6.552029, 6.809771, 7.187616, 7.513918, 8.017712, 8.224957, 8.084474, 8.079148, 8.180991, 8.274269, 8.413748, 8.559599, 8.756090, 9.017927, 9.032720, 9.047983, 8.826873, 8.366489, 8.011876, 7.500830, 7.140406, 6.812626, 6.538719, 6.552218, 6.540129, 6.659927, 6.728530, 7.179692, 7.989210, 8.399173, 8.781128, 9.122303, 9.396378, 9.698512, 9.990104, 10.276543, 10.357284, 10.465869, 10.253833, 10.018503, 9.738407, 9.484367, 9.087025, 8.526409, 8.041126, 7.147168, 6.626706, 6.209446, 5.867231, 5.697439, 5.536769, 5.421413, 5.238297, 5.470136, 5.863007, 6.183083, 6.603569, 6.906278, 7.092324, 7.326612, 7.576052, 7.823430, 7.922775, 8.041677, 8.063403, 8.073229, 8.099726, 8.168522, 8.099041, 8.011404, 7.753147, 6.945211, 6.524244, 6.557723, 6.497742, 6.256247, 5.988794, 6.268093, 6.583316, 7.106842, 8.053929, 8.508237, 8.938915, 9.311863, 9.619753, 9.931745, 10.182361, 10.420978, 10.390829, 10.389230, 10.079342, 9.741479, 9.444561, 9.237448, 8.777687, 7.976436, 7.451502, 6.742856, 6.271545, 5.782289, 5.403089, 5.341954, 5.243509, 5.522993, 5.897001, 6.047042, 6.100738, 6.361727, 6.849562, 7.112544, 7.185346, 7.309412, 7.423746, 7.532142, 7.510318, 7.480175, 7.726362, 8.061117, 8.127072, 8.206166, 8.029634, 7.592953, 7.304869, 7.005394, 6.750019, 6.461377, 6.226432, 6.287047, 6.306452, 6.783694, 7.450957, 7.861692, 8.441530, 8.739626, 8.921994, 9.168961, 9.428077, 9.711664, 10.032714, 10.349937, 10.483985, 10.647475, 10.574038, 10.522431, 10.192246, 9.756246, 9.342511, 8.872072, 8.414189, 7.606582, 7.084701, 6.149903, 5.517257, 5.839429, 6.098090, 6.268935, 6.475965, 6.560543, 6.598942, 6.693938, 6.802531, 6.934345, 7.078370, 7.267736, 7.569640, 7.872204, 8.083603, 8.331226, 8.527144, 8.773523, 8.836599, 8.894303, 8.808326, 8.641717, 8.397901, 7.849034, 7.482899, 7.050252, 6.714103, 6.900603, 7.050765, 7.322905, 7.637986, 8.024340, 8.614505, 8.933591, 9.244008, 9.427410, 9.401385, 9.457744, 9.585068, 9.699673, 9.785478, 9.884559, 9.769732, 9.655075, 9.423071, 9.210198, 8.786654, 8.061787, 7.560976, 6.855829, 6.390707, 5.904006, 5.526631, 5.712303, 5.867027, 5.768367, 5.523352, 5.909118, 6.745543, 6.859218 ] + + deltaS = [ 9916.490263 ,12014.263380 ,13019.275755 ,12296.373612 ,8870.995603 ,1797.354574 ,-6392.880771 ,-16150.825387 ,-27083.245106 ,-40130.421462 ,-50377.169958 ,-57787.717468 ,-60797.223427 ,-59274.041897 ,-55970.213230 ,-51154.650927 ,-45877.841034 ,-40278.553775 ,-34543.967175 ,-28849.633641 ,-23192.776605 ,-17531.130740 ,-11862.021829 ,-6182.456792 ,-450.481090 ,5201.184400 ,10450.773882 ,15373.018272 ,20255.699431 ,24964.431669 ,29470.745887 ,33678.079947 ,37209.808930 ,39664.432393 ,41046.735479 ,40462.982011 ,39765.070209 ,39270.815830 ,39888.077002 ,42087.276604 ,45332.012929 ,49719.128772 ,54622.190928 ,59919.718626 ,65436.341097 ,70842.911460 ,76143.747430 ,81162.358574 ,85688.102884 ,89488.917734 ,91740.108470 ,91998.787916 ,87875.986012 ,79123.877908 ,66435.611045 ,48639.250610 ,27380.282817 ,2166.538464 ,-21236.428084 ,-43490.803535 ,-60436.624080 ,-73378.401966 ,-80946.278268 ,-84831.969493 ,-84696.627286 ,-81085.365407 ,-76410.847049 ,-70874.415387 ,-65156.276464 ,-59379.086883 ,-53557.267619 ,-47784.164830 ,-42078.001172 ,-36340.061427 ,-30541.788202 ,-24805.281435 ,-19280.817165 ,-13893.690606 ,-8444.172221 ,-3098.160839 ,2270.908649 ,7594.679295 ,12780.079247 ,17801.722109 ,22543.091206 ,26897.369814 ,31051.285734 ,34933.809557 ,38842.402859 ,42875.230152 ,47024.395356 ,51161.516122 ,55657.298307 ,60958.155424 ,66545.635029 ,72202.930397 ,77934.761905 ,83588.207792 ,89160.874522 ,94606.115027 ,99935.754968 ,104701.404975 ,107581.670606 ,108768.440311 ,107905.700480 ,104062.148863 ,96620.281684 ,83588.443029 ,61415.088182 ,27124.031692 ,-7537.285321 ,-43900.451653 ,-70274.062783 ,-87573.481475 ,-101712.148408 ,-116135.719087 ,-124187.225446 ,-124725.278371 ,-122458.145590 ,-117719.918256 ,-112352.138605 ,-106546.806030 ,-100583.803012 ,-94618.253238 ,-88639.090897 ,-82725.009842 ,-76938.910669 ,-71248.957807 ,-65668.352795 ,-60272.761991 ,-55179.538428 ,-50456.021161 ,-46037.728058 ,-42183.912670 ,-39522.184006 ,-38541.255303 ,-38383.665728 ,-39423.998130 ,-40489.466130 ,-41450.406768 ,-42355.156592 ,-43837.562085 ,-43677.262972 ,-41067.896944 ,-37238.628465 ,-32230.392026 ,-26762.766062 ,-20975.163308 ,-15019.218554 ,-9053.105545 ,-3059.663132 ,2772.399618 ,8242.538397 ,13407.752291 ,18016.047539 ,22292.125752 ,26616.583347 ,30502.564253 ,33153.890890 ,34216.684448 ,33394.220786 ,29657.417791 ,23064.375405 ,12040.831532 ,-2084.921068 ,-21390.235970 ,-38176.615985 ,-51647.714482 ,-59242.564959 ,-60263.150854 ,-58599.245165 ,-54804.972560 ,-50092.112608 ,-44465.812552 ,-38533.096297 ,-32747.104307 ,-27130.082610 ,-21529.632955 ,-15894.611939 ,-10457.566933 ,-5429.042583 ,-903.757828 ,2481.947589 ,5173.789976 ,8358.768202 ,11565.584635 ,14431.147931 ,16951.619820 ,18888.807708 ,20120.884465 ,20222.141242 ,18423.168124 ,16498.668271 ,14442.624242 ,14070.038273 ,16211.370808 ,19639.815904 ,24280.360465 ,29475.380079 ,35030.793540 ,40812.325095 ,46593.082382 ,52390.906885 ,58109.310860 ,63780.896094 ,68984.456561 ,72559.442320 ,74645.487900 ,74695.219755 ,72098.143876 ,66609.929889 ,56864.971296 ,41589.295266 ,19057.032104 ,-5951.329863 ,-34608.796853 ,-56603.801584 ,-72678.838057 ,-83297.070856 ,-90127.593511 ,-92656.040614 ,-91394.995510 ,-88192.056842 ,-83148.833075 ,-77582.587173 ,-71750.440823 ,-65765.369857 ,-59716.101820 ,-53613.430067 ,-47473.832358 ,-41287.031890 ,-35139.919259 ,-29097.671507 ,-23178.836760 ,-17486.807388 ,-12046.775779 ,-6802.483422 ,-1867.556171 ,2644.380534 ,6615.829501 ,10332.557518 ,13706.737038 ,17017.991307 ,20303.136670 ,23507.386461 ,26482.194102 ,29698.585356 ,33196.305757 ,37385.914179 ,42872.996212 ,48725.617879 ,54564.488527 ,60453.841604 ,66495.146265 ,72668.620416 ,78723.644870 ,84593.136677 ,89974.936239 ,93439.798630 ,95101.207834 ,94028.126381 ,89507.925620 ,80989.846001 ,66944.274744 ,47016.422041 ,19932.783790 ,-6198.433172 ,-32320.379400 ,-49822.852084 ,-60517.553414 ,-66860.548269 ,-70849.714105 ,-71058.721556 ,-67691.947812 ,-63130.703822 ,-57687.607311 ,-51916.952488 ,-45932.054982 ,-39834.909941 ,-33714.535713 ,-27564.443333 ,-21465.186188 ,-15469.326408 ,-9522.358787 ,-3588.742161 ,2221.802073 ,7758.244339 ,13020.269708 ,18198.562827 ,23211.338588 ,28051.699645 ,32708.577247 ,37413.795242 ,42181.401920 ,46462.499633 ,49849.582315 ,53026.578940 ,55930.600705 ,59432.642178 ,64027.356857 ,69126.843653 ,74620.328837 ,80372.056070 ,86348.152766 ,92468.907239 ,98568.998246 ,104669.511588 ,110445.790143 ,115394.348973 ,119477.553152 ,121528.574511 ,121973.674087 ,121048.017786 ,118021.473181 ,112151.993711 ,102195.999157 ,85972.731130 ,61224.719621 ,31949.279603 ,-3726.022971 ,-36485.298619 ,-67336.469799 ,-87799.366129 ,-98865.713558 ,-104103.651120 ,-105068.402300 ,-103415.820781 ,-99261.356633 ,-94281.850081 ,-88568.701325 ,-82625.711921 ,-76766.776770 ,-70998.803524 ,-65303.404499 ,-59719.198305 ,-54182.230439 ,-48662.904657 ,-43206.731668 ,-37732.701095 ,-32375.478519 ,-27167.508567 ,-22197.211891 ,-17722.869502 ,-13925.135219 ,-10737.893027 ,-8455.327914 ,-7067.008358 ,-7086.991191 ,-7527.693561 ,-8378.025732 ,-8629.383998 ,-7854.586079 ,-5853.040657 ,-1973.225485 ,2699.850783 ,8006.098287 ,13651.734934 ,19139.318072 ,24476.645420 ,29463.480336 ,33899.078820 ,37364.528796 ,38380.214949 ,37326.585649 ,33428.470616 ,27441.000494 ,21761.126583 ,15368.408081 ,7224.234078 ,-2702.217396 ,-14109.682505 ,-27390.915614 ,-38569.562393 ,-47875.155339 ,-53969.121872 ,-57703.473001 ,-57993.198171 ,-54908.391840 ,-50568.410328 ,-45247.622563 ,-39563.224328 ,-33637.786521 ,-27585.345413 ,-21572.074797 ,-15597.363909 ,-9577.429076 ,-3475.770622 ,2520.378408 ,8046.881775 ,13482.345595 ] + + beta_set = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26] + # from new_data_set import * # Uncomment this line to use new data set From 35ad3c2a177a422c0d87601ce7b52fb4f426cfb6 Mon Sep 17 00:00:00 2001 From: Albert Lee Date: Fri, 5 Apr 2024 11:06:07 -0400 Subject: [PATCH 29/99] Refactored high and low beta constraints in disease_model.py --- gdplib/pyomo_examples/disease_model.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/gdplib/pyomo_examples/disease_model.py b/gdplib/pyomo_examples/disease_model.py index 67b2347..47b7b81 100644 --- a/gdplib/pyomo_examples/disease_model.py +++ b/gdplib/pyomo_examples/disease_model.py @@ -302,8 +302,9 @@ def _disj(model, i): return model -# # disjuncts -# # high beta disjuncts +# disjuncts +# The commented code sets up explicit high and low beta constraints for the SIR model using a big-M reformulation, bypassing Pyomo's built-in disjunctive programming tools. +# high beta disjuncts # def highbeta_L(m,i): # """ # Defines the lower bound constraint for the high transmission parameter beta in the SIR model. @@ -317,6 +318,7 @@ def _disj(model, i): # Returns # ------- +# tuple # A tuple (0.0, expr, None) where expr is the Pyomo expression for the lower bound of the high beta disjunct at the i-th biweekly period. # This represents the lower bound of the high beta disjunct at the i-th biweekly period. @@ -340,7 +342,9 @@ def _disj(model, i): # i : int # Index of biweekly periods in the data set. -# Returns: +# Returns +# ------- +# tuple # A tuple (None, expr, 0.0) where expr is the Pyomo expression for the upper bound of the high beta disjunct at the i-th biweekly period. # This represents the upper bound of the high beta disjunct at the i-th biweekly period. @@ -386,6 +390,7 @@ def _disj(model, i): # Returns # ------- +# tuple # A tuple (0.0, expr, None) where expr is the Pyomo expression for the lower bound of the low beta disjunct at the i-th biweekly period. # This represents the lower bound of the low beta disjunct at the i-th biweekly period. From c8d3a1bb8792e952e89da0d3af1f13a9898097dc Mon Sep 17 00:00:00 2001 From: Albert Lee Date: Fri, 5 Apr 2024 11:09:42 -0400 Subject: [PATCH 30/99] Remove disease model results markdown file. --- gdplib/pyomo_examples/READMEdisease.md | 17 ----------------- 1 file changed, 17 deletions(-) delete mode 100644 gdplib/pyomo_examples/READMEdisease.md diff --git a/gdplib/pyomo_examples/READMEdisease.md b/gdplib/pyomo_examples/READMEdisease.md deleted file mode 100644 index 604baea..0000000 --- a/gdplib/pyomo_examples/READMEdisease.md +++ /dev/null @@ -1,17 +0,0 @@ -# Results for the disease_model.py -The model is ran with the following software configuration: -Python 3.12.1 -Pyomo 6.7.0 -GAMS 44.3.0 (solver: BARON 23.6.22.) - -The hardware configuration is: -Dantzig Server -Processor: Linux cluster with 48 AMD EPYC 7643 2.3GHz CPUs -RAM: 1.0 TB - -Solution = 304.416210719832 found at node 1 -Best possible = 304.415906304 -Absolute gap = 0.000304415832090399 optca = 1E-9 -Relative gap = 9.99998756211332E-7 optcr = 1E-6 - -Total time = 2.08 seconds From 00a90d6eb4acb7418376ee25dcd829e7b73baee8 Mon Sep 17 00:00:00 2001 From: Albert Lee Date: Tue, 9 Apr 2024 21:24:20 -0400 Subject: [PATCH 31/99] Added the documentation of the disjunction part of the batch processing.py --- gdplib/pyomo_examples/batch_processing.py | 88 ++++++++++++++--------- 1 file changed, 53 insertions(+), 35 deletions(-) diff --git a/gdplib/pyomo_examples/batch_processing.py b/gdplib/pyomo_examples/batch_processing.py index c03eda3..96b7000 100644 --- a/gdplib/pyomo_examples/batch_processing.py +++ b/gdplib/pyomo_examples/batch_processing.py @@ -345,97 +345,103 @@ def finish_in_time_rule(model): # Disjunctions def storage_tank_selection_disjunct_rule(disjunct, selectStorageTank, j): - """_summary_ + """ + Defines the conditions under which a storage tank will be included or excluded between stages j and j+1. + This rule is applied to a disjunct, which is part of a disjunction representing this binary decision. Parameters ---------- - disjunct : _type_ - _description_ - selectStorageTank : _type_ - _description_ + disjunct : Pyomo.Disjunct + A Pyomo Disjunct object representing a specific case within the disjunction. + selectStorageTank : int + A binary indicator (0 or 1) where 1 means a storage tank is included and 0 means it is not. j : int Index for the processing stages in the plant. Stages are ordered and include various processing steps. Returns ------- - _type_ - _description_ + None + This function defines constraints within the disjunct based on the decision to include (selectStorageTank=1) or exclude (selectStorageTank=0) a storage tank. + Constraints ensure the storage tank's volume can accommodate the batch sizes at stage j and j+1 if included, or ensure batch size continuity if excluded. """ model = disjunct.model() def volume_stage_j_rule(disjunct, i): """ - + Ensures the storage tank size between stages j and j+1 is sufficient to accommodate the batch size of product i at stage j, considering the storage tank size factor. Parameters ---------- - disjunct : - _description_ + disjunct : Pyomo.Disjunct + The disjunct within which this constraint is defined. i : int Product index, representing different products being processed in the plant, each with its own set of processing times across various stages. Returns ------- - _type_ - _description_ + Pyomo.Constraint + A constraint ensuring the storage tank size is sufficient for the batch size at stage j. """ return model.storageTankSize_log[j] >= log(model.StorageTankSizeFactor[j]) + \ model.batchSize_log[i, j] def volume_stage_jPlus1_rule(disjunct, i): - """_summary_ + """ + Ensures the storage tank size between stages j and j+1 is sufficient to accommodate the batch size of product i at stage j+1, considering the storage tank size factor. Parameters ---------- - disjunct : _type_ - _description_ + disjunct : Pyomo.Disjunct + The disjunct within which this constraint is defined. i : int Product index, representing different products being processed in the plant, each with its own set of processing times across various stages. Returns ------- - _type_ - _description_ + Pyomo.Constraint + A constraint ensuring the storage tank size is sufficient for the batch size at stage j+1. """ return model.storageTankSize_log[j] >= log(model.StorageTankSizeFactor[j]) + \ model.batchSize_log[i, j+1] def batch_size_rule(disjunct, i): - """_summary_ + """ + Ensures the difference in batch sizes between stages j and j+1 for product i is within the acceptable limits defined by the storage tank size factor by product. Parameters ---------- - disjunct : _type_ - _description_ + disjunct : Pyomo.Disjunct + The disjunct within which this constraint is defined. i : int Product index, representing different products being processed in the plant, each with its own set of processing times across various stages. Returns ------- - _type_ - _description_ + Pyomo.Constraint + A constraint enforcing acceptable batch size differences between stages j and j+1. """ return inequality(-log(model.StorageTankSizeFactorByProd[i,j]), model.batchSize_log[i,j] - model.batchSize_log[i, j+1], log(model.StorageTankSizeFactorByProd[i,j])) def no_batch_rule(disjunct, i): - """_summary_ + """ + Enforces batch size continuity between stages j and j+1 for product i, applicable when no storage tank is selected. Parameters ---------- - disjunct : _type_ - _description_ + disjunct : Pyomo.Disjunct + The disjunct within which this constraint is defined. i : int Product index, representing different products being processed in the plant, each with its own set of processing times across various stages. Returns ------- - _type_ - _description_ + Pyomo.Constraint + A constraint ensuring batch size continuity between stages j and j+1 """ return model.batchSize_log[i,j] - model.batchSize_log[i,j+1] == 0 @@ -474,7 +480,8 @@ def select_storage_tanks_rule(model, j): # though this is a disjunction in the GAMs model, it is more efficiently formulated this way: # TODO: what on earth is k? def units_out_of_phase_rule(model, j): - """_summary_ + """ + Defines the constraints for the logarithmic representation of the number of units k out of phase in stage j. Parameters ---------- @@ -485,8 +492,13 @@ def units_out_of_phase_rule(model, j): Returns ------- - _type_ - _description_ + None + Adds a constraint to the Pyomo model representing the logarithmic sum of out-of-phase units at stage j. + This constraint is not returned but directly added to the model. + + Notes + ----- + These are not directly related to disjunctions but more to the logical modeling of unit operations. """ return model.unitsOutOfPhase_log[j] == sum(model.LogCoeffs[k] * model.outOfPhase[j,k] \ for k in model.PARALLELUNITS) @@ -504,8 +516,13 @@ def units_in_phase_rule(model, j): Returns ------- - _type_ - _description_ + None + Incorporates a constraint into the Pyomo model that corresponds to the logarithmic sum of in-phase units at stage j. + The constraint is directly applied to the model without an explicit return value. + + Notes + ----- + These are not directly related to disjunctions but more to the logical modeling of unit operations. """ return model.unitsInPhase_log[j] == sum(model.LogCoeffs[k] * model.inPhase[j,k] \ for k in model.PARALLELUNITS) @@ -513,7 +530,8 @@ def units_in_phase_rule(model, j): # and since I didn't do the disjunction as a disjunction, we need the XORs: def units_out_of_phase_xor_rule(model, j): - """_summary_ + """ + Enforces an exclusive OR (XOR) constraint ensuring that exactly one configuration for the number of units out of phase is selected at stage j. Parameters ---------- @@ -524,7 +542,7 @@ def units_out_of_phase_xor_rule(model, j): Returns ------- - _type_ + Pyomo.Constraint A Pyomo constraint expression calculating the logarithmic representation of the number of units out of phase at stage j """ return sum(model.outOfPhase[j,k] for k in model.PARALLELUNITS) == 1 @@ -532,7 +550,7 @@ def units_out_of_phase_xor_rule(model, j): def units_in_phase_xor_rule(model, j): """ - Enforces an exclusive OR (XOR) constraint ensuring that exactly one configuration for the number of units out of phase is selected at stage j. + Enforces an exclusive OR (XOR) constraint ensuring that exactly one configuration for the number of units in phase is selected at stage j. Parameters ---------- From f4a9a51f973cac50698355448bf1eb862aaed353 Mon Sep 17 00:00:00 2001 From: Albert Lee Date: Tue, 9 Apr 2024 21:46:36 -0400 Subject: [PATCH 32/99] Added header and remove unusual comments. --- gdplib/pyomo_examples/batch_processing.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/gdplib/pyomo_examples/batch_processing.py b/gdplib/pyomo_examples/batch_processing.py index 96b7000..e8894d6 100644 --- a/gdplib/pyomo_examples/batch_processing.py +++ b/gdplib/pyomo_examples/batch_processing.py @@ -1,3 +1,13 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + from os.path import join from pyomo.common.fileutils import this_file_dir @@ -528,7 +538,6 @@ def units_in_phase_rule(model, j): for k in model.PARALLELUNITS) model.units_in_phase = Constraint(model.STAGES, rule=units_in_phase_rule) - # and since I didn't do the disjunction as a disjunction, we need the XORs: def units_out_of_phase_xor_rule(model, j): """ Enforces an exclusive OR (XOR) constraint ensuring that exactly one configuration for the number of units out of phase is selected at stage j. From 7c981006ce32f9587bc7c6be2bbf11f18321462e Mon Sep 17 00:00:00 2001 From: Albert Lee Date: Tue, 9 Apr 2024 22:13:42 -0400 Subject: [PATCH 33/99] Added doc on the parameters, variables and constraints. --- gdplib/pyomo_examples/batch_processing.py | 49 ++++++++++++----------- 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/gdplib/pyomo_examples/batch_processing.py b/gdplib/pyomo_examples/batch_processing.py index e8894d6..d481199 100644 --- a/gdplib/pyomo_examples/batch_processing.py +++ b/gdplib/pyomo_examples/batch_processing.py @@ -140,15 +140,15 @@ def get_log_coeffs(model, k): """ return log(model.PARALLELUNITS.ord(k)) - model.LogCoeffs = Param(model.PARALLELUNITS, initialize=get_log_coeffs) + model.LogCoeffs = Param(model.PARALLELUNITS, initialize=get_log_coeffs, doc='Logarithmic Coefficients') # bounds model.volumeLB = Param(model.STAGES, default=VolumeLB, doc='Lower Bound of Volume of the Units') model.volumeUB = Param(model.STAGES, default=VolumeUB, doc='Upper Bound of Volume of the Units') - model.storageTankSizeLB = Param(model.STAGES, default=StorageTankSizeLB) - model.storageTankSizeUB = Param(model.STAGES, default=StorageTankSizeUB) - model.unitsInPhaseUB = Param(model.STAGES, default=UnitsInPhaseUB) - model.unitsOutOfPhaseUB = Param(model.STAGES, default=UnitsOutOfPhaseUB) + model.storageTankSizeLB = Param(model.STAGES, default=StorageTankSizeLB, doc='Lower Bound of Storage Tank Size') + model.storageTankSizeUB = Param(model.STAGES, default=StorageTankSizeUB, doc='Upper Bound of Storage Tank Size') + model.unitsInPhaseUB = Param(model.STAGES, default=UnitsInPhaseUB, doc='Upper Bound of Units in Phase') + model.unitsOutOfPhaseUB = Param(model.STAGES, default=UnitsOutOfPhaseUB, doc='Upper Bound of Units Out of Phase') # Variables @@ -192,9 +192,9 @@ def get_volume_bounds(model, j): A tuple containing the lower and upper bounds for the volume of processing units at stage j.. """ return (model.volumeLB[j], model.volumeUB[j]) - model.volume_log = Var(model.STAGES, bounds=get_volume_bounds) - model.batchSize_log = Var(model.PRODUCTS, model.STAGES) - model.cycleTime_log = Var(model.PRODUCTS) + model.volume_log = Var(model.STAGES, bounds=get_volume_bounds, doc='Logarithmic Volume of the Units') + model.batchSize_log = Var(model.PRODUCTS, model.STAGES, doc='Logarithmic Batch Size of the Products') + model.cycleTime_log = Var(model.PRODUCTS, doc='Logarithmic Cycle Time of the Products') def get_unitsOutOfPhase_bounds(model, j): """ @@ -213,7 +213,7 @@ def get_unitsOutOfPhase_bounds(model, j): A tuple containing the lower and upper bounds for the logarithmic representation of the number of units out of phase at stage j. """ return (0, model.unitsOutOfPhaseUB[j]) - model.unitsOutOfPhase_log = Var(model.STAGES, bounds=get_unitsOutOfPhase_bounds) + model.unitsOutOfPhase_log = Var(model.STAGES, bounds=get_unitsOutOfPhase_bounds, doc='Logarithmic Units Out of Phase') def get_unitsInPhase_bounds(model, j): """ @@ -232,7 +232,7 @@ def get_unitsInPhase_bounds(model, j): A tuple containing the minimum and maximum bounds for the logarithmic number of units in phase at stage j, ensuring model constraints are met. """ return (0, model.unitsInPhaseUB[j]) - model.unitsInPhase_log = Var(model.STAGES, bounds=get_unitsInPhase_bounds) + model.unitsInPhase_log = Var(model.STAGES, bounds=get_unitsInPhase_bounds, doc='Logarithmic Units In Phase') def get_storageTankSize_bounds(model, j): """ @@ -252,11 +252,11 @@ def get_storageTankSize_bounds(model, j): """ return (model.storageTankSizeLB[j], model.storageTankSizeUB[j]) # TODO: these bounds make it infeasible... - model.storageTankSize_log = Var(model.STAGES, bounds=get_storageTankSize_bounds) + model.storageTankSize_log = Var(model.STAGES, bounds=get_storageTankSize_bounds, doc='Logarithmic Storage Tank Size') # binary variables for deciding number of parallel units in and out of phase - model.outOfPhase = Var(model.STAGES, model.PARALLELUNITS, within=Binary) - model.inPhase = Var(model.STAGES, model.PARALLELUNITS, within=Binary) + model.outOfPhase = Var(model.STAGES, model.PARALLELUNITS, within=Binary, doc='Out of Phase Units') + model.inPhase = Var(model.STAGES, model.PARALLELUNITS, within=Binary, doc='In Phase Units') # Objective @@ -282,7 +282,7 @@ def get_cost_rule(model): return model.Alpha1 * sum(exp(model.unitsInPhase_log[j] + model.unitsOutOfPhase_log[j] + \ model.Beta1 * model.volume_log[j]) for j in model.STAGES) +\ model.Alpha2 * sum(exp(model.Beta2 * model.storageTankSize_log[j]) for j in model.STAGESExceptLast) - model.min_cost = Objective(rule=get_cost_rule) + model.min_cost = Objective(rule=get_cost_rule, doc='Minimize the Total Cost of the Plant Design') # Constraints def processing_capacity_rule(model, j, i): @@ -307,7 +307,7 @@ def processing_capacity_rule(model, j, i): """ return model.volume_log[j] >= log(model.ProductSizeFactor[i, j]) + model.batchSize_log[i, j] - \ model.unitsInPhase_log[j] - model.processing_capacity = Constraint(model.STAGES, model.PRODUCTS, rule=processing_capacity_rule) + model.processing_capacity = Constraint(model.STAGES, model.PRODUCTS, rule=processing_capacity_rule, doc='Processing Capacity') def processing_time_rule(model, j, i): """ @@ -331,7 +331,7 @@ def processing_time_rule(model, j, i): """ return model.cycleTime_log[i] >= log(model.ProcessingTime[i, j]) - model.batchSize_log[i, j] - \ model.unitsOutOfPhase_log[j] - model.processing_time = Constraint(model.STAGES, model.PRODUCTS, rule=processing_time_rule) + model.processing_time = Constraint(model.STAGES, model.PRODUCTS, rule=processing_time_rule, doc='Processing Time') def finish_in_time_rule(model): """ @@ -349,7 +349,7 @@ def finish_in_time_rule(model): """ return model.HorizonTime >= sum(model.ProductionAmount[i]*exp(model.cycleTime_log[i]) \ for i in model.PRODUCTS) - model.finish_in_time = Constraint(rule=finish_in_time_rule) + model.finish_in_time = Constraint(rule=finish_in_time_rule, doc='Finish in Time') # Disjunctions @@ -466,7 +466,7 @@ def no_batch_rule(disjunct, i): # disjunct.no_volume = Constraint(expr=model.storageTankSize_log[j] == MinFlow) disjunct.no_batch = Constraint(model.PRODUCTS, rule=no_batch_rule) model.storage_tank_selection_disjunct = Disjunct([0,1], model.STAGESExceptLast, - rule=storage_tank_selection_disjunct_rule) + rule=storage_tank_selection_disjunct_rule, doc='Storage Tank Selection Disjunct') def select_storage_tanks_rule(model, j): """ @@ -485,10 +485,10 @@ def select_storage_tanks_rule(model, j): A list of disjuncts representing the choices for including or not including a storage tank between stages j and j+1. """ return [model.storage_tank_selection_disjunct[selectTank, j] for selectTank in [0,1]] - model.select_storage_tanks = Disjunction(model.STAGESExceptLast, rule=select_storage_tanks_rule) + model.select_storage_tanks = Disjunction(model.STAGESExceptLast, rule=select_storage_tanks_rule, doc='Select Storage Tanks') # though this is a disjunction in the GAMs model, it is more efficiently formulated this way: - # TODO: what on earth is k? + # TODO: what on earth is k? Number of Parallel units. def units_out_of_phase_rule(model, j): """ Defines the constraints for the logarithmic representation of the number of units k out of phase in stage j. @@ -512,10 +512,11 @@ def units_out_of_phase_rule(model, j): """ return model.unitsOutOfPhase_log[j] == sum(model.LogCoeffs[k] * model.outOfPhase[j,k] \ for k in model.PARALLELUNITS) - model.units_out_of_phase = Constraint(model.STAGES, rule=units_out_of_phase_rule) + model.units_out_of_phase = Constraint(model.STAGES, rule=units_out_of_phase_rule, doc='Units Out of Phase') def units_in_phase_rule(model, j): """_summary_ + Defines the constraints for the logarithmic representation of the number of units k in-phase in stage j. Parameters ---------- @@ -536,7 +537,7 @@ def units_in_phase_rule(model, j): """ return model.unitsInPhase_log[j] == sum(model.LogCoeffs[k] * model.inPhase[j,k] \ for k in model.PARALLELUNITS) - model.units_in_phase = Constraint(model.STAGES, rule=units_in_phase_rule) + model.units_in_phase = Constraint(model.STAGES, rule=units_in_phase_rule, doc='Units In Phase') def units_out_of_phase_xor_rule(model, j): """ @@ -555,7 +556,7 @@ def units_out_of_phase_xor_rule(model, j): A Pyomo constraint expression calculating the logarithmic representation of the number of units out of phase at stage j """ return sum(model.outOfPhase[j,k] for k in model.PARALLELUNITS) == 1 - model.units_out_of_phase_xor = Constraint(model.STAGES, rule=units_out_of_phase_xor_rule) + model.units_out_of_phase_xor = Constraint(model.STAGES, rule=units_out_of_phase_xor_rule, doc='Exclusive OR for Units Out of Phase') def units_in_phase_xor_rule(model, j): """ @@ -574,7 +575,7 @@ def units_in_phase_xor_rule(model, j): A Pyomo constraint expression enforcing the XOR condition for units out of phase at stage j. """ return sum(model.inPhase[j,k] for k in model.PARALLELUNITS) == 1 - model.units_in_phase_xor = Constraint(model.STAGES, rule=units_in_phase_xor_rule) + model.units_in_phase_xor = Constraint(model.STAGES, rule=units_in_phase_xor_rule, doc='Exclusive OR for Units In Phase') return model.create_instance(join(this_file_dir(), 'batch_processing.dat')) From 797a29f4e561ffcd937bc89d3a21be70a68adaca Mon Sep 17 00:00:00 2001 From: Albert Lee Date: Tue, 9 Apr 2024 22:25:29 -0400 Subject: [PATCH 34/99] Added headings of the documentation. --- gdplib/pyomo_examples/med_term_purchasing.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/gdplib/pyomo_examples/med_term_purchasing.py b/gdplib/pyomo_examples/med_term_purchasing.py index 81c2695..a85d262 100644 --- a/gdplib/pyomo_examples/med_term_purchasing.py +++ b/gdplib/pyomo_examples/med_term_purchasing.py @@ -1,3 +1,13 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + from pyomo.environ import * from pyomo.gdp import * from pyomo.common.fileutils import this_file_dir From dc3d15bd7477aae337fe9c28634f35bb34c13f34 Mon Sep 17 00:00:00 2001 From: Albert Lee Date: Wed, 10 Apr 2024 15:17:50 -0400 Subject: [PATCH 35/99] Set up the Bounds on the variables. --- gdplib/pyomo_examples/med_term_purchasing.py | 571 ++++++++++--------- 1 file changed, 297 insertions(+), 274 deletions(-) diff --git a/gdplib/pyomo_examples/med_term_purchasing.py b/gdplib/pyomo_examples/med_term_purchasing.py index a85d262..e1d970f 100644 --- a/gdplib/pyomo_examples/med_term_purchasing.py +++ b/gdplib/pyomo_examples/med_term_purchasing.py @@ -49,7 +49,8 @@ def build_model(): References ---------- [1] Vecchietti, Aldo, and I. Grossmann. "Computational experience with logmip solving linear and nonlinear disjunctive programming problems." In Proc. of FOCAPD, pp. 587-590. 2004. - [2] Park, M., Park, S., Mele, F. D., & Grossmann, I. E. (2006). Modeling of purchase and sales contracts in supply chain optimization. Industrial & Engineering Chemistry Research, 45(14), 5013-5026. DOI: https://doi.org/10.1021/ie0513144 + [2] Vecchietti, A., S. Lee and I.E. Grossmann, “Modeling of Discrete/Continuous Optimization Problems: Characterization and Formulation of Disjunctions and their Relaxations,” + Computers and Chemical Engineering 27, 433-448 (2003). """ model = AbstractModel() @@ -129,15 +130,15 @@ def build_model(): # Price for quantities less than min amount under discount contract # pd1(j, t) in GAMS - model.RegPrice_Discount = Param(model.Streams, model.TimePeriods) + model.RegPrice_Discount = Param(model.Streams, model.TimePeriod, doc='Price for quantities less than min amount under discount contract') # Discounted price for the quantity purchased exceeding the min amount # pd2(j,t0 in GAMS - model.DiscountPrice_Discount = Param(model.Streams, model.TimePeriods) + model.DiscountPrice_Discount = Param(model.Streams, model.TimePeriods, doc='Discounted price for the quantity purchased exceeding the min amount') # Price for quantities below min amount # pb1(j,t) in GAMS - model.RegPrice_Bulk = Param(model.Streams, model.TimePeriods) + model.RegPrice_Bulk = Param(model.Streams, model.TimePeriods, doc='Price for quantities below min amount under bulk contract') # Price for quantities above min amount # pb2(j, t) in GAMS @@ -177,71 +178,73 @@ def build_model(): # epsilon_jt # cinv(j, t) in GAMS # inventory cost of material j at time t - model.InventoryCost = Param(model.Streams, model.TimePeriods) + model.InventoryCost = Param(model.Streams, model.TimePeriods, doc='Inventory cost of material j at time t') # invub(j, t) in GAMS # inventory upper bound - model.InventoryLevelUB = Param(model.Streams, model.TimePeriods, default=0) + model.InventoryLevelUB = Param(model.Streams, model.TimePeriods, default=0, doc='Inventory upper bound') ## UPPER BOUNDS HARDCODED INTO GAMS MODEL # All of these upper bounds are hardcoded. So I am just leaving them that way. # This means they all have to be the same as each other right now. def getAmountUBs(model, j, t): - """_summary_ + """ + Retrieves the upper bound for the amount that can be purchased or processed for a given material j and time period t. Parameters ---------- model : Pyomo.AbstractModel Pyomo abstract model for medium-term purchasing contracts problem. - j : _type_ - _description_ - t : Index - Time period. + j : int + Index of materials. + t : int + Index of time period. Returns ------- - _type_ - _description_ + float + The hardcoded upper bound on the amount for any material and time period, defined by the global variable `AMOUNT_UB`. """ return AMOUNT_UB def getCostUBs(model, j, t): - """_summary_ + """ + Retrieves the upper bound for the cost associated with purchasing or processing a given material in a specific time period. Parameters ---------- model : Pyomo.AbstractModel Pyomo abstract model for medium-term purchasing contracts problem. - j : _type_ - _description_ - t : Index - Time period. + j : int + Index of materials. + t : int + Index of time period. Returns ------- - _type_ - _description_ + float + The hardcoded upper bound on costs for any material and time period, specified by the global variable `COST_UB`. """ return COST_UB model.AmountPurchasedUB_FP = Param(model.Streams, model.TimePeriods, - initialize=getAmountUBs) + initialize=getAmountUBs, doc='Upper bound on amount purchased under fixed price contract') model.AmountPurchasedUB_Discount = Param(model.Streams, model.TimePeriods, - initialize=getAmountUBs) + initialize=getAmountUBs, doc='Upper bound on amount purchased under discount contract') model.AmountPurchasedBelowMinUB_Discount = Param(model.Streams, model.TimePeriods, - initialize=getAmountUBs) + initialize=getAmountUBs, doc='Upper bound on amount purchased below min amount for discount under discount contract') model.AmountPurchasedAboveMinUB_Discount = Param(model.Streams, model.TimePeriods, - initialize=getAmountUBs) + initialize=getAmountUBs, doc='Upper bound on amount purchased above min amount for discount under discount contract') model.AmountPurchasedUB_FD = Param(model.Streams, model.TimePeriods, - initialize=getAmountUBs) + initialize=getAmountUBs, doc='Upper bound on amount purchased under fixed duration contract') model.AmountPurchasedUB_Bulk = Param(model.Streams, model.TimePeriods, - initialize=getAmountUBs) + initialize=getAmountUBs, doc='Upper bound on amount purchased under bulk contract') - model.CostUB_FP = Param(model.Streams, model.TimePeriods, initialize=getCostUBs) - model.CostUB_FD = Param(model.Streams, model.TimePeriods, initialize=getCostUBs) - model.CostUB_Discount = Param(model.Streams, model.TimePeriods, initialize=getCostUBs) - model.CostUB_Bulk = Param(model.Streams, model.TimePeriods, initialize=getCostUBs) + model.CostUB_FP = Param(model.Streams, model.TimePeriods, initialize=getCostUBs, doc='Upper bound on cost of fixed price contract') + model.CostUB_FD = Param(model.Streams, model.TimePeriods, initialize=getCostUBs, doc='Upper bound on cost of fixed duration contract') + model.CostUB_Discount = Param(model.Streams, model.TimePeriods, initialize=getCostUBs, DOC='Upper bound on cost of discount contract') + model.CostUB_Bulk = Param(model.Streams, model.TimePeriods, initialize=getCostUBs, doc='Upper bound on cost of bulk contract') #################### @@ -261,44 +264,52 @@ def getCostUBs(model, j, t): # inventory level of chemical j at time period t def getInventoryBounds(model, i, j): """ - Inventory level of chemical j at time period t + Defines the lower and upper bounds for the inventory level of material j associated with process i. Parameters ---------- model : Pyomo.AbstractModel Pyomo abstract model for medium-term purchasing contracts problem. - i : _type_ - _description_ - j : _type_ - _description_ + i : int + Index of processes. + j : int + Index of materials. Returns ------- - _type_ - _description_ + tuple + A tuple containing two floats: the lower bound (0) and the upper bound for the inventory level of material j. + The upper bound is retrieved from the model's 'InventoryLevelUB' parameter using indices i and j. """ return (0, model.InventoryLevelUB[i,j]) model.InventoryLevel = Var(model.Streams, model.TimePeriods, - bounds=getInventoryBounds) + bounds=getInventoryBounds, doc='Inventory level of material j at time period t') # SF_jt # sf(j, t) in GAMS # Shortfall of demand for chemical j at time period t def getShortfallBounds(model, i, j): - """_summary_ + """ + Defines the lower and upper bounds for the shortfall in demand for material j during a specific time period, associated with process i. Parameters ---------- model : Pyomo.AbstractModel - _description_ - i : _type_ - _description_ - j : _type_ - _description_ + Pyomo abstract model for medium-term purchasing contracts problem. + i : int + Index of processes. + j : int + Index of materials. + + Returns + ------- + tuple + A tuple (0, upper_bound), where 0 is the lower bound (nonnegative shortfalls) and 'upper_bound' is extracted from 'model.ShortfallUB[i, j]'. + 'model.ShortfallUB[i, j]' represents the maximal permitted shortfall for material i in the context of process i. """ return(0, model.ShortfallUB[i,j]) model.Shortfall = Var(model.Products, model.TimePeriods, - bounds=getShortfallBounds) + bounds=getShortfallBounds, doc='Shortfall of demand for material j at time period t') # amounts purchased under different contracts @@ -306,139 +317,151 @@ def getShortfallBounds(model, i, j): # spf(j, t) in GAMS # Amount of raw material j bought under fixed price contract at time period t def get_FP_bounds(model, j, t): - """_summary_ + """ + Determines the bounds for the amount of raw material 'j' that can be purchased under a fixed price contract during time period 't'. Parameters ---------- model : Pyomo.AbstractModel Pyomo abstract model for medium-term purchasing contracts problem. - j : _type_ - _description_ - t : Index - Time period. + j : int + Index of materials. + t : int + Index of time period. Returns ------- _type_ - _description_ + A tuple (0, upper_bound), where '0' is the lower bound (non-negative constraint) and 'upper_bound' is sourced from 'model.AmountPurchasedUB_FP[j,t]'. + 'model.AmountPurchasedUB_FP[j,t]' represents the maximum allowed purchase amount for material 'j' in period 't' """ return (0, model.AmountPurchasedUB_FP[j,t]) model.AmountPurchased_FP = Var(model.Streams, model.TimePeriods, - bounds=get_FP_bounds) + bounds=get_FP_bounds, doc='Amount of raw material j bought under fixed price contract at time period t') # spd(j, t) in GAMS def get_Discount_Total_bounds(model, j, t): - """_summary_ + """ + Determines the lower and upper bounds for the total amount of material j that can be purchased under discount contracts during time period t. Parameters ---------- model : Pyomo.AbstractModel Pyomo abstract model for medium-term purchasing contracts problem. - j : _type_ - _description_ - t : Index - Time period. + j : int + Index of materials. + t : int + Index of time period. Returns ------- - _type_ - _description_ + tuple + A tuple containing two elements: the lower bound (0) and the upper bound for the total amount of material j that can be purchased under discount contracts in period t. + The upper bound is retrieved from 'model.AmountPurchasedUB_Discount[j, t]'. """ return (0, model.AmountPurchasedUB_Discount[j,t]) model.AmountPurchasedTotal_Discount = Var(model.Streams, model.TimePeriods, - bounds=get_Discount_Total_bounds) + bounds=get_Discount_Total_bounds, doc='Total amount of material j bought under discount contract at time period t') # Amount purchased below min amount for discount under discount contract # spd1(j, t) in GAMS def get_Discount_BelowMin_bounds(model, j, t): - """_summary_ + """ + Determines the lower and upper bounds for the amount of material j purchased below the minimum quantity for discounts under discount contracts during time period t. Parameters ---------- model : Pyomo.AbstractModel Pyomo abstract model for medium-term purchasing contracts problem. - j : _type_ - _description_ - t : Index - Time period. + j : int + Index of materials. + t : int + Index of time period. Returns ------- - _type_ - _description_ + tuple + A tuple (0, upper_bound), where '0' is the lower bound, and 'upper_bound' is 'model.AmountPurchasedBelowMinUB_Discount[j, t]'. + 'model.AmountPurchasedBelowMinUB_Discount[j, t] indicates the maximal purchasable amount below the discount threshold for material j in period't. """ return (0, model.AmountPurchasedBelowMinUB_Discount[j,t]) model.AmountPurchasedBelowMin_Discount = Var(model.Streams, - model.TimePeriods, bounds=get_Discount_BelowMin_bounds) + model.TimePeriods, bounds=get_Discount_BelowMin_bounds, doc='Amount purchased below min amount for discount under discount contract') # spd2(j, t) in GAMS # Amount purchased above min amount for discount under discount contract def get_Discount_AboveMin_bounds(model, j, t): - """_summary_ + """ + Determines the lower and upper bounds for the amount of material j purchased above the minimum quantity eligible for discounts under discount contracts during time period t. Parameters ---------- model : Pyomo.AbstractModel Pyomo abstract model for medium-term purchasing contracts problem. - j : _type_ - _description_ - t : Index - Time period. + j : int + Index of materials. + t : int + Index of time period. Returns ------- - _type_ - _description_ + tuple + A tuple (0, upper_bound), where '0' is the lower bound and 'upper_bound' is sourced from 'model.AmountPurchasedBelowMinUB_Discount[j, t]'. + 'model.AmountPurchasedBelowMinUB_Discount[j, t]' indicates the maximum amount that can be purchased above the discount threshold for material j in period t. """ return (0, model.AmountPurchasedBelowMinUB_Discount[j,t]) model.AmountPurchasedAboveMin_Discount = Var(model.Streams, - model.TimePeriods, bounds=get_Discount_AboveMin_bounds) + model.TimePeriods, bounds=get_Discount_AboveMin_bounds, doc='Amount purchased above min amount for discount under discount contract') # Amount purchased under bulk contract # spb(j, t) in GAMS def get_bulk_bounds(model, j, t): - """_summary_ + """ + Sets the lower and upper bounds for the amount of material j that can be purchased under a bulk contract during time period t. Parameters ---------- model : Pyomo.AbstractModel Pyomo abstract model for medium-term purchasing contracts problem. - j : _type_ - _description_ - t : Index - Time period. + j : int + Index of materials. + t : int + Index of time period. Returns ------- - _type_ - _description_ + tuple + A tuple (0, upper_bound), where '0' is the lower bound and 'upper_bound' is sourced from 'model.AmountPurchasedUB_Bulk[j,t]'. + 'model.AmountPurchasedUB_Bulk[j,t]' indicates the maximal allowable bulk purchase amount for material 'j' in period 't'. """ return (0, model.AmountPurchasedUB_Bulk[j,t]) model.AmountPurchased_Bulk = Var(model.Streams, model.TimePeriods, - bounds=get_bulk_bounds) + bounds=get_bulk_bounds, doc='Amount purchased under bulk contract') # spl(j, t) in GAMS # Amount purchased under Fixed Duration contract def get_FD_bounds(model, j, t): - """_summary_ + """ + Sets the lower and upper bounds for the quantity of material j that can be acquired under a fixed duration contract during the time period t. Parameters ---------- model : Pyomo.AbstractModel Pyomo abstract model for medium-term purchasing contracts problem. - j : _type_ - _description_ - t : Index - Time period. + j : int + Index of materials. + t : int + Index of time period. Returns ------- - _type_ - _description_ + tuple + A tuple (0, upper_bound), where '0' is the lower bound and upper_bound is taken from 'model.AmountPurchasedUB_FD[j, t]'. + 'model.AmountPurchasedUB_FD[j, t]' indicates the maximum permissible purchase quantity for material j under a fixed duration contract in time period t. """ return (0, model.AmountPurchasedUB_FD[j,t]) model.AmountPurchased_FD = Var(model.Streams, model.TimePeriods, - bounds=get_FD_bounds) + bounds=get_FD_bounds, doc='Amount purchased under Fixed Duration contract') # costs @@ -451,19 +474,19 @@ def get_CostUBs_FD(model, j, t): Parameters ---------- model : Pyomo.AbstractModel - _description_ - j : _type_ - _description_ - t : Index - Time period. + Pyomo abstract model for medium-term purchasing contracts problem. + j : int + Index of materials. + t : int + Index of time period. Returns ------- - _type_ + tuple _description_ """ return (0, model.CostUB_FD[j,t]) - model.Cost_FD = Var(model.Streams, model.TimePeriods, bounds=get_CostUBs_FD) + model.Cost_FD = Var(model.Streams, model.TimePeriods, bounds=get_CostUBs_FD, doc='Cost of variable length contract') # costpf(j, t) in GAMS # cost of fixed duration contract @@ -474,18 +497,18 @@ def get_CostUBs_FP(model, j, t): ---------- model : Pyomo.AbstractModel Pyomo abstract model for medium-term purchasing contracts problem. - j : _type_ - _description_ - t : Index - Time period. + j : int + Index of materials. + t : int + Index of time period. Returns ------- - _type_ + tuple _description_ """ return (0, model.CostUB_FP[j,t]) - model.Cost_FP = Var(model.Streams, model.TimePeriods, bounds=get_CostUBs_FP) + model.Cost_FP = Var(model.Streams, model.TimePeriods, bounds=get_CostUBs_FP, doc='Cost of fixed price contract') # costpd(j, t) in GAMS # cost of discount contract @@ -496,19 +519,19 @@ def get_CostUBs_Discount(model, j, t): ---------- model : Pyomo.AbstractModel Pyomo abstract model for medium-term purchasing contracts problem. - j : _type_ - _description_ - t : Index - _description_ + j : int + Index of materials. + t : int + Index of time period. Returns ------- - _type_ + tuple _description_ """ return (0, model.CostUB_Discount[j,t]) model.Cost_Discount = Var(model.Streams, model.TimePeriods, - bounds=get_CostUBs_Discount) + bounds=get_CostUBs_Discount, doc='Cost of discount contract') # costpb(j, t) in GAMS # cost of bulk contract @@ -519,18 +542,18 @@ def get_CostUBs_Bulk(model, j, t): ---------- model : Pyomo.AbstractModel Pyomo abstract model for medium-term purchasing contracts problem. - j : _type_ - _description_ - t : Index - Time period. + j : int + Index of materials. + t : int + Index of time period. Returns ------- - _type_ + tuple _description_ """ return (0, model.CostUB_Bulk[j,t]) - model.Cost_Bulk = Var(model.Streams, model.TimePeriods, bounds=get_CostUBs_Bulk) + model.Cost_Bulk = Var(model.Streams, model.TimePeriods, bounds=get_CostUBs_Bulk, doc='Cost of bulk contract') # binary variables @@ -557,8 +580,8 @@ def profit_rule(model): Returns ------- - _type_ - _description_ + Pyomo.Objective + Objective function: maximize profit of the medium-term purchasing contracts problem. """ salesIncome = sum(model.Prices[j,t] * model.FlowRate[j,t] for j in model.Products for t in model.TimePeriods) @@ -578,7 +601,7 @@ def profit_rule(model): inventoryCost = sum(model.InventoryCost[j,t] * model.InventoryLevel[j,t] for j in model.Products for t in model.TimePeriods) return salesIncome - purchaseCost - productionCost - inventoryCost - shortfallCost - model.profit = Objective(rule=profit_rule, sense=maximize) + model.profit = Objective(rule=profit_rule, sense=maximize, doc='Maximize profit') # flow of raw materials is the total amount purchased (accross all contracts) def raw_material_flow_rule(model, j, t): @@ -586,12 +609,12 @@ def raw_material_flow_rule(model, j, t): Parameters ---------- - model : _type_ - _description_ - j : _type_ - _description_ - t : Index - Time period. + model : Pyomo.AbstractModel + Pyomo abstract model for medium-term purchasing contracts problem. + j : int + Index of materials. + t : int + Index of time period. Returns ------- @@ -609,12 +632,12 @@ def discount_amount_total_rule(model, j, t): Parameters ---------- - model : _type_ - _description_ - j : _type_ - _description_ - t : Index - Time period. + model : Pyomo.AbstractModel + Pyomo abstract model for medium-term purchasing contracts problem. + j : int + Index of materials. + t : int + Index of time period. Returns ------- @@ -634,10 +657,10 @@ def mass_balance_rule1(model, t): Parameters ---------- - model : _type_ - _description_ - t : Index - Time period. + model : Pyomo.AbstractModel + Pyomo abstract model for medium-term purchasing contracts problem. + t : int + Index of time period. Returns ------- @@ -652,10 +675,10 @@ def mass_balance_rule2(model, t): Parameters ---------- - model : _type_ - _description_ - t : Index - Time period. + model : Pyomo.AbstractModel + Pyomo abstract model for medium-term purchasing contracts problem. + t : int + Index of time period. Returns ------- @@ -674,10 +697,10 @@ def mass_balance_rule4(model, t): Parameters ---------- - model : _type_ - _description_ - t : Index - Time period. + model : Pyomo.AbstractModel + Pyomo abstract model for medium-term purchasing contracts problem. + t : int + Index of time period. Returns ------- @@ -694,10 +717,10 @@ def process_balance_rule1(model, t): Parameters ---------- - model : _type_ - _description_ - t : Index - Time period. + model : Pyomo.AbstractModel + Pyomo abstract model for medium-term purchasing contracts problem. + t : int + Index of time period. Returns ------- @@ -712,10 +735,10 @@ def process_balance_rule2(model, t): Parameters ---------- - model : _type_ - _description_ - t : Index - Time period. + model : Pyomo.AbstractModel + Pyomo abstract model for medium-term purchasing contracts problem. + t : int + Index of time period. Returns ------- @@ -731,10 +754,10 @@ def process_balance_rule3(model, t): Parameters ---------- - model : _type_ - _description_ - t : Index - Time period. + model : Pyomo.AbstractModel + Pyomo abstract model for medium-term purchasing contracts problem. + t : int + Index of time period. Returns ------- @@ -750,10 +773,10 @@ def process_balance_rule4(model, t): Parameters ---------- - model : _type_ - _description_ - t : Index - Time period. + model : Pyomo.AbstractModel + Pyomo abstract model for medium-term purchasing contracts problem. + t : int + Index of time period. Returns ------- @@ -772,9 +795,9 @@ def process_capacity_rule1(model, t): Parameters ---------- model : Pyomo.AbstractModel - _description_ - t : Index - Time period. + Pyomo abstract model for medium-term purchasing contracts problem. + t : int + Index of time period. Returns ------- @@ -790,9 +813,9 @@ def process_capacity_rule2(model, t): Parameters ---------- model : Pyomo.AbstractModel - _description_ - t : Index - Time period. + Pyomo abstract model for medium-term purchasing contracts problem. + t : int + Index of time period. Returns ------- @@ -808,9 +831,9 @@ def process_capacity_rule3(model, t): Parameters ---------- model : Pyomo.AbstractModel - _description_ - t : Index - Time period. + Pyomo abstract model for medium-term purchasing contracts problem. + t : int + Index of time period. Returns ------- @@ -829,9 +852,9 @@ def inventory_balance1(model, t): Parameters ---------- model : Pyomo.AbstractModel - _description_ - t : Index - Time period. + Pyomo abstract model for medium-term purchasing contracts problem. + t : int + Index of time period. Returns ------- @@ -848,9 +871,9 @@ def inventory_balance_rule2(model, t): Parameters ---------- model : Pyomo.AbstractModel - _description_ - t : Index - Time period. + Pyomo abstract model for medium-term purchasing contracts problem. + t : int + Index of time period. Returns ------- @@ -869,9 +892,9 @@ def inventory_balance_rule3(model, t): Parameters ---------- model : Pyomo.AbstractModel - _description_ - t : Index - Time period. + Pyomo abstract model for medium-term purchasing contracts problem. + t : int + Index of time period. Returns ------- @@ -891,11 +914,11 @@ def inventory_capacity_rule(model, j, t): Parameters ---------- model : Pyomo.AbstractModel - _description_ - j : _type_ - _description_ - t : Index - Time period. + Pyomo abstract model for medium-term purchasing contracts problem. + j : int + Index of materials. + t : int + Index of time period. Returns ------- @@ -912,11 +935,11 @@ def shortfall_rule(model, j, t): Parameters ---------- model : Pyomo.AbstractModel - _description_ - j : _type_ - _description_ - t : Index - Time period. + Pyomo abstract model for medium-term purchasing contracts problem. + j : int + Index of materials. + t : int + Index of time period. Returns ------- @@ -933,11 +956,11 @@ def shortfall_max_rule(model, j, t): Parameters ---------- model : Pyomo.AbstractModel - _description_ - j : _type_ - _description_ - t : Index - Time period. + Pyomo abstract model for medium-term purchasing contracts problem. + j : int + Index of materials. + t : int + Index of time period. Returns ------- @@ -954,11 +977,11 @@ def supplier_capacity_rule(model, j, t): Parameters ---------- model : Pyomo.AbstractModel - _description_ - j : _type_ - _description_ - t : Index - Time period. + Pyomo abstract model for medium-term purchasing contracts problem. + j : int + Index of materials. + t : int + Index of time period. Returns ------- @@ -975,11 +998,11 @@ def demand_UB_rule(model, j, t): Parameters ---------- model : Pyomo.AbstractModel - _description_ - j : _type_ - _description_ - t : Index - Time period. + Pyomo abstract model for medium-term purchasing contracts problem. + j : int + Index of materials. + t : int + Index of time period. Returns ------- @@ -995,11 +1018,11 @@ def demand_LB_rule(model, j, t): Parameters ---------- model : Pyomo.AbstractModel - _description_ - j : _type_ - _description_ - t : Index - Time period. + Pyomo abstract model for medium-term purchasing contracts problem. + j : int + Index of materials. + t : int + Index of time period. Returns ------- @@ -1018,12 +1041,12 @@ def FP_contract_disjunct_rule(disjunct, j, t, buy): Parameters ---------- - disjunct : Index + disjunct : Pyomo.Disjunct _description_ - j : _type_ - _description_ - t : Index - Time period. + j : int + Index of materials. + t : int + Index of time period. buy : _type_ _description_ """ @@ -1042,11 +1065,11 @@ def FP_contract_rule(model, j, t): Parameters ---------- model : Pyomo.AbstractModel - _description_ - j : _type_ - _description_ - t : Index - Time period. + Pyomo abstract model for medium-term purchasing contracts problem. + j : int + Index of materials. + t : int + Index of time period. Returns ------- @@ -1064,11 +1087,11 @@ def FP_contract_cost_rule(model, j, t): Parameters ---------- model : Pyomo.AbstractModel - _description_ - j : _type_ - _description_ - t : Index - Time period. + Pyomo abstract model for medium-term purchasing contracts problem. + j : int + Index of materials. + t : int + Index of time period. Returns ------- @@ -1089,14 +1112,14 @@ def discount_contract_disjunct_rule(disjunct, j, t, buy): Parameters ---------- - disjunct : Index - _description_ - j : _type_ - _description_ - t : Index - _description_ - buy : _type_ + disjunct : Pyomo.Disjunct _description_ + j : int + Index of materials. + t : int + Index of time period. + buy : str + Decision parameter that indicates whether to buy under the fixed price contract or not. Raises ------ @@ -1133,11 +1156,11 @@ def discount_contract_rule(model, j, t): Parameters ---------- model : Pyomo.AbstractModel - _description_ - j : _type_ - _description_ - t : Index - Time period. + Pyomo abstract model for medium-term purchasing contracts problem. + j : int + Index of materials. + t : int + Index of time period. Returns ------- @@ -1156,11 +1179,11 @@ def discount_cost_rule(model, j, t): Parameters ---------- model : Pyomo.AbstractModel - _description_ - j : _type_ - _description_ - t : Index - Time period. + Pyomo abstract model for medium-term purchasing contracts problem. + j : int + Index of materials. + t : int + Index of time period. Returns ------- @@ -1182,14 +1205,14 @@ def bulk_contract_disjunct_rule(disjunct, j, t, buy): Parameters ---------- - disjunct : Index - _description_ - j : _type_ - _description_ - t : Index - Time period. - buy : _type_ - _description_ + disjunct : Pyomo.Disjunct + -description_ + j : int + Index of materials. + t : int + Index of time period. + buy : str + Decision parameter that indicates whether to buy under the fixed price contract or not. Raises ------ @@ -1224,11 +1247,11 @@ def bulk_contract_rule(model, j, t): Parameters ---------- model : Pyomo.AbstractModel - _description_ - j : _type_ - _description_ - t : Index - _description_ + Pyomo abstract model for medium-term purchasing contracts problem. + j : int + Index of materials. + t : int + Index of time period. Returns ------- @@ -1249,10 +1272,10 @@ def FD_1mo_contract(disjunct, j, t): ---------- disjunct : Index _description_ - j : _type_ - _description_ - t : Index - Time period. + j : int + Index of materials. + t : int + Index of time period. """ model = disjunct.model() disjunct.amount1 = Constraint(expr=model.AmountPurchased_FD[j,t] >= \ @@ -1269,10 +1292,10 @@ def FD_2mo_contract(disjunct, j, t): ---------- disjunct : Index _description_ - j : _type_ - _description_ - t : Index - Time period. + j : int + Index of materials. + t : int + Index of time period. """ model = disjunct.model() disjunct.amount1 = Constraint(expr=model.AmountPurchased_FD[j,t] >= \ @@ -1295,10 +1318,10 @@ def FD_3mo_contract(disjunct, j, t): ---------- disjunct : Index _description_ - j : _type_ - _description_ - t : Index - Time period. + j : int + Index of materials. + t : int + Index of time period. """ model = disjunct.model() # NOTE: I think there is a mistake in the GAMS file in line 327. @@ -1329,10 +1352,10 @@ def FD_no_contract(disjunct, j, t): ---------- disjunct : Index _description_ - j : _type_ - _description_ - t : Index - Time period. + j : int + Index of materials. + t : int + Index of time period. """ model = disjunct.model() disjunct.amount1 = Constraint(expr=model.AmountPurchased_FD[j,t] == 0) @@ -1352,11 +1375,11 @@ def FD_contract(model, j, t): Parameters ---------- model : Pyomo.AbstractModel - _description_ - j : _type_ - _description_ - t : Index - Time period. + Pyomo abstract model for medium-term purchasing contracts problem. + j : int + Index of materials. + t : int + Index of time period. Returns ------- From 0f413f59067e421acab31bfff664897fb5fc644f Mon Sep 17 00:00:00 2001 From: Albert Lee Date: Wed, 10 Apr 2024 17:14:20 -0400 Subject: [PATCH 36/99] Added Documentation of the Variables. --- gdplib/pyomo_examples/med_term_purchasing.py | 62 +++++++++++--------- 1 file changed, 35 insertions(+), 27 deletions(-) diff --git a/gdplib/pyomo_examples/med_term_purchasing.py b/gdplib/pyomo_examples/med_term_purchasing.py index e1d970f..cf14132 100644 --- a/gdplib/pyomo_examples/med_term_purchasing.py +++ b/gdplib/pyomo_examples/med_term_purchasing.py @@ -469,7 +469,8 @@ def get_FD_bounds(model, j, t): # costpl(j, t) in GAMS # cost of variable length contract def get_CostUBs_FD(model, j, t): - """_summary_ + """ + Build the lower and upper bounds for the cost of purchasing material j under a fixed duration contract during time period t. Parameters ---------- @@ -483,7 +484,8 @@ def get_CostUBs_FD(model, j, t): Returns ------- tuple - _description_ + A tuple (0, upper_bound), where '0' is the lower bound, the minimal cost scenario, and 'upper_bound' is retrieved from 'model.CostUB_FD[j, t]', + 'model.CostUB_FD[j, t]' represents the highest permissible cost for purchasing material j under a fixed duration contract in time period t. """ return (0, model.CostUB_FD[j,t]) model.Cost_FD = Var(model.Streams, model.TimePeriods, bounds=get_CostUBs_FD, doc='Cost of variable length contract') @@ -491,7 +493,8 @@ def get_CostUBs_FD(model, j, t): # costpf(j, t) in GAMS # cost of fixed duration contract def get_CostUBs_FP(model, j, t): - """_summary_ + """ + Sets the lower and upper bounds for the cost of purchasing material j under a fixed price contract during time period t. Parameters ---------- @@ -505,7 +508,8 @@ def get_CostUBs_FP(model, j, t): Returns ------- tuple - _description_ + A tuple (0, upper_bound), where '0' is the lower bound and upper_bound is sourced from 'model.CostUB_FP[j, t]'. + 'model.CostUB_FP[j, t]' denotes the highest permissible cost for purchasing material 'j' under a fixed price contract in time period 't' """ return (0, model.CostUB_FP[j,t]) model.Cost_FP = Var(model.Streams, model.TimePeriods, bounds=get_CostUBs_FP, doc='Cost of fixed price contract') @@ -513,7 +517,8 @@ def get_CostUBs_FP(model, j, t): # costpd(j, t) in GAMS # cost of discount contract def get_CostUBs_Discount(model, j, t): - """_summary_ + """ + Set the lower and upper bounds for the cost of acquiring material j under discount contracts during time period t. Parameters ---------- @@ -527,7 +532,8 @@ def get_CostUBs_Discount(model, j, t): Returns ------- tuple - _description_ + A tuple (0, upper bound), where 0' represents the lower bound, indicating the lowest possible cost condition, and upper bound is the 'model.CostUB_Discount[j, t]'. + 'model.CostUB_Discount[j, t]' represents the maximum allowed cost for purchasing material j under a discount contract in the given time period t. """ return (0, model.CostUB_Discount[j,t]) model.Cost_Discount = Var(model.Streams, model.TimePeriods, @@ -536,7 +542,8 @@ def get_CostUBs_Discount(model, j, t): # costpb(j, t) in GAMS # cost of bulk contract def get_CostUBs_Bulk(model, j, t): - """_summary_ + """ + Set the lower and upper bounds for the cost incurred from purchasing material j under bulk contracts during time period t. Parameters ---------- @@ -550,7 +557,8 @@ def get_CostUBs_Bulk(model, j, t): Returns ------- tuple - _description_ + A tuple with 0 as the lower bound and 'model.CostUB_Bulk[j, t]' as the upper bound. + 'model.CostUB_Bulk[j, t]' represents the maximum cost for purchasing material j under a bulk contract in time period t. """ return (0, model.CostUB_Bulk[j,t]) model.Cost_Bulk = Var(model.Streams, model.TimePeriods, bounds=get_CostUBs_Bulk, doc='Cost of bulk contract') @@ -558,10 +566,10 @@ def get_CostUBs_Bulk(model, j, t): # binary variables - model.BuyFPContract = RangeSet(0,1) - model.BuyDiscountContract = Set(initialize=('BelowMin', 'AboveMin', 'NotSelected')) - model.BuyBulkContract = Set(initialize=('BelowMin', 'AboveMin', 'NotSelected')) - model.BuyFDContract = Set(initialize=('1Month', '2Month', '3Month', 'NotSelected')) + model.BuyFPContract = RangeSet(0,1) # buy fixed price contract + model.BuyDiscountContract = Set(initialize=('BelowMin', 'AboveMin', 'NotSelected'), doc='Buy discount contract') + model.BuyBulkContract = Set(initialize=('BelowMin', 'AboveMin', 'NotSelected'), doc='Buy bulk contract') + model.BuyFDContract = Set(initialize=('1Month', '2Month', '3Month', 'NotSelected'), doc='Buy fixed duration contract') ################ @@ -618,7 +626,7 @@ def raw_material_flow_rule(model, j, t): Returns ------- - _type_ + Pyomo.Constraint _description_ """ return model.FlowRate[j,t] == model.AmountPurchased_FD[j,t] + \ @@ -641,7 +649,7 @@ def discount_amount_total_rule(model, j, t): Returns ------- - _type_ + Pyomo.Constraint _description_ """ return model.AmountPurchasedTotal_Discount[j,t] == \ @@ -664,7 +672,7 @@ def mass_balance_rule1(model, t): Returns ------- - _type_ + Pyomo.Constraint _description_ """ return model.FlowRate[1, t] == model.FlowRate[2, t] + model.FlowRate[3, t] @@ -682,7 +690,7 @@ def mass_balance_rule2(model, t): Returns ------- - _type_ + Pyomo.Constraint _description_ """ return model.FlowRate[5, t] == model.FlowRate[4, t] + model.FlowRate[8,t] @@ -704,7 +712,7 @@ def mass_balance_rule4(model, t): Returns ------- - _type_ + Pyomo.Constraint _description_ """ return model.FlowRate[3, t] == 10*model.FlowRate[5, t] @@ -724,7 +732,7 @@ def process_balance_rule1(model, t): Returns ------- - _type_ + Pyomo.Constraint _description_ """ return model.FlowRate[9, t] == model.ProcessConstants[1] * model.FlowRate[2, t] @@ -742,7 +750,7 @@ def process_balance_rule2(model, t): Returns ------- - _type_ + Pyomo.Constraint _description_ """ return model.FlowRate[10, t] == model.ProcessConstants[2] * \ @@ -761,7 +769,7 @@ def process_balance_rule3(model, t): Returns ------- - _type_ + Pyomo.Constraint _description_ """ return model.FlowRate[8, t] == RandomConst_Line264 * \ @@ -780,7 +788,7 @@ def process_balance_rule4(model, t): Returns ------- - _type_ + Pyomo.Constraint _description_ """ return model.FlowRate[11, t] == RandomConst_Line265 * \ @@ -801,7 +809,7 @@ def process_capacity_rule1(model, t): Returns ------- - _type_ + Pyomo.Constraint _description_ """ return model.FlowRate[9, t] <= model.Capacity[1] @@ -819,7 +827,7 @@ def process_capacity_rule2(model, t): Returns ------- - _type_ + Pyomo.Constraint _description_ """ return model.FlowRate[10, t] <= model.Capacity[2] @@ -837,7 +845,7 @@ def process_capacity_rule3(model, t): Returns ------- - _type_ + Pyomo.Constraint _description_ """ return model.FlowRate[11, t] + model.FlowRate[8, t] <= model.Capacity[3] @@ -989,7 +997,7 @@ def supplier_capacity_rule(model, j, t): _description_ """ return model.FlowRate[j, t] <= model.SupplyAndDemandUBs[j, t] - model.supplier_capacity = Constraint(model.RawMaterials, model.TimePeriods, rule=supplier_capacity_rule) + model.supplier_capacity = Constraint(model.RawMaterials, model.TimePeriods, rule=supplier_capacity_rule, doc='') # demand upper bound def demand_UB_rule(model, j, t): @@ -1047,8 +1055,8 @@ def FP_contract_disjunct_rule(disjunct, j, t, buy): Index of materials. t : int Index of time period. - buy : _type_ - _description_ + buy : str + Decision parameter that indicates whether to buy under the fixed price contract or not. """ model = disjunct.model() if buy: From 6ede81f672a6be9f6a93af9b1baf18ae5abe6b4a Mon Sep 17 00:00:00 2001 From: Albert Lee Date: Wed, 10 Apr 2024 17:52:09 -0400 Subject: [PATCH 37/99] Update objective function and constraints in medium-term purchasing contracts problem --- gdplib/pyomo_examples/med_term_purchasing.py | 31 +++++++++++++++----- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/gdplib/pyomo_examples/med_term_purchasing.py b/gdplib/pyomo_examples/med_term_purchasing.py index cf14132..61e1249 100644 --- a/gdplib/pyomo_examples/med_term_purchasing.py +++ b/gdplib/pyomo_examples/med_term_purchasing.py @@ -579,7 +579,8 @@ def get_CostUBs_Bulk(model, j, t): # Objective: maximize profit def profit_rule(model): """ - Objective function: maximize profit + Objective function: maximize profit of the medium-term purchasing contracts problem. + The profit is given by sales revenues, operating costs, purchasing costs, inventory costs, and shortfall penalties Parameters ---------- @@ -613,7 +614,8 @@ def profit_rule(model): # flow of raw materials is the total amount purchased (accross all contracts) def raw_material_flow_rule(model, j, t): - """_summary_ + """ + Ensures the total flow of raw material j in time period t equals the sum of amounts purchased under all contract types. Parameters ---------- @@ -627,16 +629,17 @@ def raw_material_flow_rule(model, j, t): Returns ------- Pyomo.Constraint - _description_ + An equality constraint ensuring the material flow balance for each raw material j in each time period t. """ return model.FlowRate[j,t] == model.AmountPurchased_FD[j,t] + \ model.AmountPurchased_FP[j,t] + model.AmountPurchased_Bulk[j,t] + \ model.AmountPurchasedTotal_Discount[j,t] model.raw_material_flow = Constraint(model.RawMaterials, model.TimePeriods, - rule=raw_material_flow_rule) + rule=raw_material_flow_rule, doc='Material flow balance for each raw material j in each time period t') def discount_amount_total_rule(model, j, t): - """_summary_ + """ + Balances the total amount of material j purchased under discount contracts in time period t with the sum of amounts purchased below and above the minimum discount threshold. Parameters ---------- @@ -650,13 +653,13 @@ def discount_amount_total_rule(model, j, t): Returns ------- Pyomo.Constraint - _description_ + An equality constraint that ensures the total discounted purchase amount of material j in time period t is the sum of amounts bought below and above the discount threshold. """ return model.AmountPurchasedTotal_Discount[j,t] == \ model.AmountPurchasedBelowMin_Discount[j,t] + \ model.AmountPurchasedAboveMin_Discount[j,t] model.discount_amount_total_rule = Constraint(model.RawMaterials, model.TimePeriods, - rule=discount_amount_total_rule) + rule=discount_amount_total_rule, doc='Total discounted purchase amount of material j in time period t is the sum of amounts bought below and above the discount threshold') # mass balance equations for each node # these are specific to the process network in this example. @@ -697,6 +700,20 @@ def mass_balance_rule2(model, t): model.mass_balance2 = Constraint(model.TimePeriods, rule=mass_balance_rule2) def mass_balance_rule3(model, t): + """_summary_ + + Parameters + ---------- + model : Pyomo.AbstractModel + Pyomo abstract model for medium-term purchasing contracts problem. + t : int + Index of time period. + + Returns + ------- + _type_ + _description_ + """ return model.FlowRate[6, t] == model.FlowRate[7, t] model.mass_balance3 = Constraint(model.TimePeriods, rule=mass_balance_rule3) From d6fe86a72c21d93f83d6294bd578d5a614e0b108 Mon Sep 17 00:00:00 2001 From: Albert Lee Date: Thu, 11 Apr 2024 13:09:25 -0400 Subject: [PATCH 38/99] Set up the maximum flow and material balances --- gdplib/pyomo_examples/med_term_purchasing.py | 107 +++++++++++++------ 1 file changed, 73 insertions(+), 34 deletions(-) diff --git a/gdplib/pyomo_examples/med_term_purchasing.py b/gdplib/pyomo_examples/med_term_purchasing.py index 61e1249..b26462b 100644 --- a/gdplib/pyomo_examples/med_term_purchasing.py +++ b/gdplib/pyomo_examples/med_term_purchasing.py @@ -664,7 +664,11 @@ def discount_amount_total_rule(model, j, t): # mass balance equations for each node # these are specific to the process network in this example. def mass_balance_rule1(model, t): - """_summary_ + """ + Represents the mass balance equation for the first node in the process network. + + Stream 1 is the inlet stream, and streams 2 and 3 are the outlet streams. + The mass balance equation states that the total flow rate into the node (stream 1) is equal to the sum of the flow rates out of the node (streams 2 and 3). Parameters ---------- @@ -676,13 +680,17 @@ def mass_balance_rule1(model, t): Returns ------- Pyomo.Constraint - _description_ + A constraint that enforces the mass balance equation for the first node in the process network. """ return model.FlowRate[1, t] == model.FlowRate[2, t] + model.FlowRate[3, t] - model.mass_balance1 = Constraint(model.TimePeriods, rule=mass_balance_rule1) + model.mass_balance1 = Constraint(model.TimePeriods, rule=mass_balance_rule1, doc='Mass balance equation for the first node in the process network') def mass_balance_rule2(model, t): - """_summary_ + """ + Represents the mass balance equation for the second node in the process network. + + Stream 4 and 8 are the inlet streams, and stream 5 is the outlet stream. + The mass balance equation states that the total flow rate into the node (streams 4 and 8) is equal to the flow rate out of the node (stream 5). Parameters ---------- @@ -694,13 +702,17 @@ def mass_balance_rule2(model, t): Returns ------- Pyomo.Constraint - _description_ + A constraint that enforces the mass balance equation for the second node in the process network. """ return model.FlowRate[5, t] == model.FlowRate[4, t] + model.FlowRate[8,t] - model.mass_balance2 = Constraint(model.TimePeriods, rule=mass_balance_rule2) + model.mass_balance2 = Constraint(model.TimePeriods, rule=mass_balance_rule2, doc='Mass balance equation for the second node in the process network') def mass_balance_rule3(model, t): - """_summary_ + """ + Represents the mass balance equation for the third node in the process network. + + Stream 6 is the inlet stream, and stream 7 is the outlet stream. + The mass balance equation states that the total flow rate into the node (stream 6) is equal to the flow rate out of the node (stream 7). Parameters ---------- @@ -711,14 +723,18 @@ def mass_balance_rule3(model, t): Returns ------- - _type_ - _description_ + Pyomo.Constraint + A constraint that enforces the mass balance equation for the third node in the process network. """ return model.FlowRate[6, t] == model.FlowRate[7, t] - model.mass_balance3 = Constraint(model.TimePeriods, rule=mass_balance_rule3) + model.mass_balance3 = Constraint(model.TimePeriods, rule=mass_balance_rule3, doc='Mass balance equation for the third node in the process network') def mass_balance_rule4(model, t): - """_summary_ + """ + Represents the mass balance equation for Process 2 in the process network. + + Stream 3 and Stream 5 are the inlet of Process 2. + The mass flowrate of Stream 3 is 10 times the mass flowrate of Stream 5. Parameters ---------- @@ -730,15 +746,18 @@ def mass_balance_rule4(model, t): Returns ------- Pyomo.Constraint - _description_ + A constraint that enforces the mass balance equation for Process 2 in the process network. """ return model.FlowRate[3, t] == 10*model.FlowRate[5, t] - model.mass_balance4 = Constraint(model.TimePeriods, rule=mass_balance_rule4) + model.mass_balance4 = Constraint(model.TimePeriods, rule=mass_balance_rule4, doc='Mass balance equation for Process 2 in the process network') # process input/output constraints # these are also totally specific to the process network def process_balance_rule1(model, t): - """_summary_ + """ + Represents the input/output balance equation for Process 1 in the process network. + + Process 1 has input Streams 2 and output Stream 9. Parameters ---------- @@ -750,13 +769,16 @@ def process_balance_rule1(model, t): Returns ------- Pyomo.Constraint - _description_ + A constraint that enforces the input/output balance equation for Process 1 in the process network. """ return model.FlowRate[9, t] == model.ProcessConstants[1] * model.FlowRate[2, t] - model.process_balance1 = Constraint(model.TimePeriods, rule=process_balance_rule1) + model.process_balance1 = Constraint(model.TimePeriods, rule=process_balance_rule1, doc='Input/output balance equation for Process 1 in the process network') def process_balance_rule2(model, t): - """_summary_ + """ + Represents the input/output balance equation for Process 2 in the process network. + + Process 2 has input Streams 5 and 3 and output Stream 10. Parameters ---------- @@ -768,14 +790,18 @@ def process_balance_rule2(model, t): Returns ------- Pyomo.Constraint - _description_ + A constraint that enforces the input/output balance equation for Process 2 in the process network. """ return model.FlowRate[10, t] == model.ProcessConstants[2] * \ (model.FlowRate[5, t] + model.FlowRate[3, t]) - model.process_balance2 = Constraint(model.TimePeriods, rule=process_balance_rule2) + model.process_balance2 = Constraint(model.TimePeriods, rule=process_balance_rule2, doc='Input/output balance equation for Process 2 in the process network') def process_balance_rule3(model, t): - """_summary_ + """ + Represents the input/output balance equation for Process 3 in the process network. + + Process 3 has input Stream 7 and outputs Streams 11 and 8. + RandomConst_Line264 is a hardcoded constant and determines the portion of Stream 7 that goes to Stream 8. Parameters ---------- @@ -787,14 +813,18 @@ def process_balance_rule3(model, t): Returns ------- Pyomo.Constraint - _description_ + A constraint that enforces the input/output balance equation for Process 3 in the process network. """ return model.FlowRate[8, t] == RandomConst_Line264 * \ model.ProcessConstants[3] * model.FlowRate[7, t] - model.process_balance3 = Constraint(model.TimePeriods, rule=process_balance_rule3) + model.process_balance3 = Constraint(model.TimePeriods, rule=process_balance_rule3, doc='Input/output balance equation 1 for Process 3 in the process network') def process_balance_rule4(model, t): - """_summary_ + """ + Represents the input/output balance equation for Process 3 in the process network. + + Process 3 has input Stream 7 and outputs Streams 11 and 8. + RandomConst_Line265 is a hardcoded constant and determines the portion of Stream 7 that goes to Stream 11. Parameters ---------- @@ -806,16 +836,19 @@ def process_balance_rule4(model, t): Returns ------- Pyomo.Constraint - _description_ + A constraint that enforces the input/output balance equation for Process 3 in the process network. """ return model.FlowRate[11, t] == RandomConst_Line265 * \ model.ProcessConstants[3] * model.FlowRate[7, t] - model.process_balance4 = Constraint(model.TimePeriods, rule=process_balance_rule4) + model.process_balance4 = Constraint(model.TimePeriods, rule=process_balance_rule4, doc='Input/output balance equation 2 for Process 3 in the process network') # process capacity contraints # these are hardcoded based on the three processes and the process flow structure def process_capacity_rule1(model, t): - """_summary_ + """ + Set the capacity constraint for Process 1 in the process network. + + Process 1 has a capacity constraint on Stream 9, which is the output stream of Process 1. Parameters ---------- @@ -827,13 +860,16 @@ def process_capacity_rule1(model, t): Returns ------- Pyomo.Constraint - _description_ + A constraint that enforces the capacity constraint for Process 1 in the process network. """ return model.FlowRate[9, t] <= model.Capacity[1] - model.process_capacity1 = Constraint(model.TimePeriods, rule=process_capacity_rule1) + model.process_capacity1 = Constraint(model.TimePeriods, rule=process_capacity_rule1, doc='Capacity constraint for Process 1 in the process network') def process_capacity_rule2(model, t): - """_summary_ + """ + Set the capacity constraint for Process 2 in the process network. + + Process 2 has a capacity constraint on Stream 10, which is the output stream of Process 2. Parameters ---------- @@ -845,13 +881,16 @@ def process_capacity_rule2(model, t): Returns ------- Pyomo.Constraint - _description_ + A constraint that enforces the capacity constraint for Process 2 in the process network. """ return model.FlowRate[10, t] <= model.Capacity[2] - model.process_capacity2 = Constraint(model.TimePeriods, rule=process_capacity_rule2) + model.process_capacity2 = Constraint(model.TimePeriods, rule=process_capacity_rule2, doc='Capacity constraint for Process 2 in the process network') def process_capacity_rule3(model, t): - """_summary_ + """ + Set the capacity constraint for Process 3 in the process network. + + Process 3 has capacity constraints on Streams 11 and 8, which are the output streams of Process 3. Parameters ---------- @@ -863,10 +902,10 @@ def process_capacity_rule3(model, t): Returns ------- Pyomo.Constraint - _description_ + A constraint that enforces the capacity constraint for Process 3 in the process network. """ return model.FlowRate[11, t] + model.FlowRate[8, t] <= model.Capacity[3] - model.process_capacity3 = Constraint(model.TimePeriods, rule=process_capacity_rule3) + model.process_capacity3 = Constraint(model.TimePeriods, rule=process_capacity_rule3, doc='Capacity constraint for Process 3 in the process network') # Inventory balance of final products # again, these are hardcoded. From c9d5556e2d0568efc96eaf4a2363eb4773b62578 Mon Sep 17 00:00:00 2001 From: Albert Lee Date: Thu, 11 Apr 2024 13:53:30 -0400 Subject: [PATCH 39/99] Added documentation of the Pyomo Constraints. --- gdplib/pyomo_examples/med_term_purchasing.py | 106 ++++++++++++------- 1 file changed, 66 insertions(+), 40 deletions(-) diff --git a/gdplib/pyomo_examples/med_term_purchasing.py b/gdplib/pyomo_examples/med_term_purchasing.py index b26462b..826cb18 100644 --- a/gdplib/pyomo_examples/med_term_purchasing.py +++ b/gdplib/pyomo_examples/med_term_purchasing.py @@ -331,7 +331,7 @@ def get_FP_bounds(model, j, t): Returns ------- - _type_ + tuple A tuple (0, upper_bound), where '0' is the lower bound (non-negative constraint) and 'upper_bound' is sourced from 'model.AmountPurchasedUB_FP[j,t]'. 'model.AmountPurchasedUB_FP[j,t]' represents the maximum allowed purchase amount for material 'j' in period 't' """ @@ -911,7 +911,11 @@ def process_capacity_rule3(model, t): # again, these are hardcoded. def inventory_balance1(model, t): - """_summary_ + """ + Maintains inventory balance for the material associated with stream 12 at the first inventory node across time periods. + + This constraint ensures that the inventory level of the material at the beginning of each time period t, combined with the incoming flow from stream 9, equals the sum of the outflow to the next process (or demand) represented by stream 12 and the inventory level at the end of the time period. For the initial time period, the previous inventory is assumed to be zero. + This balance is vital for tracking inventory levels accurately, allowing the model to make informed decisions about production, storage, and sales to maximize overall profit. Parameters ---------- @@ -922,15 +926,19 @@ def inventory_balance1(model, t): Returns ------- - _type_ - _description_ + Pyomo.Constraint + A constraint enforcing the balance of inventory levels for the material flowing through stream 12 at the first inventory node, taking into account the material inflows and outflows as well as changes in inventory from the previous to the current time period. """ prev = 0 if t == min(model.TimePeriods) else model.InventoryLevel[12, t-1] return prev + model.FlowRate[9, t] == model.FlowRate[12, t] + model.InventoryLevel[12,t] - model.inventory_balance1 = Constraint(model.TimePeriods, rule=inventory_balance1) + model.inventory_balance1 = Constraint(model.TimePeriods, rule=inventory_balance1, doc='Inventory balance for material associated with stream 12 at the first inventory node') def inventory_balance_rule2(model, t): - """_summary_ + """ + Ensures inventory balance for the material associated with stream 13 at the second inventory node for the first time period. + + This constraint is applied only to the first time period (t=1) and ensures that the sum of incoming flows from streams 10 and 11 equals the sum of the outflow to the next process represented by stream 13 and the inventory level at the end of the period. + For periods beyond the first, this constraint is skipped, as the balance for these periods may be governed by other conditions or constraints within the model. This selective application is crucial for accurately modeling the startup phase of the inventory system, where initial conditions significantly impact subsequent operations. Parameters ---------- @@ -941,17 +949,23 @@ def inventory_balance_rule2(model, t): Returns ------- - _type_ - _description_ + Pyomo.Constraint or Constraint.Skip + A constraint enforcing the inventory balance for the material flowing through stream 13 at the second inventory node during the first time period. + For all other periods, the function returns `Constraint.Skip`, indicating no constraint is applied. """ if t != 1: return Constraint.Skip return model.FlowRate[10, t] + model.FlowRate[11, t] == \ model.InventoryLevel[13,t] + model.FlowRate[13, t] - model.inventory_balance2 = Constraint(model.TimePeriods, rule=inventory_balance_rule2) + model.inventory_balance2 = Constraint(model.TimePeriods, rule=inventory_balance_rule2, doc='Inventory balance for material associated with stream 13 at the second inventory node') def inventory_balance_rule3(model, t): - """_summary_ + """ + Maintains the inventory balance for material associated with stream 13 at the second inventory node for all time periods after the first. + + This constraint is crucial for modeling the dynamic behavior of inventory levels over time, ensuring that the sum of the previous period's inventory level and the current period's inflows from streams 10 and 11 equals the current period's outflow (through stream 13) and ending inventory level. + It reflects the principle of inventory continuity, accounting for inflows, outflows, and storage from one period to the next. + The constraint is skipped for the first period (t=1) to accommodate initial conditions or startup behaviors specific to the model's context. Parameters ---------- @@ -962,18 +976,20 @@ def inventory_balance_rule3(model, t): Returns ------- - _type_ - _description_ + Pyomo.Constraint or Constraint.Skip + A constraint enforcing the inventory balance for the material flowing through stream 13 at the second inventory node from the second period onwards. + For the first period, the function returns `Constraint.Skip`, indicating the constraint does not apply. """ if t <= 1: return Constraint.Skip return model.InventoryLevel[13, t-1] + model.FlowRate[10, t] + \ model.FlowRate[11,t] == model.InventoryLevel[13, t] + model.FlowRate[13, t] - model.inventory_balance3 = Constraint(model.TimePeriods, rule=inventory_balance_rule3) + model.inventory_balance3 = Constraint(model.TimePeriods, rule=inventory_balance_rule3, doc='Inventory balance for material associated with stream 13 at the second inventory node') # Max capacities of inventories def inventory_capacity_rule(model, j, t): - """_summary_ + """ + Sets the maximum inventory capacity for each material j at each time period t. Parameters ---------- @@ -986,15 +1002,17 @@ def inventory_capacity_rule(model, j, t): Returns ------- - _type_ - _description_ + Pyomo.Constraint + A constraint that sets the maximum permissible inventory level for material j in time period t, ensuring that the inventory does not exceed the predefined upper bound 'InventoryLevelUB[j, t]'. + This maintains the model's alignment with practical storage limitations. """ return model.InventoryLevel[j,t] <= model.InventoryLevelUB[j,t] - model.inventory_capacity_rule = Constraint(model.Products, model.TimePeriods, rule=inventory_capacity_rule) + model.inventory_capacity_rule = Constraint(model.Products, model.TimePeriods, rule=inventory_capacity_rule, doc='Maximum inventory capacity for each material j at each time period t') # Shortfall calculation def shortfall_rule(model, j, t): - """_summary_ + """ + Calculates the shortfall for each product 'j' in each time period 't'. Parameters ---------- @@ -1007,15 +1025,17 @@ def shortfall_rule(model, j, t): Returns ------- - _type_ - _description_ + Pyomo.Constraint + A constraint defining the shortfall for product j during time period t as the difference between the supply or demand upper bound and the actual flow rate. + This calculation is pivotal for evaluating performance and identifying bottlenecks or excess capacities within the supply chain. """ return model.Shortfall[j, t] == model.SupplyAndDemandUBs[j, t] - model.FlowRate[j,t] - model.shortfall = Constraint(model.Products, model.TimePeriods, rule=shortfall_rule) + model.shortfall = Constraint(model.Products, model.TimePeriods, rule=shortfall_rule, doc='Shortfall calculation for each product j in each time period t') # maximum shortfall allowed def shortfall_max_rule(model, j, t): - """_summary_ + """ + Imposes an upper limit on the shortfall allowed for each product j in each time period t. Parameters ---------- @@ -1028,15 +1048,17 @@ def shortfall_max_rule(model, j, t): Returns ------- - _type_ - _description_ + Pyomo.Constraint + A constraint that limits the shortfall for product j during time period t to a maximum value specified by 'ShortfallUB[j, t]'. + This constraint is instrumental in aligning the model's solutions with real-world operational constraints and strategic objectives. """ return model.Shortfall[j, t] <= model.ShortfallUB[j, t] - model.shortfall_max = Constraint(model.Products, model.TimePeriods, rule=shortfall_max_rule) + model.shortfall_max = Constraint(model.Products, model.TimePeriods, rule=shortfall_max_rule, doc='Maximum shortfall allowed for each product j in each time period t') # maxiumum capacities of suppliers def supplier_capacity_rule(model, j, t): - """_summary_ + """ + Enforces the upper limits on the supply capacity for each raw material j provided by suppliers in each time period t. Parameters ---------- @@ -1049,15 +1071,17 @@ def supplier_capacity_rule(model, j, t): Returns ------- - _type_ - _description_ + Pyomo.Constraint + A constraint that limits the flow rate of raw material j from suppliers in time period t to not exceed the predefined upper bound 'SupplyAndDemandUBs[j, t]'. + This constraint is crucial for ensuring the feasibility of the supply chain model and its alignment with practical supply capabilities. """ return model.FlowRate[j, t] <= model.SupplyAndDemandUBs[j, t] - model.supplier_capacity = Constraint(model.RawMaterials, model.TimePeriods, rule=supplier_capacity_rule, doc='') + model.supplier_capacity = Constraint(model.RawMaterials, model.TimePeriods, rule=supplier_capacity_rule, doc='Maximum supply capacity for each raw material j in each time period t') # demand upper bound def demand_UB_rule(model, j, t): - """_summary_ + """ + Ensures that the supply of each product j does not exceed its maximum demand in each time period t. Parameters ---------- @@ -1070,14 +1094,16 @@ def demand_UB_rule(model, j, t): Returns ------- - _type_ - _description_ + Pyomo.Constraint + A constraint limiting the flow rate of product j to not exceed the predefined maximum demand 'SupplyAndDemandUBs[j, t]' in time period t, ensuring production is demand-driven. """ return model.FlowRate[j, t] <= model.SupplyAndDemandUBs[j,t] - model.demand_UB = Constraint(model.Products, model.TimePeriods, rule=demand_UB_rule) + model.demand_UB = Constraint(model.Products, model.TimePeriods, rule=demand_UB_rule, doc='Maximum demand allowed for each product j in each time period t') + # demand lower bound def demand_LB_rule(model, j, t): - """_summary_ + """ + Ensures that the supply of each product j meets at least the minimum demand in each time period t. Parameters ---------- @@ -1090,11 +1116,11 @@ def demand_LB_rule(model, j, t): Returns ------- - _type_ - _description_ + Pyomo.Constraint + A constraint ensuring that the flow rate of product j meets or exceeds the minimum demand 'DemandLB[j, t]' in time period t, supporting effective market engagement. """ return model.FlowRate[j, t] >= model.DemandLB[j,t] - model.demand_LB = Constraint(model.Products, model.TimePeriods, rule=demand_LB_rule) + model.demand_LB = Constraint(model.Products, model.TimePeriods, rule=demand_LB_rule, doc='Minimum demand required for each product j in each time period t') # FIXED PRICE CONTRACT @@ -1137,7 +1163,7 @@ def FP_contract_rule(model, j, t): Returns ------- - _type_ + Pyomo.Constraint _description_ """ return [model.FP_contract_disjunct[j,t,buy] for buy in model.BuyFPContract] @@ -1159,7 +1185,7 @@ def FP_contract_cost_rule(model, j, t): Returns ------- - _type_ + Pyomo.Constraint _description_ """ return model.Cost_FP[j,t] == model.AmountPurchased_FP[j,t] * \ @@ -1251,7 +1277,7 @@ def discount_cost_rule(model, j, t): Returns ------- - _type_ + Pyomo.Constraint _description_ """ return model.Cost_Discount[j,t] == model.RegPrice_Discount[j,t] * \ From 804b908787e2358525c77f3199e700bddb634078 Mon Sep 17 00:00:00 2001 From: Albert Lee Date: Thu, 11 Apr 2024 14:13:33 -0400 Subject: [PATCH 40/99] Added Documentation of the Fixed Price Disjunction. --- gdplib/pyomo_examples/med_term_purchasing.py | 34 ++++++++++++++------ 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/gdplib/pyomo_examples/med_term_purchasing.py b/gdplib/pyomo_examples/med_term_purchasing.py index 826cb18..86b360e 100644 --- a/gdplib/pyomo_examples/med_term_purchasing.py +++ b/gdplib/pyomo_examples/med_term_purchasing.py @@ -1127,18 +1127,29 @@ def demand_LB_rule(model, j, t): # Disjunction for Fixed Price contract buying options def FP_contract_disjunct_rule(disjunct, j, t, buy): - """_summary_ + """ + Defines disjunctive constraints for procurement decisions under a Fixed Price (FP) contract for material j in time period t. + + A decision must be made whether to engage in purchasing under the contract terms or not for each material in each time period. + This function encapsulates the disjunctive nature of this decision: if the decision is to buy ('buy' parameter is True), + the amount purchased under the FP contract is limited by a predefined maximum ('MAX_AMOUNT_FP'); + otherwise, no purchase is made under the FP contract for that material and period. + This disjunctive approach allows for modeling complex decision-making processes in procurement strategies. Parameters ---------- disjunct : Pyomo.Disjunct - _description_ + A Pyomo Disjunct object representing a part of the disjunction. It encapsulates the constraints that are valid under a specific scenario ('buy' or not buy). j : int Index of materials. t : int Index of time period. buy : str - Decision parameter that indicates whether to buy under the fixed price contract or not. + A decision parameter indicating whether to purchase ('buy' is True) under the FP contract or not ('buy' is False) for material j in time period t + + Notes + ----- + The 'buy' parameter is treated as a binary variable in the context of the model, where True indicates a decision to engage in purchasing under the FP contract, and False indicates otherwise. """ model = disjunct.model() if buy: @@ -1146,11 +1157,14 @@ def FP_contract_disjunct_rule(disjunct, j, t, buy): else: disjunct.c = Constraint(expr=model.AmountPurchased_FP[j,t] == 0) model.FP_contract_disjunct = Disjunct(model.RawMaterials, model.TimePeriods, - model.BuyFPContract, rule=FP_contract_disjunct_rule) + model.BuyFPContract, rule=FP_contract_disjunct_rule, doc='Disjunctive constraints for Fixed Price contract buying options') # Fixed price disjunction def FP_contract_rule(model, j, t): - """_summary_ + """ + Creates a choice between buying or not buying materials under a Fixed Price contract for each material 'j' and time 't'. + + This function sets up a disjunction, which is like a crossroads for the model: for each material and time period, it can choose one of the paths defined in the 'FP_contract_disjunct_rule'. Parameters ---------- @@ -1163,12 +1177,12 @@ def FP_contract_rule(model, j, t): Returns ------- - Pyomo.Constraint - _description_ + Pyomo.Disjunction + A disjunction that represents the decision-making point for FP contract purchases, contributing to the model's overall procurement strategy. """ return [model.FP_contract_disjunct[j,t,buy] for buy in model.BuyFPContract] model.FP_disjunction = Disjunction(model.RawMaterials, model.TimePeriods, - rule=FP_contract_rule) + rule=FP_contract_rule, doc='Disjunction for Fixed Price contract buying options') # cost constraint for fixed price contract (independent constraint) def FP_contract_cost_rule(model, j, t): @@ -1186,12 +1200,12 @@ def FP_contract_cost_rule(model, j, t): Returns ------- Pyomo.Constraint - _description_ + A constraint equating the FP contract cost for material j in period t to the product of the purchased amount and its price, ensuring accurate financial accounting in the model. """ return model.Cost_FP[j,t] == model.AmountPurchased_FP[j,t] * \ model.Prices[j,t] model.FP_contract_cost = Constraint(model.RawMaterials, model.TimePeriods, - rule=FP_contract_cost_rule) + rule=FP_contract_cost_rule, doc='Cost constraint for Fixed Price contract') # DISCOUNT CONTRACT From b6d7071d85a4769fc41a776cdfb609bbacf4848a Mon Sep 17 00:00:00 2001 From: Albert Lee Date: Thu, 11 Apr 2024 14:18:57 -0400 Subject: [PATCH 41/99] Disjunctions for Discount Constract Documentation. --- gdplib/pyomo_examples/med_term_purchasing.py | 40 +++++++++++--------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/gdplib/pyomo_examples/med_term_purchasing.py b/gdplib/pyomo_examples/med_term_purchasing.py index 86b360e..cdf9119 100644 --- a/gdplib/pyomo_examples/med_term_purchasing.py +++ b/gdplib/pyomo_examples/med_term_purchasing.py @@ -1147,7 +1147,7 @@ def FP_contract_disjunct_rule(disjunct, j, t, buy): buy : str A decision parameter indicating whether to purchase ('buy' is True) under the FP contract or not ('buy' is False) for material j in time period t - Notes + Notes ----- The 'buy' parameter is treated as a binary variable in the context of the model, where True indicates a decision to engage in purchasing under the FP contract, and False indicates otherwise. """ @@ -1212,7 +1212,11 @@ def FP_contract_cost_rule(model, j, t): # Disjunction for Discount contract def discount_contract_disjunct_rule(disjunct, j, t, buy): - """_summary_ + """ + Sets rules for purchasing materials j in time t under discount contracts based on the buying decision 'buy'. + + For discount contracts, the decision involves purchasing below or above a minimum amount for a discount, or not selecting the contract at all. + This rule reflects these choices by adjusting purchasing amounts and enforcing corresponding constraints. Parameters ---------- @@ -1223,12 +1227,8 @@ def discount_contract_disjunct_rule(disjunct, j, t, buy): t : int Index of time period. buy : str - Decision parameter that indicates whether to buy under the fixed price contract or not. - - Raises - ------ - RuntimeError - _description_ + Decision on purchasing strategy: 'BelowMin', 'AboveMin', or 'NotSelected'. + """ model = disjunct.model() if buy == 'BelowMin': @@ -1251,11 +1251,14 @@ def discount_contract_disjunct_rule(disjunct, j, t, buy): else: raise RuntimeError("Unrecognized choice for discount contract: %s" % buy) model.discount_contract_disjunct = Disjunct(model.RawMaterials, model.TimePeriods, - model.BuyDiscountContract, rule=discount_contract_disjunct_rule) + model.BuyDiscountContract, rule=discount_contract_disjunct_rule, doc='Disjunctive constraints for Discount contract buying options') # Discount contract disjunction def discount_contract_rule(model, j, t): - """_summary_ + """ + Determines the disjunction for purchasing under discount contracts for each material and time period, based on available decisions. + + This function sets up the model to choose among different discount purchasing strategies, enhancing the flexibility in procurement planning. Parameters ---------- @@ -1268,17 +1271,20 @@ def discount_contract_rule(model, j, t): Returns ------- - _type_ - _description_ + Pyomo.Disjunction + The disjunction representing the choice among discount contract purchasing strategies. """ return [model.discount_contract_disjunct[j,t,buy] \ for buy in model.BuyDiscountContract] model.discount_contract = Disjunction(model.RawMaterials, model.TimePeriods, - rule=discount_contract_rule) + rule=discount_contract_rule, doc='Disjunction for Discount contract buying options') # cost constraint for discount contract (independent constraint) def discount_cost_rule(model, j, t): - """_summary_ + """ + Calculates the cost of purchasing material 'j' in time 't' under a discount contract, accounting for different price levels. + + This constraint ensures the model correctly accounts for the total cost of purchases under discount contracts, which may involve different prices based on quantity thresholds. Parameters ---------- @@ -1292,13 +1298,13 @@ def discount_cost_rule(model, j, t): Returns ------- Pyomo.Constraint - _description_ + The constraint that calculates the total cost of purchases under discount contracts. """ return model.Cost_Discount[j,t] == model.RegPrice_Discount[j,t] * \ model.AmountPurchasedBelowMin_Discount[j,t] + \ model.DiscountPrice_Discount[j,t] * model.AmountPurchasedAboveMin_Discount[j,t] model.discount_cost = Constraint(model.RawMaterials, model.TimePeriods, - rule=discount_cost_rule) + rule=discount_cost_rule, doc='Cost constraint for Discount contract') # BULK CONTRACT @@ -1316,7 +1322,7 @@ def bulk_contract_disjunct_rule(disjunct, j, t, buy): t : int Index of time period. buy : str - Decision parameter that indicates whether to buy under the fixed price contract or not. + _description_ Raises ------ From 37d961e3f406d3b2ddd3771da43dcb7fd1b65e0d Mon Sep 17 00:00:00 2001 From: Albert Lee Date: Thu, 11 Apr 2024 14:26:28 -0400 Subject: [PATCH 42/99] Bulk Contract Disjunction Documented. --- gdplib/pyomo_examples/med_term_purchasing.py | 31 +++++++++++--------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/gdplib/pyomo_examples/med_term_purchasing.py b/gdplib/pyomo_examples/med_term_purchasing.py index cdf9119..0a96610 100644 --- a/gdplib/pyomo_examples/med_term_purchasing.py +++ b/gdplib/pyomo_examples/med_term_purchasing.py @@ -1221,8 +1221,7 @@ def discount_contract_disjunct_rule(disjunct, j, t, buy): Parameters ---------- disjunct : Pyomo.Disjunct - _description_ - j : int + A Pyomo Disjunct object representing a part of the disjunction. It encapsulates the constraints that are valid under a specific scenario ('BelowMin', 'AboveMin', or 'NotSelected'). Index of materials. t : int Index of time period. @@ -1311,23 +1310,23 @@ def discount_cost_rule(model, j, t): # Bulk contract buying options disjunct def bulk_contract_disjunct_rule(disjunct, j, t, buy): - """_summary_ + """ + Defines conditions for bulk purchases of material j at time t based on the decision 'buy'. + + This rule determines how much of a material is bought under a bulk contract and at what price, based on whether purchases are below or above a specified minimum amount, or if the bulk option is not selected. + It enforces different constraints for the amount and cost of materials under these scenarios. Parameters ---------- disjunct : Pyomo.Disjunct - -description_ + A Pyomo Disjunct object representing a part of the disjunction. It encapsulates the constraints that are valid under a specific scenario ('BelowMin', 'AboveMin', or 'NotSelected'). j : int Index of materials. t : int Index of time period. buy : str - _description_ + The decision on how to engage with the bulk contract: 'BelowMin', 'AboveMin', or 'NotSelected'. - Raises - ------ - RuntimeError - _description_ """ model = disjunct.model() if buy == 'BelowMin': @@ -1348,11 +1347,15 @@ def bulk_contract_disjunct_rule(disjunct, j, t, buy): else: raise RuntimeError("Unrecognized choice for bulk contract: %s" % buy) model.bulk_contract_disjunct = Disjunct(model.RawMaterials, model.TimePeriods, - model.BuyBulkContract, rule=bulk_contract_disjunct_rule) + model.BuyBulkContract, rule=bulk_contract_disjunct_rule, doc='Disjunctive constraints for Bulk contract buying options') # Bulk contract disjunction def bulk_contract_rule(model, j, t): - """_summary_ + """ + Establishes a decision-making framework for bulk purchases, allowing the model to choose among predefined scenarios. + + This function sets up a flexible structure for deciding on bulk purchases. + Each material and time period can be evaluated independently, allowing the model to adapt to various conditions and optimize procurement strategies under bulk contracts. Parameters ---------- @@ -1365,12 +1368,12 @@ def bulk_contract_rule(model, j, t): Returns ------- - _type_ - _description_ + Pyomo.Disjunction + A set of disjunctive conditions that the model can choose from when making bulk purchasing decisions. """ return [model.bulk_contract_disjunct[j,t,buy] for buy in model.BuyBulkContract] model.bulk_contract = Disjunction(model.RawMaterials, model.TimePeriods, - rule=bulk_contract_rule) + rule=bulk_contract_rule, doc='Disjunction for Bulk contract buying options') # FIXED DURATION CONTRACT From 7a2bc1fe0cf6687a9b44d2544068455c2a02647b Mon Sep 17 00:00:00 2001 From: Albert Lee Date: Thu, 11 Apr 2024 14:32:20 -0400 Subject: [PATCH 43/99] Fixed Duration Contract Disjunction Documentation. --- gdplib/pyomo_examples/med_term_purchasing.py | 48 ++++++++++++-------- 1 file changed, 28 insertions(+), 20 deletions(-) diff --git a/gdplib/pyomo_examples/med_term_purchasing.py b/gdplib/pyomo_examples/med_term_purchasing.py index 0a96610..ad7f47c 100644 --- a/gdplib/pyomo_examples/med_term_purchasing.py +++ b/gdplib/pyomo_examples/med_term_purchasing.py @@ -1379,12 +1379,14 @@ def bulk_contract_rule(model, j, t): # FIXED DURATION CONTRACT def FD_1mo_contract(disjunct, j, t): - """_summary_ + """ + Defines the constraints for engaging in a 1-month fixed duration contract for material j at time t. + This includes a minimum purchase amount and the cost calculation based on contract-specific prices. Parameters ---------- - disjunct : Index - _description_ + disjunct : Pyomo.Disjunct + A component representing the 1-month contract scenario. j : int Index of materials. t : int @@ -1396,15 +1398,17 @@ def FD_1mo_contract(disjunct, j, t): disjunct.price1 = Constraint(expr=model.Cost_FD[j,t] == \ model.Prices_Length[j,1,t] * model.AmountPurchased_FD[j,t]) model.FD_1mo_contract = Disjunct( - model.RawMaterials, model.TimePeriods, rule=FD_1mo_contract) + model.RawMaterials, model.TimePeriods, rule=FD_1mo_contract, doc='1-month fixed duration contract') def FD_2mo_contract(disjunct, j, t): - """_summary_ + """ + Establishes conditions for a 2-month fixed duration contract. + This involves a minimum purchase requirement for two consecutive periods and corresponding cost calculations. Parameters ---------- - disjunct : Index - _description_ + disjunct : Pyomo.Disjunct + The 2-month contract scenario component j : int Index of materials. t : int @@ -1422,15 +1426,16 @@ def FD_2mo_contract(disjunct, j, t): disjunct.price2 = Constraint(expr=model.Cost_FD[j,t+1] == \ model.Prices_Length[j,2,t] * model.AmountPurchased_FD[j, t+1]) model.FD_2mo_contract = Disjunct( - model.RawMaterials, model.TimePeriods, rule=FD_2mo_contract) + model.RawMaterials, model.TimePeriods, rule=FD_2mo_contract, doc='2-month fixed duration contract') def FD_3mo_contract(disjunct, j, t): - """_summary_ + """ + Sets up a 3-month fixed duration contract scenario with minimum purchase requirements extending over three periods and the cost calculation. Parameters ---------- - disjunct : Index - _description_ + disjunct : Pyomo.Disjunct + The 3-month contract scenario component. j : int Index of materials. t : int @@ -1456,15 +1461,17 @@ def FD_3mo_contract(disjunct, j, t): disjunct.cost3 = Constraint(expr=model.Cost_FD[j,t+2] == \ model.Prices_Length[j,3,t] * model.AmountPurchased_FD[j,t+2]) model.FD_3mo_contract = Disjunct( - model.RawMaterials, model.TimePeriods, rule=FD_3mo_contract) + model.RawMaterials, model.TimePeriods, rule=FD_3mo_contract, doc='3-month fixed duration contract') def FD_no_contract(disjunct, j, t): - """_summary_ + """ + Represents the scenario where no fixed duration contract is selected for material j at time t. + Ensures no purchases or costs are accounted for under FD contracts. Parameters ---------- - disjunct : Index - _description_ + disjunct : Pyomo.Disjunct + The 'no contract' scenario component. j : int Index of materials. t : int @@ -1480,10 +1487,11 @@ def FD_no_contract(disjunct, j, t): disjunct.amount3 = Constraint(expr=model.AmountPurchased_FD[j,t+2] == 0) disjunct.cost3 = Constraint(expr=model.Cost_FD[j,t+2] == 0) model.FD_no_contract = Disjunct( - model.RawMaterials, model.TimePeriods, rule=FD_no_contract) + model.RawMaterials, model.TimePeriods, rule=FD_no_contract, doc='No fixed duration contract') def FD_contract(model, j, t): - """_summary_ + """ + Consolidates the FD contract scenarios into a single decision framework, allowing the model to choose the most optimal contract length or to not select an FD contract for each material and time period. Parameters ---------- @@ -1496,13 +1504,13 @@ def FD_contract(model, j, t): Returns ------- - _type_ - _description_ + Pyomo.Disjunction + The disjunctive decision structure for FD contracts. """ return [ model.FD_1mo_contract[j,t], model.FD_2mo_contract[j,t], model.FD_3mo_contract[j,t], model.FD_no_contract[j,t], ] model.FD_contract = Disjunction(model.RawMaterials, model.TimePeriods, - rule=FD_contract) + rule=FD_contract, doc='Fixed duration contract scenarios') return model From e6ea4227d586a35f329c7655cf64d95b36003ea0 Mon Sep 17 00:00:00 2001 From: Albert Lee Date: Thu, 11 Apr 2024 21:32:44 -0400 Subject: [PATCH 44/99] Added documentation of positioning --- gdplib/logical/positioning.py | 42 +++++++++++++++++++++++++++++------ 1 file changed, 35 insertions(+), 7 deletions(-) diff --git a/gdplib/logical/positioning.py b/gdplib/logical/positioning.py index b9e610c..a34c8af 100644 --- a/gdplib/logical/positioning.py +++ b/gdplib/logical/positioning.py @@ -23,10 +23,22 @@ def build_model(): + """ + Build the optimal positioning model + + Returns + ------- + m : Pyomo.ConcreteModel + The optimal positioning model + + Notes + ----- + + """ m = ConcreteModel() - m.locations = RangeSet(5) - m.consumers = RangeSet(25) - m.products = RangeSet(10) + m.locations = RangeSet(5) # 5 locations + m.consumers = RangeSet(25) # 25 consumers + m.products = RangeSet(10) # 10 products fixed_profit_data = { 1: 1, @@ -55,7 +67,7 @@ def build_model(): 24: 0.7, 25: 0.7, } - m.fixed_profit = Param(m.consumers, initialize=fixed_profit_data) + m.fixed_profit = Param(m.consumers, initialize=fixed_profit_data, doc='Fixed profit for each consumer') # fixed profit for each consumer minimum_weights_data = { 1: 77.84, @@ -84,7 +96,9 @@ def build_model(): 24: 340.581, 25: 407.52, } - m.minimum_weights = Param(m.consumers, initialize=minimum_weights_data) + m.minimum_weights = Param(m.consumers, initialize=minimum_weights_data, doc='Minimum weights for each consumer') # minimum weights for each consumer + + # Bounds for the locations location_bounds = { 1: (2, 4.5), 2: (0, 8.0), @@ -121,8 +135,8 @@ def build_model(): 24 8.79 5.04 4.83 6.94 0.38 25 2.66 4.19 6.49 8.04 1.66 """) - ideal_points_table = pd.read_csv(ideal_points_data, delimiter=r'\s+') - ideal_points_dict = {(k[0], int(k[1])): v for k, v in ideal_points_table.stack().to_dict().items()} + ideal_points_table = pd.read_csv(ideal_points_data, delimiter=r'\s+') # ideal points for each consumer and product + ideal_points_dict = {(k[0], int(k[1])): v for k, v in ideal_points_table.stack().to_dict().items()} m.ideal_points = Param(m.consumers, m.locations, initialize=ideal_points_dict) weights_data = StringIO(""" @@ -188,6 +202,20 @@ def build_model(): @m.Disjunction(m.consumers) def d(m, i): + """_summary_ + + Parameters + ---------- + m : Pyomo.ConcreteModel + _description_ + i : _type_ + _description_ + + Returns + ------- + _type_ + _description_ + """ return [ [sum(m.weights[i, k] * (m.x[k] - m.ideal_points[i, k]) ** 2 for k in m.locations) - r[i] <= m.U], [] From 9df7ceda02ca3614bcfa06e6e8191deab6470ff7 Mon Sep 17 00:00:00 2001 From: Carolina Tristan Date: Tue, 16 Apr 2024 18:21:33 -0400 Subject: [PATCH 45/99] Update documentation link for Medium-term Purchasing Contracts problem --- gdplib/pyomo_examples/med_term_purchasing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gdplib/pyomo_examples/med_term_purchasing.py b/gdplib/pyomo_examples/med_term_purchasing.py index ad7f47c..7bb7f31 100644 --- a/gdplib/pyomo_examples/med_term_purchasing.py +++ b/gdplib/pyomo_examples/med_term_purchasing.py @@ -13,7 +13,7 @@ from pyomo.common.fileutils import this_file_dir from os.path import join -# Medium-term Purchasing Contracts problem from http://minlp.org/library/lib.php?lib=GDP +# Medium-term Purchasing Contracts problem from https://www.minlp.org/library/problem/index.php?i=129 # This model maximizes profit in a short-term horizon in which various contracts # are available for purchasing raw materials. The model decides inventory levels, # amounts to purchase, amount sold, and flows through the process nodes while From 2818b4374e1afb65eb0e5c004cac1275c751d753 Mon Sep 17 00:00:00 2001 From: Carolina Tristan Date: Tue, 16 Apr 2024 18:23:44 -0400 Subject: [PATCH 46/99] Update copyright year in med_term_purchasing.py --- gdplib/pyomo_examples/med_term_purchasing.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gdplib/pyomo_examples/med_term_purchasing.py b/gdplib/pyomo_examples/med_term_purchasing.py index 7bb7f31..af77c49 100644 --- a/gdplib/pyomo_examples/med_term_purchasing.py +++ b/gdplib/pyomo_examples/med_term_purchasing.py @@ -1,7 +1,8 @@ # ___________________________________________________________________________ # # Pyomo: Python Optimization Modeling Objects -# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Copyright (c) 2008-2024 +# National Technology and Engineering Solutions of Sandia, LLC # Under the terms of Contract DE-NA0003525 with National Technology and # Engineering Solutions of Sandia, LLC, the U.S. Government retains certain # rights in this software. From d153238eac297259051ce79379093a9ca94ecdcf Mon Sep 17 00:00:00 2001 From: Carolina Tristan Date: Fri, 19 Apr 2024 18:07:49 -0400 Subject: [PATCH 47/99] Update references in med_term_purchasing.py --- gdplib/pyomo_examples/med_term_purchasing.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/gdplib/pyomo_examples/med_term_purchasing.py b/gdplib/pyomo_examples/med_term_purchasing.py index af77c49..774c50b 100644 --- a/gdplib/pyomo_examples/med_term_purchasing.py +++ b/gdplib/pyomo_examples/med_term_purchasing.py @@ -49,9 +49,9 @@ def build_model(): References ---------- - [1] Vecchietti, Aldo, and I. Grossmann. "Computational experience with logmip solving linear and nonlinear disjunctive programming problems." In Proc. of FOCAPD, pp. 587-590. 2004. - [2] Vecchietti, A., S. Lee and I.E. Grossmann, “Modeling of Discrete/Continuous Optimization Problems: Characterization and Formulation of Disjunctions and their Relaxations,” - Computers and Chemical Engineering 27, 433-448 (2003). + [1] Vecchietti, A., & Grossmann, I. (2004). Computational experience with logmip solving linear and nonlinear disjunctive programming problems. Proc. of FOCAPD, 587–590. + [2] Vecchietti, A., Lee, S., & Grossmann, I. E. (2003). Modeling of discrete/continuous optimization problems: characterization and formulation of disjunctions and their relaxations. Computers & Chemical Engineering, 27(3), 433–448. DOI: 10.1016/S0098-1354(02)00220-X + [3] Park, M., Park, S., Mele, F. D., & Grossmann, I. E. (2006). Modeling of purchase and sales contracts in supply chain optimization. Industrial and Engineering Chemistry Research, 45(14), 5013–5026. DOI: 10.1021/ie0513144 """ model = AbstractModel() From f43c5e7d863abf722d4e3c4c0654f26d4b7cf560 Mon Sep 17 00:00:00 2001 From: Albert Lee Date: Fri, 19 Apr 2024 19:04:00 -0400 Subject: [PATCH 48/99] Update positioning model with documentation and references --- gdplib/logical/positioning.py | 51 ++++++++++++++++++++--------------- 1 file changed, 30 insertions(+), 21 deletions(-) diff --git a/gdplib/logical/positioning.py b/gdplib/logical/positioning.py index a34c8af..bfa3a91 100644 --- a/gdplib/logical/positioning.py +++ b/gdplib/logical/positioning.py @@ -24,16 +24,21 @@ def build_model(): """ - Build the optimal positioning model + Constructs and returns a Pyomo ConcreteModel object configured to solve the optimal product positioning problem. The model seeks to position a new product in a way that optimizes its location in a multidimensional attribute space, balancing consumer satisfaction against production and positioning costs. Returns ------- m : Pyomo.ConcreteModel - The optimal positioning model + The optimal positioning model that includes variables for product locations, consumer satisfaction indicators, and the constraints and objective function necessary for finding the optimal product positioning strategy. Notes ----- + The model uses a disjunctive programming approach to handle the binary nature of consumer decisions and includes quadratic cost functions to represent varying cost behaviors associated with different product positioning strategies. + References + ---------- + [1] Duran, M. A., & Grossmann, I. E. (1986). An outer-approximation algorithm for a class of mixed-integer nonlinear programs. Mathematical programming, 36, 307-339. https://doi.org/10.1007/BF02592064 + [2] Gavish, B., Horsky, D., & Srikanth, K. (1983). An approach to the optimal positioning of a new product. Management Science, 29(11), 1277-1297. https://doi.org/10.1287/mnsc.29.11.1277 """ m = ConcreteModel() m.locations = RangeSet(5) # 5 locations @@ -137,7 +142,7 @@ def build_model(): """) ideal_points_table = pd.read_csv(ideal_points_data, delimiter=r'\s+') # ideal points for each consumer and product ideal_points_dict = {(k[0], int(k[1])): v for k, v in ideal_points_table.stack().to_dict().items()} - m.ideal_points = Param(m.consumers, m.locations, initialize=ideal_points_dict) + m.ideal_points = Param(m.consumers, m.locations, initialize=ideal_points_dict, doc='Ideal points for each consumer and product') weights_data = StringIO(""" 1 2 3 4 5 @@ -169,7 +174,7 @@ def build_model(): """) weights_table = pd.read_csv(weights_data, delimiter=r'\s+') weights_dict = {(k[0], int(k[1])): v for k, v in weights_table.stack().to_dict().items()} - m.weights = Param(m.consumers, m.locations, initialize=weights_dict) + m.weights = Param(m.consumers, m.locations, initialize=weights_dict, doc='Weights for each consumer and product') existing_products_data = StringIO(""" 1 2 3 4 5 @@ -186,35 +191,38 @@ def build_model(): """) existing_products_table = pd.read_csv(existing_products_data, delimiter=r'\s+') existing_products_dict = {(k[0], int(k[1])): v for k, v in existing_products_table.stack().to_dict().items()} - m.existing_products = Param(m.products, m.locations, initialize=existing_products_dict) + m.existing_products = Param(m.products, m.locations, initialize=existing_products_dict, doc='Existing products for each location and product') # m.consumers * m.products + # Squared weighted Euclidean distance between the existing products and the ideal points rr = {(i, j): sum(m.weights[i, k] * (m.existing_products[j, k] - m.ideal_points[i, k]) * ( m.existing_products[j, k] - m.ideal_points[i, k]) for k in m.locations) for (i, j) in m.consumers * m.products} + # minimum dissimilarity between the existing products and consumers' ideal points r = {i: min(rr[i, j] for j in m.products) for i in m.consumers} - m.x = Var(m.locations) - m.Y = BooleanVar(m.consumers) - m.H = Param(initialize=1000) - m.U = Var(bounds=(0, 5000)) + m.x = Var(m.locations, doc='Location of each product') + m.Y = BooleanVar(m.consumers, doc='Indicates if consumer positioning is satisfactory based on distance to ideal points.') + m.H = Param(initialize=1000, doc='Big M value') + m.U = Var(bounds=(0, 5000), doc='Upper bound on the sum of the distances to the ideal points') # Slack variable @m.Disjunction(m.consumers) def d(m, i): - """_summary_ + """ + Define the disjunction that model whether the distance squared sum of weights and deviations from ideal points for consumer i is less than or equal to a utility threshold (r[i]) adjusted by a slack variable m.U. Parameters ---------- m : Pyomo.ConcreteModel - _description_ - i : _type_ - _description_ + The optimal positioning model + i : int + Index for consumers Returns ------- - _type_ - _description_ + Pyomo.Disjunction + A Pyomo Disjunction object that evaluates if the consumer's preference is met (`true` scenario) with a single Pyomo expression, or not met (`false` scenario) where the list is empty. """ return [ [sum(m.weights[i, k] * (m.x[k] - m.ideal_points[i, k]) ** 2 for k in m.locations) - r[i] <= m.U], @@ -227,15 +235,16 @@ def d(m, i): m.x[k].setlb(lb) m.x[k].setub(ub) - m.c1 = Constraint(expr=m.x[1] - m.x[2] + m.x[3] + m.x[4] + m.x[5] <= 10) - m.c2 = Constraint(expr=0.6 * m.x[1] - 0.9 * m.x[2] - 0.5 * m.x[3] + 0.1 * m.x[4] + m.x[5] <= -0.64) - m.c3 = Constraint(expr=m.x[1] - m.x[2] + m.x[3] - m.x[4] + m.x[5] >= 0.69) - m.c4 = Constraint(expr=0.157 * m.x[1] + 0.05 * m.x[2] <= 1.5) - m.c5 = Constraint(expr=0.25 * m.x[2] + 1.05 * m.x[4] - 0.3 * m.x[5] >= 4.5) + m.c1 = Constraint(expr=m.x[1] - m.x[2] + m.x[3] + m.x[4] + m.x[5] <= 10, doc='Constraint 1') + m.c2 = Constraint(expr=0.6 * m.x[1] - 0.9 * m.x[2] - 0.5 * m.x[3] + 0.1 * m.x[4] + m.x[5] <= -0.64, doc='Constraint 2') + m.c3 = Constraint(expr=m.x[1] - m.x[2] + m.x[3] - m.x[4] + m.x[5] >= 0.69, doc='Constraint 3') + m.c4 = Constraint(expr=0.157 * m.x[1] + 0.05 * m.x[2] <= 1.5, doc='Constraint 4') + m.c5 = Constraint(expr=0.25 * m.x[2] + 1.05 * m.x[4] - 0.3 * m.x[5] >= 4.5, doc='Constraint 5') + # Minimizes total adjusted dissimilarity costs and maximizes consumer-based fixed profit, while balancing quadratic and linear operational costs across multiple product locations. m.obj = Objective( expr=10 * m.U - sum(m.fixed_profit[i] * m.Y[i].get_associated_binary() for i in m.consumers) + 0.6 * m.x[1] ** 2 - 0.9 * m.x[ - 2] - 0.5 * m.x[3] + 0.1 * m.x[4] ** 2 + m.x[5]) + 2] - 0.5 * m.x[3] + 0.1 * m.x[4] ** 2 + m.x[5], doc='Objective function') return m From 5af8182ae5c44e8ace1a4c51164914e40c83b344 Mon Sep 17 00:00:00 2001 From: Albert Lee Date: Fri, 19 Apr 2024 20:23:16 -0400 Subject: [PATCH 49/99] Added explanation of the matrices --- gdplib/logical/spectralog.py | 70 +++++++++++++++++++++++++++++++----- 1 file changed, 61 insertions(+), 9 deletions(-) diff --git a/gdplib/logical/spectralog.py b/gdplib/logical/spectralog.py index 9bb6b17..8427236 100644 --- a/gdplib/logical/spectralog.py +++ b/gdplib/logical/spectralog.py @@ -21,6 +21,25 @@ def build_model(): + """_Summary_ + + Parameters + ---------- + + Returns + ------- + Pyomo.ConcreteModel + A Pyomo model representing the IR spectroscopy parameter estimation problem. + + Notes + ----- + + References + ---------- + [1] Vecchietti, A., & Grossmann, I. E. (1997). LOGMIP: a disjunctive 0–1 nonlinear optimizer for process systems models. Computers & chemical engineering, 21, S427-S432. https://doi.org/10.1016/S0098-1354(97)87539-4 + [2] Brink, A., & Westerlund, T. (1995). The joint problem of model structure determination and parameter estimation in quantitative IR spectroscopy. Chemometrics and intelligent laboratory systems, 29(1), 29-36. https://doi.org/10.1016/0169-7439(95)00033-3 + """ + # Matrix of absorbance values across different wave numbers (rows) and spectra numbers (columns) spectroscopic_data = StringIO(""" 1 2 3 4 5 6 7 8 1 0.0003 0.0764 0.0318 0.0007 0.0534 0.0773 0.0536 0.0320 @@ -39,6 +58,8 @@ def build_model(): flat_spectro_data = spectroscopic_data_table.stack() spectro_data_dict = {(k[0], int(k[1])): v for k, v in flat_spectro_data.to_dict().items()} # column labels to integer + # Measured concentration data for each compound(row) and spectra number(column) + # Units for concentration for each component 1, 2, and 3 are ppm, ppm, and % for CO, NO, and CO2, respectively. c_data = StringIO(""" 1 2 3 4 5 6 7 8 1 502 204 353 702 0 1016 104 204 @@ -48,7 +69,7 @@ def build_model(): c_data_table = pd.read_csv(c_data, delimiter=r'\s+') c_data_dict = {(k[0], int(k[1])): v for k, v in c_data_table.stack().to_dict().items()} - # Covariance matrix + # Covariance matrix; It is assumed to be known that it is equal to the identity matrix at first problem iteration r_data = StringIO(""" 1 2 3 1 1 0 0 @@ -59,13 +80,13 @@ def build_model(): r_data_dict = {(k[0], int(k[1])): v for k, v in r_data_table.stack().to_dict().items()} m = ConcreteModel(name="IR spectroscopy parameter estimation") - m.wave_number = RangeSet(10) - m.spectra_data = RangeSet(8) - m.compounds = RangeSet(3) + m.wave_number = RangeSet(10) # 10 wave numbers + m.spectra_data = RangeSet(8) # 8 spectra data points + m.compounds = RangeSet(3) # 3 compounds; 1, 2, 3 refer to CO, NO, and CO2, respectively - m.A = Param(m.wave_number, m.spectra_data, initialize=spectro_data_dict) - m.C = Param(m.compounds, m.spectra_data, initialize=c_data_dict) - m.R = Param(m.compounds, m.compounds, initialize=r_data_dict) + m.A = Param(m.wave_number, m.spectra_data, initialize=spectro_data_dict, doc='Absorbance data') + m.C = Param(m.compounds, m.spectra_data, initialize=c_data_dict, doc='Concentration data') + m.R = Param(m.compounds, m.compounds, initialize=r_data_dict, doc='Covariance matrix') m.val = Var(m.spectra_data) m.ent = Var(m.compounds, m.wave_number, bounds=(0, 1)) @@ -74,9 +95,25 @@ def build_model(): @m.Disjunction(m.compounds, m.wave_number) def d(m, k, i): + """_summary_ + + Parameters + ---------- + m : Pyomo.ConcreteModel + _description_ + k : int + Index of compounds. + i : int + Index of wave numbers. + + Returns + ------- + Pyomo.Disjunction + _description_ + """ return [ - [m.P[k, i] <= 1000, m.P[k, i] >= 0, m.ent[k, i] == 1], - [m.P[k, i] == 0, m.ent[k, i] == 0] + [m.P[k, i] <= 1000, m.P[k, i] >= 0, m.ent[k, i] == 1], # Conditions for the compound being active + [m.P[k, i] == 0, m.ent[k, i] == 0] # Conditions for the compound being inactive ] for k, i in m.compounds * m.wave_number: @@ -85,6 +122,21 @@ def d(m, k, i): @m.Constraint(m.spectra_data) def eq1(m, j): + """ + Defines a disjunction for each compound and wave number that determines whether a compound is active at a particular wave number based on the parameter estimates. + + Parameters + ---------- + m : Pyomo.ConcreteModel + _description_ + j : int + Index for the spectra data points, representing different experimental conditions. + + Returns + ------- + _type_ + _description_ + """ return m.val[j] == sum( sum((m.C[kk, j] / 100 - sum(m.P[kk, i] * m.A[i, j] for i in m.wave_number)) * m.R[kk, k] From d941ddd0b50ad24fd29761678e0034c365ef6ef9 Mon Sep 17 00:00:00 2001 From: Albert Lee Date: Fri, 19 Apr 2024 20:24:48 -0400 Subject: [PATCH 50/99] Black Formatting --- gdplib/logical/positioning.py | 156 ++++++++++++++++++++++++---------- 1 file changed, 113 insertions(+), 43 deletions(-) diff --git a/gdplib/logical/positioning.py b/gdplib/logical/positioning.py index bfa3a91..8047c21 100644 --- a/gdplib/logical/positioning.py +++ b/gdplib/logical/positioning.py @@ -16,7 +16,9 @@ from pyomo.environ import * from pyomo.gdp import * 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 six import StringIO import pandas as pd @@ -30,7 +32,7 @@ def build_model(): ------- m : Pyomo.ConcreteModel The optimal positioning model that includes variables for product locations, consumer satisfaction indicators, and the constraints and objective function necessary for finding the optimal product positioning strategy. - + Notes ----- The model uses a disjunctive programming approach to handle the binary nature of consumer decisions and includes quadratic cost functions to represent varying cost behaviors associated with different product positioning strategies. @@ -41,9 +43,9 @@ def build_model(): [2] Gavish, B., Horsky, D., & Srikanth, K. (1983). An approach to the optimal positioning of a new product. Management Science, 29(11), 1277-1297. https://doi.org/10.1287/mnsc.29.11.1277 """ m = ConcreteModel() - m.locations = RangeSet(5) # 5 locations - m.consumers = RangeSet(25) # 25 consumers - m.products = RangeSet(10) # 10 products + m.locations = RangeSet(5) # 5 locations + m.consumers = RangeSet(25) # 25 consumers + m.products = RangeSet(10) # 10 products fixed_profit_data = { 1: 1, @@ -72,7 +74,9 @@ def build_model(): 24: 0.7, 25: 0.7, } - m.fixed_profit = Param(m.consumers, initialize=fixed_profit_data, doc='Fixed profit for each consumer') # fixed profit for each consumer + m.fixed_profit = Param( + m.consumers, initialize=fixed_profit_data, doc='Fixed profit for each consumer' + ) # fixed profit for each consumer minimum_weights_data = { 1: 77.84, @@ -101,18 +105,17 @@ def build_model(): 24: 340.581, 25: 407.52, } - m.minimum_weights = Param(m.consumers, initialize=minimum_weights_data, doc='Minimum weights for each consumer') # minimum weights for each consumer + m.minimum_weights = Param( + m.consumers, + initialize=minimum_weights_data, + doc='Minimum weights for each consumer', + ) # minimum weights for each consumer # Bounds for the locations - location_bounds = { - 1: (2, 4.5), - 2: (0, 8.0), - 3: (3, 9.0), - 4: (0, 5.0), - 5: (4, 10), - } + location_bounds = {1: (2, 4.5), 2: (0, 8.0), 3: (3, 9.0), 4: (0, 5.0), 5: (4, 10)} - ideal_points_data = StringIO(""" + ideal_points_data = StringIO( + """ 1 2 3 4 5 1 2.26 5.15 4.03 1.74 4.74 2 5.51 9.01 3.84 1.47 9.92 @@ -139,12 +142,23 @@ def build_model(): 23 1.37 0.54 1.55 5.56 5.85 24 8.79 5.04 4.83 6.94 0.38 25 2.66 4.19 6.49 8.04 1.66 - """) - ideal_points_table = pd.read_csv(ideal_points_data, delimiter=r'\s+') # ideal points for each consumer and product - ideal_points_dict = {(k[0], int(k[1])): v for k, v in ideal_points_table.stack().to_dict().items()} - m.ideal_points = Param(m.consumers, m.locations, initialize=ideal_points_dict, doc='Ideal points for each consumer and product') + """ + ) + ideal_points_table = pd.read_csv( + ideal_points_data, delimiter=r'\s+' + ) # ideal points for each consumer and product + ideal_points_dict = { + (k[0], int(k[1])): v for k, v in ideal_points_table.stack().to_dict().items() + } + m.ideal_points = Param( + m.consumers, + m.locations, + initialize=ideal_points_dict, + doc='Ideal points for each consumer and product', + ) - weights_data = StringIO(""" + weights_data = StringIO( + """ 1 2 3 4 5 1 9.57 2.74 9.75 3.96 8.67 2 8.38 3.93 5.18 5.2 7.82 @@ -171,12 +185,21 @@ def build_model(): 23 1.47 5.71 6.95 1.42 3.49 24 5.4 3.12 5.37 6.1 3.71 25 6.32 0.81 6.12 6.73 7.93 - """) + """ + ) weights_table = pd.read_csv(weights_data, delimiter=r'\s+') - weights_dict = {(k[0], int(k[1])): v for k, v in weights_table.stack().to_dict().items()} - m.weights = Param(m.consumers, m.locations, initialize=weights_dict, doc='Weights for each consumer and product') + weights_dict = { + (k[0], int(k[1])): v for k, v in weights_table.stack().to_dict().items() + } + m.weights = Param( + m.consumers, + m.locations, + initialize=weights_dict, + doc='Weights for each consumer and product', + ) - existing_products_data = StringIO(""" + existing_products_data = StringIO( + """ 1 2 3 4 5 1 0.62 5.06 7.82 0.22 4.42 2 5.21 2.66 9.54 5.03 8.01 @@ -188,24 +211,44 @@ def build_model(): 8 8.35 3.79 1.19 1.96 5.88 9 6.44 0.17 9.93 6.8 9.75 10 6.49 1.92 0.05 4.89 6.43 - """) + """ + ) existing_products_table = pd.read_csv(existing_products_data, delimiter=r'\s+') - existing_products_dict = {(k[0], int(k[1])): v for k, v in existing_products_table.stack().to_dict().items()} - m.existing_products = Param(m.products, m.locations, initialize=existing_products_dict, doc='Existing products for each location and product') + existing_products_dict = { + (k[0], int(k[1])): v + for k, v in existing_products_table.stack().to_dict().items() + } + m.existing_products = Param( + m.products, + m.locations, + initialize=existing_products_dict, + doc='Existing products for each location and product', + ) # m.consumers * m.products # Squared weighted Euclidean distance between the existing products and the ideal points - rr = {(i, j): sum(m.weights[i, k] * (m.existing_products[j, k] - m.ideal_points[i, k]) * ( - m.existing_products[j, k] - m.ideal_points[i, k]) - for k in m.locations) - for (i, j) in m.consumers * m.products} - # minimum dissimilarity between the existing products and consumers' ideal points + rr = { + (i, j): sum( + m.weights[i, k] + * (m.existing_products[j, k] - m.ideal_points[i, k]) + * (m.existing_products[j, k] - m.ideal_points[i, k]) + for k in m.locations + ) + for (i, j) in m.consumers * m.products + } + # minimum dissimilarity between the existing products and consumers' ideal points r = {i: min(rr[i, j] for j in m.products) for i in m.consumers} m.x = Var(m.locations, doc='Location of each product') - m.Y = BooleanVar(m.consumers, doc='Indicates if consumer positioning is satisfactory based on distance to ideal points.') + m.Y = BooleanVar( + m.consumers, + doc='Indicates if consumer positioning is satisfactory based on distance to ideal points.', + ) m.H = Param(initialize=1000, doc='Big M value') - m.U = Var(bounds=(0, 5000), doc='Upper bound on the sum of the distances to the ideal points') # Slack variable + m.U = Var( + bounds=(0, 5000), + doc='Upper bound on the sum of the distances to the ideal points', + ) # Slack variable @m.Disjunction(m.consumers) def d(m, i): @@ -225,9 +268,17 @@ def d(m, i): A Pyomo Disjunction object that evaluates if the consumer's preference is met (`true` scenario) with a single Pyomo expression, or not met (`false` scenario) where the list is empty. """ return [ - [sum(m.weights[i, k] * (m.x[k] - m.ideal_points[i, k]) ** 2 for k in m.locations) - r[i] <= m.U], - [] + [ + sum( + m.weights[i, k] * (m.x[k] - m.ideal_points[i, k]) ** 2 + for k in m.locations + ) + - r[i] + <= m.U + ], + [], ] + for i in m.consumers: m.Y[i].associate_binary_var(m.d[i].disjuncts[0].binary_indicator_var) for k in m.locations: @@ -235,16 +286,33 @@ def d(m, i): m.x[k].setlb(lb) m.x[k].setub(ub) - m.c1 = Constraint(expr=m.x[1] - m.x[2] + m.x[3] + m.x[4] + m.x[5] <= 10, doc='Constraint 1') - m.c2 = Constraint(expr=0.6 * m.x[1] - 0.9 * m.x[2] - 0.5 * m.x[3] + 0.1 * m.x[4] + m.x[5] <= -0.64, doc='Constraint 2') - m.c3 = Constraint(expr=m.x[1] - m.x[2] + m.x[3] - m.x[4] + m.x[5] >= 0.69, doc='Constraint 3') + m.c1 = Constraint( + expr=m.x[1] - m.x[2] + m.x[3] + m.x[4] + m.x[5] <= 10, doc='Constraint 1' + ) + m.c2 = Constraint( + expr=0.6 * m.x[1] - 0.9 * m.x[2] - 0.5 * m.x[3] + 0.1 * m.x[4] + m.x[5] + <= -0.64, + doc='Constraint 2', + ) + m.c3 = Constraint( + expr=m.x[1] - m.x[2] + m.x[3] - m.x[4] + m.x[5] >= 0.69, doc='Constraint 3' + ) m.c4 = Constraint(expr=0.157 * m.x[1] + 0.05 * m.x[2] <= 1.5, doc='Constraint 4') - m.c5 = Constraint(expr=0.25 * m.x[2] + 1.05 * m.x[4] - 0.3 * m.x[5] >= 4.5, doc='Constraint 5') + m.c5 = Constraint( + expr=0.25 * m.x[2] + 1.05 * m.x[4] - 0.3 * m.x[5] >= 4.5, doc='Constraint 5' + ) # Minimizes total adjusted dissimilarity costs and maximizes consumer-based fixed profit, while balancing quadratic and linear operational costs across multiple product locations. m.obj = Objective( - expr=10 * m.U - sum(m.fixed_profit[i] * m.Y[i].get_associated_binary() for i in m.consumers) + 0.6 * m.x[1] ** 2 - 0.9 * m.x[ - 2] - 0.5 * m.x[3] + 0.1 * m.x[4] ** 2 + m.x[5], doc='Objective function') + expr=10 * m.U + - sum(m.fixed_profit[i] * m.Y[i].get_associated_binary() for i in m.consumers) + + 0.6 * m.x[1] ** 2 + - 0.9 * m.x[2] + - 0.5 * m.x[3] + + 0.1 * m.x[4] ** 2 + + m.x[5], + doc='Objective function', + ) return m @@ -254,6 +322,8 @@ def d(m, i): TransformationFactory('core.logical_to_linear').apply_to(m) # res = SolverFactory('gdpopt').solve(m, tee=True, nlp_solver='gams') TransformationFactory('gdp.bigm').apply_to(m) - SolverFactory('gams').solve(m, tee=True, solver='baron', add_options=['option optcr=0;']) + SolverFactory('gams').solve( + m, tee=True, solver='baron', add_options=['option optcr=0;'] + ) update_boolean_vars_from_binary(m) m.Y.display() From 10666b5b51c04f5490a7cd253e242c0318b68684 Mon Sep 17 00:00:00 2001 From: Albert Lee Date: Fri, 19 Apr 2024 20:57:17 -0400 Subject: [PATCH 51/99] Added documentation of the IR parameter estimation problem. --- gdplib/logical/spectralog.py | 132 ++++++++++++++++++++++++----------- 1 file changed, 92 insertions(+), 40 deletions(-) diff --git a/gdplib/logical/spectralog.py b/gdplib/logical/spectralog.py index 8427236..c34496b 100644 --- a/gdplib/logical/spectralog.py +++ b/gdplib/logical/spectralog.py @@ -15,16 +15,16 @@ from pyomo.environ import * from pyomo.gdp import * 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 six import StringIO import pandas as pd def build_model(): - """_Summary_ - - Parameters - ---------- + """ + Constructs and returns a Pyomo Concrete Model for IR spectroscopy parameter estimation. Returns ------- @@ -33,6 +33,8 @@ def build_model(): Notes ----- + - The model uses a disjunctive programming approach where decision variables can trigger different sets of constraints, + representing different physcochemical conditions. References ---------- @@ -40,7 +42,8 @@ def build_model(): [2] Brink, A., & Westerlund, T. (1995). The joint problem of model structure determination and parameter estimation in quantitative IR spectroscopy. Chemometrics and intelligent laboratory systems, 29(1), 29-36. https://doi.org/10.1016/0169-7439(95)00033-3 """ # Matrix of absorbance values across different wave numbers (rows) and spectra numbers (columns) - spectroscopic_data = StringIO(""" + spectroscopic_data = StringIO( + """ 1 2 3 4 5 6 7 8 1 0.0003 0.0764 0.0318 0.0007 0.0534 0.0773 0.0536 0.0320 2 0.0007 0.0003 0.0004 0.0009 0.0005 0.0009 0.0005 0.0003 @@ -52,46 +55,84 @@ def build_model(): 8 0.0507 0.0361 0.0433 0.0635 0.0048 0.0891 0.0213 0.0310 9 0.0905 0.0600 0.0754 0.1098 0.0038 0.1443 0.0420 0.0574 10 0.0016 0.0209 0.0063 0.0010 0.0132 0.0203 0.0139 0.0057 - """) + """ + ) # Note: this could come from an external data file spectroscopic_data_table = pd.read_csv(spectroscopic_data, delimiter=r'\s+') flat_spectro_data = spectroscopic_data_table.stack() - spectro_data_dict = {(k[0], int(k[1])): v for k, v in flat_spectro_data.to_dict().items()} # column labels to integer + spectro_data_dict = { + (k[0], int(k[1])): v for k, v in flat_spectro_data.to_dict().items() + } # column labels to integer # Measured concentration data for each compound(row) and spectra number(column) # Units for concentration for each component 1, 2, and 3 are ppm, ppm, and % for CO, NO, and CO2, respectively. - c_data = StringIO(""" + c_data = StringIO( + """ 1 2 3 4 5 6 7 8 1 502 204 353 702 0 1016 104 204 2 97 351 351 351 700 0 201 97 3 0 22 8 0 14 22 14 8 - """) + """ + ) c_data_table = pd.read_csv(c_data, delimiter=r'\s+') - c_data_dict = {(k[0], int(k[1])): v for k, v in c_data_table.stack().to_dict().items()} + c_data_dict = { + (k[0], int(k[1])): v for k, v in c_data_table.stack().to_dict().items() + } # Covariance matrix; It is assumed to be known that it is equal to the identity matrix at first problem iteration - r_data = StringIO(""" + r_data = StringIO( + """ 1 2 3 1 1 0 0 2 0 1 0 3 0 0 1 - """) + """ + ) r_data_table = pd.read_csv(r_data, delimiter=r'\s+') - r_data_dict = {(k[0], int(k[1])): v for k, v in r_data_table.stack().to_dict().items()} + r_data_dict = { + (k[0], int(k[1])): v for k, v in r_data_table.stack().to_dict().items() + } m = ConcreteModel(name="IR spectroscopy parameter estimation") - m.wave_number = RangeSet(10) # 10 wave numbers - m.spectra_data = RangeSet(8) # 8 spectra data points - m.compounds = RangeSet(3) # 3 compounds; 1, 2, 3 refer to CO, NO, and CO2, respectively - - m.A = Param(m.wave_number, m.spectra_data, initialize=spectro_data_dict, doc='Absorbance data') - m.C = Param(m.compounds, m.spectra_data, initialize=c_data_dict, doc='Concentration data') - m.R = Param(m.compounds, m.compounds, initialize=r_data_dict, doc='Covariance matrix') - - m.val = Var(m.spectra_data) - m.ent = Var(m.compounds, m.wave_number, bounds=(0, 1)) - m.Y = BooleanVar(m.compounds, m.wave_number) - m.P = Var(m.compounds, m.wave_number, bounds=(0, 1000)) + m.wave_number = RangeSet(10) # 10 wave numbers + m.spectra_data = RangeSet(8) # 8 spectra data points + m.compounds = RangeSet( + 3 + ) # 3 compounds; 1, 2, 3 refer to CO, NO, and CO2, respectively + + m.A = Param( + m.wave_number, + m.spectra_data, + initialize=spectro_data_dict, + doc='Absorbance data', + ) + m.C = Param( + m.compounds, m.spectra_data, initialize=c_data_dict, doc='Concentration data' + ) + m.R = Param( + m.compounds, m.compounds, initialize=r_data_dict, doc='Covariance matrix' + ) + + m.val = Var( + m.spectra_data, doc='Calculated objective values for each spectra data point' + ) + m.ent = Var( + m.compounds, + m.wave_number, + bounds=(0, 1), + doc='Binary variables affecting the objective function and constraints.', + ) + m.Y = BooleanVar( + m.compounds, + m.wave_number, + doc='Boolean decisions for compound presence at each wave number.', + ) + m.P = Var( + m.compounds, + m.wave_number, + bounds=(0, 1000), + doc='Continuous variables estimating the concentration level of each compound at each wave number.', + ) @m.Disjunction(m.compounds, m.wave_number) def d(m, k, i): @@ -100,7 +141,7 @@ def d(m, k, i): Parameters ---------- m : Pyomo.ConcreteModel - _description_ + A Pyomo model representing the IR spectroscopy parameter estimation problem. k : int Index of compounds. i : int @@ -109,16 +150,23 @@ def d(m, k, i): Returns ------- Pyomo.Disjunction - _description_ + A disjunctive constraint that specifies the operational conditions for each compound-wave number pair based on the model's parameters. """ return [ - [m.P[k, i] <= 1000, m.P[k, i] >= 0, m.ent[k, i] == 1], # Conditions for the compound being active - [m.P[k, i] == 0, m.ent[k, i] == 0] # Conditions for the compound being inactive + [ + m.P[k, i] <= 1000, + m.P[k, i] >= 0, + m.ent[k, i] == 1, + ], # Conditions for the compound being active + [ + m.P[k, i] == 0, + m.ent[k, i] == 0, + ], # Conditions for the compound being inactive ] + # Associate each Boolean variable with a corresponding binary variable to handle logical conditions. for k, i in m.compounds * m.wave_number: - m.Y[k, i].associate_binary_var( - m.d[k, i].disjuncts[0].binary_indicator_var) + m.Y[k, i].associate_binary_var(m.d[k, i].disjuncts[0].binary_indicator_var) @m.Constraint(m.spectra_data) def eq1(m, j): @@ -128,25 +176,31 @@ def eq1(m, j): Parameters ---------- m : Pyomo.ConcreteModel - _description_ + A Pyomo model representing the IR spectroscopy parameter estimation problem. j : int Index for the spectra data points, representing different experimental conditions. Returns ------- - _type_ - _description_ + Pyomo.Constraint + An expression that equates the calculated value for each spectra data point with a mathematically derived expression from the model. """ return m.val[j] == sum( - sum((m.C[kk, j] / 100 - sum(m.P[kk, i] * m.A[i, j] for i in m.wave_number)) + sum( + (m.C[kk, j] / 100 - sum(m.P[kk, i] * m.A[i, j] for i in m.wave_number)) * m.R[kk, k] - for kk in m.compounds) + for kk in m.compounds + ) * (m.C[k, j] / 100 - sum(m.P[k, i] * m.A[i, j] for i in m.wave_number)) for k in m.compounds ) m.profit = Objective( - expr=sum(m.val[j] for j in m.spectra_data) + 2 * sum(m.ent[k, i] for k in m.compounds for i in m.wave_number)) + expr=sum(m.val[j] for j in m.spectra_data) + + 2 * sum(m.ent[k, i] for k in m.compounds for i in m.wave_number), + doc='Objective to maximize spectroscopic agreement and encourage compound presence.', + ) + # The first sum represents total spectroscopic value across data points, and the second weighted sum promotes activation of compound-wave number pairs. return m @@ -161,5 +215,3 @@ def eq1(m, j): m.profit.display() m.Y.display() m.P.display() - - From 8a0b4a6d769503d62c67d070dfe24245fc1e62aa Mon Sep 17 00:00:00 2001 From: Albert Lee Date: Tue, 23 Apr 2024 11:20:56 -0400 Subject: [PATCH 52/99] Updated medium-term purchasing contracts problem documentation --- gdplib/pyomo_examples/med_term_purchasing.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/gdplib/pyomo_examples/med_term_purchasing.py b/gdplib/pyomo_examples/med_term_purchasing.py index 774c50b..0ee4a74 100644 --- a/gdplib/pyomo_examples/med_term_purchasing.py +++ b/gdplib/pyomo_examples/med_term_purchasing.py @@ -40,13 +40,6 @@ def build_model(): Pyomo.AbstractModel Pyomo abstract model for medium-term purchasing contracts problem. - Raises - ------ - RuntimeError - _description_ - RuntimeError - _description_ - References ---------- [1] Vecchietti, A., & Grossmann, I. (2004). Computational experience with logmip solving linear and nonlinear disjunctive programming problems. Proc. of FOCAPD, 587–590. From 10e91a456866aaf34d93b27fb00b0d5107129e4e Mon Sep 17 00:00:00 2001 From: Albert Lee Date: Tue, 23 Apr 2024 12:01:03 -0400 Subject: [PATCH 53/99] Added References --- gdplib/pyomo_examples/med_term_purchasing.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/gdplib/pyomo_examples/med_term_purchasing.py b/gdplib/pyomo_examples/med_term_purchasing.py index 0ee4a74..240d80f 100644 --- a/gdplib/pyomo_examples/med_term_purchasing.py +++ b/gdplib/pyomo_examples/med_term_purchasing.py @@ -42,9 +42,15 @@ def build_model(): References ---------- +<<<<<<< HEAD [1] Vecchietti, A., & Grossmann, I. (2004). Computational experience with logmip solving linear and nonlinear disjunctive programming problems. Proc. of FOCAPD, 587–590. [2] Vecchietti, A., Lee, S., & Grossmann, I. E. (2003). Modeling of discrete/continuous optimization problems: characterization and formulation of disjunctions and their relaxations. Computers & Chemical Engineering, 27(3), 433–448. DOI: 10.1016/S0098-1354(02)00220-X [3] Park, M., Park, S., Mele, F. D., & Grossmann, I. E. (2006). Modeling of purchase and sales contracts in supply chain optimization. Industrial and Engineering Chemistry Research, 45(14), 5013–5026. DOI: 10.1021/ie0513144 +======= + [1] Vecchietti, Aldo, and I. Grossmann. "Computational experience with logmip solving linear and nonlinear disjunctive programming problems." In Proc. of FOCAPD, pp. 587-590. 2004. + [2] Vecchietti, A., S. Lee and I.E. Grossmann, “Modeling of Discrete/Continuous Optimization Problems: Characterization and Formulation of Disjunctions and their Relaxations,” Computers and Chemical Engineering 27, 433-448 (2003). https://doi.org/10.1016/S0098-1354(02)00220-X + [3] Park, M., Park, S., Mele, F. D., & Grossmann, I. E. (2006). Modeling of purchase and sales contracts in supply chain optimization. Industrial and Engineering Chemistry Research, 45(14), 5013–5026. https://doi.org/10.1021/ie0513144 +>>>>>>> a04daf9 (Added References) """ model = AbstractModel() From 1080e3abebc8878b37d9bd59bea4a978c02c7989 Mon Sep 17 00:00:00 2001 From: Albert Lee Date: Tue, 23 Apr 2024 12:13:06 -0400 Subject: [PATCH 54/99] black formatting applied --- gdplib/pyomo_examples/med_term_purchasing.py | 978 ++++++++++++++----- 1 file changed, 719 insertions(+), 259 deletions(-) diff --git a/gdplib/pyomo_examples/med_term_purchasing.py b/gdplib/pyomo_examples/med_term_purchasing.py index 240d80f..4e05b5f 100644 --- a/gdplib/pyomo_examples/med_term_purchasing.py +++ b/gdplib/pyomo_examples/med_term_purchasing.py @@ -31,6 +31,7 @@ # ShortTermContractCH.gms from the website. Some data is hardcoded into this model, # most notably the process structure itself and the mass balance information. + def build_model(): """ Build a Pyomo abstract model for the medium-term purchasing contracts problem. @@ -83,7 +84,9 @@ def build_model(): # JM # rawmat(J) in GAMS # Set of Raw Materials-- raw materials, intermediate products, and final products partition J - model.RawMaterials = Set(doc="Set of raw materials, intermediate products, and final products") + model.RawMaterials = Set( + doc="Set of raw materials, intermediate products, and final products" + ) # C # c in GAMS @@ -97,7 +100,6 @@ def build_model(): # j in GAMS model.Streams = Set(doc='Set of streams in the network') - ################## # Parameters ################## @@ -112,77 +114,140 @@ def build_model(): # a_jt^U and d_jt^U # spdm(j,t) in GAMS - model.SupplyAndDemandUBs = Param(model.Streams, model.TimePeriods, default=0, doc='Supply and demand upper bounds') + model.SupplyAndDemandUBs = Param( + model.Streams, + model.TimePeriods, + default=0, + doc='Supply and demand upper bounds', + ) # d_jt^L # lbdm(j, t) in GAMS - model.DemandLB = Param(model.Streams, model.TimePeriods, default=0, doc='Demand lower bounds') + model.DemandLB = Param( + model.Streams, model.TimePeriods, default=0, doc='Demand lower bounds' + ) # delta_it # delta(i, t) in GAMS # operating cost of process i at time t - model.OperatingCosts = Param(model.Processes, model.TimePeriods, doc='Operating cost of process i at time t') + model.OperatingCosts = Param( + model.Processes, model.TimePeriods, doc='Operating cost of process i at time t' + ) # prices of raw materials under FP contract and selling prices of products # pf(j, t) in GAMS # omega_jt and pf_jt - model.Prices = Param(model.Streams, model.TimePeriods, default=0, doc='Prices of raw materials under FP contract and selling prices of products') + model.Prices = Param( + model.Streams, + model.TimePeriods, + default=0, + doc='Prices of raw materials under FP contract and selling prices of products', + ) # Price for quantities less than min amount under discount contract # pd1(j, t) in GAMS - model.RegPrice_Discount = Param(model.Streams, model.TimePeriod, doc='Price for quantities less than min amount under discount contract') + model.RegPrice_Discount = Param( + model.Streams, + model.TimePeriod, + doc='Price for quantities less than min amount under discount contract', + ) # Discounted price for the quantity purchased exceeding the min amount # pd2(j,t0 in GAMS - model.DiscountPrice_Discount = Param(model.Streams, model.TimePeriods, doc='Discounted price for the quantity purchased exceeding the min amount') + model.DiscountPrice_Discount = Param( + model.Streams, + model.TimePeriods, + doc='Discounted price for the quantity purchased exceeding the min amount', + ) # Price for quantities below min amount # pb1(j,t) in GAMS - model.RegPrice_Bulk = Param(model.Streams, model.TimePeriods, doc='Price for quantities below min amount under bulk contract') + model.RegPrice_Bulk = Param( + model.Streams, + model.TimePeriods, + doc='Price for quantities below min amount under bulk contract', + ) # Price for quantities above min amount # pb2(j, t) in GAMS - model.DiscountPrice_Bulk = Param(model.Streams, model.TimePeriods, doc='Price for quantities above minimum amount under bulk contract') + model.DiscountPrice_Bulk = Param( + model.Streams, + model.TimePeriods, + doc='Price for quantities above minimum amount under bulk contract', + ) # prices with length contract # pl(j, p, t) in GAMS - model.Prices_Length = Param(model.Streams, model.Contracts_Length, model.TimePeriods, default=0, doc='Prices with length contract') + model.Prices_Length = Param( + model.Streams, + model.Contracts_Length, + model.TimePeriods, + default=0, + doc='Prices with length contract', + ) # sigmad_jt # sigmad(j, t) in GAMS # Minimum quantity of chemical j that must be bought before recieving a Discount under discount contract - model.MinAmount_Discount = Param(model.Streams, model.TimePeriods, default=0, doc='Minimum quantity of chemical j that must be bought before receiving a Discount under discount contract') + model.MinAmount_Discount = Param( + model.Streams, + model.TimePeriods, + default=0, + 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 # sigmab(j, t) in GAMS - model.MinAmount_Bulk = Param(model.Streams, model.TimePeriods, default=0, doc='Minimum quantity of chemical j that must be bought before receiving a Discount under bulk contract') + model.MinAmount_Bulk = Param( + model.Streams, + model.TimePeriods, + default=0, + 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 # sigmal(j, p) in GAMS - model.MinAmount_Length = Param(model.Streams, model.Contracts_Length, default=0, doc='Minimum quantity of chemical j that must be bought before receiving a Discount under length contract') + model.MinAmount_Length = Param( + model.Streams, + model.Contracts_Length, + default=0, + doc='Minimum quantity of chemical j that must be bought before receiving a Discount under length contract', + ) # main products of process i # These are 1 (true) if stream j is the main product of process i, false otherwise. # jm(j, i) in GAMS - model.MainProducts = Param(model.Streams, model.Processes, default=0, doc='Main products of process i') + model.MainProducts = Param( + model.Streams, model.Processes, default=0, doc='Main products of process i' + ) # theta_jt # psf(j, t) in GAMS # Shortfall penalty of product j at time t - model.ShortfallPenalty = Param(model.Products, model.TimePeriods, doc='Shortfall penalty of product j at time t') + model.ShortfallPenalty = Param( + model.Products, + model.TimePeriods, + doc='Shortfall penalty of product j at time t', + ) # shortfall upper bound # sfub(j, t) in GAMS - model.ShortfallUB = Param(model.Products, model.TimePeriods, default=0, doc='Shortfall upper bound') + model.ShortfallUB = Param( + model.Products, model.TimePeriods, default=0, doc='Shortfall upper bound' + ) # epsilon_jt # cinv(j, t) in GAMS # inventory cost of material j at time t - model.InventoryCost = Param(model.Streams, model.TimePeriods, doc='Inventory cost of material j at time t') + model.InventoryCost = Param( + model.Streams, model.TimePeriods, doc='Inventory cost of material j at time t' + ) # invub(j, t) in GAMS # inventory upper bound - model.InventoryLevelUB = Param(model.Streams, model.TimePeriods, default=0, doc='Inventory upper bound') + model.InventoryLevelUB = Param( + model.Streams, model.TimePeriods, default=0, doc='Inventory upper bound' + ) ## UPPER BOUNDS HARDCODED INTO GAMS MODEL @@ -228,27 +293,70 @@ def getCostUBs(model, j, t): """ return COST_UB - model.AmountPurchasedUB_FP = Param(model.Streams, model.TimePeriods, - initialize=getAmountUBs, doc='Upper bound on amount purchased under fixed price contract') - model.AmountPurchasedUB_Discount = Param(model.Streams, model.TimePeriods, - initialize=getAmountUBs, doc='Upper bound on amount purchased under discount contract') - model.AmountPurchasedBelowMinUB_Discount = Param(model.Streams, model.TimePeriods, - initialize=getAmountUBs, doc='Upper bound on amount purchased below min amount for discount under discount contract') - model.AmountPurchasedAboveMinUB_Discount = Param(model.Streams, model.TimePeriods, - initialize=getAmountUBs, doc='Upper bound on amount purchased above min amount for discount under discount contract') - model.AmountPurchasedUB_FD = Param(model.Streams, model.TimePeriods, - initialize=getAmountUBs, doc='Upper bound on amount purchased under fixed duration contract') - model.AmountPurchasedUB_Bulk = Param(model.Streams, model.TimePeriods, - initialize=getAmountUBs, doc='Upper bound on amount purchased under bulk contract') - - model.CostUB_FP = Param(model.Streams, model.TimePeriods, initialize=getCostUBs, doc='Upper bound on cost of fixed price contract') - model.CostUB_FD = Param(model.Streams, model.TimePeriods, initialize=getCostUBs, doc='Upper bound on cost of fixed duration contract') - model.CostUB_Discount = Param(model.Streams, model.TimePeriods, initialize=getCostUBs, DOC='Upper bound on cost of discount contract') - model.CostUB_Bulk = Param(model.Streams, model.TimePeriods, initialize=getCostUBs, doc='Upper bound on cost of bulk contract') - + model.AmountPurchasedUB_FP = Param( + model.Streams, + model.TimePeriods, + initialize=getAmountUBs, + doc='Upper bound on amount purchased under fixed price contract', + ) + model.AmountPurchasedUB_Discount = Param( + model.Streams, + model.TimePeriods, + initialize=getAmountUBs, + doc='Upper bound on amount purchased under discount contract', + ) + model.AmountPurchasedBelowMinUB_Discount = Param( + model.Streams, + model.TimePeriods, + initialize=getAmountUBs, + doc='Upper bound on amount purchased below min amount for discount under discount contract', + ) + model.AmountPurchasedAboveMinUB_Discount = Param( + model.Streams, + model.TimePeriods, + initialize=getAmountUBs, + doc='Upper bound on amount purchased above min amount for discount under discount contract', + ) + model.AmountPurchasedUB_FD = Param( + model.Streams, + model.TimePeriods, + initialize=getAmountUBs, + doc='Upper bound on amount purchased under fixed duration contract', + ) + model.AmountPurchasedUB_Bulk = Param( + model.Streams, + model.TimePeriods, + initialize=getAmountUBs, + doc='Upper bound on amount purchased under bulk contract', + ) + + model.CostUB_FP = Param( + model.Streams, + model.TimePeriods, + initialize=getCostUBs, + doc='Upper bound on cost of fixed price contract', + ) + model.CostUB_FD = Param( + model.Streams, + model.TimePeriods, + initialize=getCostUBs, + doc='Upper bound on cost of fixed duration contract', + ) + model.CostUB_Discount = Param( + model.Streams, + model.TimePeriods, + initialize=getCostUBs, + DOC='Upper bound on cost of discount contract', + ) + model.CostUB_Bulk = Param( + model.Streams, + model.TimePeriods, + initialize=getCostUBs, + doc='Upper bound on cost of bulk contract', + ) #################### - #VARIABLES + # VARIABLES #################### # prof in GAMS @@ -257,7 +365,12 @@ def getCostUBs(model, j, t): # f(j, t) in GAMS # mass flow rates in tons per time interval t - model.FlowRate = Var(model.Streams, model.TimePeriods, within=NonNegativeReals, doc='Mass flow rates in tons per time interval t') + model.FlowRate = Var( + model.Streams, + model.TimePeriods, + within=NonNegativeReals, + doc='Mass flow rates in tons per time interval t', + ) # V_jt # inv(j, t) in GAMS @@ -278,12 +391,17 @@ def getInventoryBounds(model, i, j): Returns ------- tuple - A tuple containing two floats: the lower bound (0) and the upper bound for the inventory level of material j. + A tuple containing two floats: the lower bound (0) and the upper bound for the inventory level of material j. The upper bound is retrieved from the model's 'InventoryLevelUB' parameter using indices i and j. """ - return (0, model.InventoryLevelUB[i,j]) - model.InventoryLevel = Var(model.Streams, model.TimePeriods, - bounds=getInventoryBounds, doc='Inventory level of material j at time period t') + return (0, model.InventoryLevelUB[i, j]) + + model.InventoryLevel = Var( + model.Streams, + model.TimePeriods, + bounds=getInventoryBounds, + doc='Inventory level of material j at time period t', + ) # SF_jt # sf(j, t) in GAMS @@ -307,10 +425,14 @@ def getShortfallBounds(model, i, j): A tuple (0, upper_bound), where 0 is the lower bound (nonnegative shortfalls) and 'upper_bound' is extracted from 'model.ShortfallUB[i, j]'. 'model.ShortfallUB[i, j]' represents the maximal permitted shortfall for material i in the context of process i. """ - return(0, model.ShortfallUB[i,j]) - model.Shortfall = Var(model.Products, model.TimePeriods, - bounds=getShortfallBounds, doc='Shortfall of demand for material j at time period t') + return (0, model.ShortfallUB[i, j]) + model.Shortfall = Var( + model.Products, + model.TimePeriods, + bounds=getShortfallBounds, + doc='Shortfall of demand for material j at time period t', + ) # amounts purchased under different contracts @@ -332,12 +454,17 @@ def get_FP_bounds(model, j, t): Returns ------- tuple - A tuple (0, upper_bound), where '0' is the lower bound (non-negative constraint) and 'upper_bound' is sourced from 'model.AmountPurchasedUB_FP[j,t]'. + A tuple (0, upper_bound), where '0' is the lower bound (non-negative constraint) and 'upper_bound' is sourced from 'model.AmountPurchasedUB_FP[j,t]'. 'model.AmountPurchasedUB_FP[j,t]' represents the maximum allowed purchase amount for material 'j' in period 't' """ - return (0, model.AmountPurchasedUB_FP[j,t]) - model.AmountPurchased_FP = Var(model.Streams, model.TimePeriods, - bounds=get_FP_bounds, doc='Amount of raw material j bought under fixed price contract at time period t') + return (0, model.AmountPurchasedUB_FP[j, t]) + + model.AmountPurchased_FP = Var( + model.Streams, + model.TimePeriods, + bounds=get_FP_bounds, + doc='Amount of raw material j bought under fixed price contract at time period t', + ) # spd(j, t) in GAMS def get_Discount_Total_bounds(model, j, t): @@ -356,12 +483,17 @@ def get_Discount_Total_bounds(model, j, t): Returns ------- tuple - A tuple containing two elements: the lower bound (0) and the upper bound for the total amount of material j that can be purchased under discount contracts in period t. + A tuple containing two elements: the lower bound (0) and the upper bound for the total amount of material j that can be purchased under discount contracts in period t. The upper bound is retrieved from 'model.AmountPurchasedUB_Discount[j, t]'. """ - return (0, model.AmountPurchasedUB_Discount[j,t]) - model.AmountPurchasedTotal_Discount = Var(model.Streams, model.TimePeriods, - bounds=get_Discount_Total_bounds, doc='Total amount of material j bought under discount contract at time period t') + return (0, model.AmountPurchasedUB_Discount[j, t]) + + model.AmountPurchasedTotal_Discount = Var( + model.Streams, + model.TimePeriods, + bounds=get_Discount_Total_bounds, + doc='Total amount of material j bought under discount contract at time period t', + ) # Amount purchased below min amount for discount under discount contract # spd1(j, t) in GAMS @@ -381,12 +513,17 @@ def get_Discount_BelowMin_bounds(model, j, t): Returns ------- tuple - A tuple (0, upper_bound), where '0' is the lower bound, and 'upper_bound' is 'model.AmountPurchasedBelowMinUB_Discount[j, t]'. + A tuple (0, upper_bound), where '0' is the lower bound, and 'upper_bound' is 'model.AmountPurchasedBelowMinUB_Discount[j, t]'. 'model.AmountPurchasedBelowMinUB_Discount[j, t] indicates the maximal purchasable amount below the discount threshold for material j in period't. """ - return (0, model.AmountPurchasedBelowMinUB_Discount[j,t]) - model.AmountPurchasedBelowMin_Discount = Var(model.Streams, - model.TimePeriods, bounds=get_Discount_BelowMin_bounds, doc='Amount purchased below min amount for discount under discount contract') + return (0, model.AmountPurchasedBelowMinUB_Discount[j, t]) + + model.AmountPurchasedBelowMin_Discount = Var( + model.Streams, + model.TimePeriods, + bounds=get_Discount_BelowMin_bounds, + doc='Amount purchased below min amount for discount under discount contract', + ) # spd2(j, t) in GAMS # Amount purchased above min amount for discount under discount contract @@ -406,12 +543,17 @@ def get_Discount_AboveMin_bounds(model, j, t): Returns ------- tuple - A tuple (0, upper_bound), where '0' is the lower bound and 'upper_bound' is sourced from 'model.AmountPurchasedBelowMinUB_Discount[j, t]'. + A tuple (0, upper_bound), where '0' is the lower bound and 'upper_bound' is sourced from 'model.AmountPurchasedBelowMinUB_Discount[j, t]'. 'model.AmountPurchasedBelowMinUB_Discount[j, t]' indicates the maximum amount that can be purchased above the discount threshold for material j in period t. """ - return (0, model.AmountPurchasedBelowMinUB_Discount[j,t]) - model.AmountPurchasedAboveMin_Discount = Var(model.Streams, - model.TimePeriods, bounds=get_Discount_AboveMin_bounds, doc='Amount purchased above min amount for discount under discount contract') + return (0, model.AmountPurchasedBelowMinUB_Discount[j, t]) + + model.AmountPurchasedAboveMin_Discount = Var( + model.Streams, + model.TimePeriods, + bounds=get_Discount_AboveMin_bounds, + doc='Amount purchased above min amount for discount under discount contract', + ) # Amount purchased under bulk contract # spb(j, t) in GAMS @@ -434,9 +576,14 @@ def get_bulk_bounds(model, j, t): A tuple (0, upper_bound), where '0' is the lower bound and 'upper_bound' is sourced from 'model.AmountPurchasedUB_Bulk[j,t]'. 'model.AmountPurchasedUB_Bulk[j,t]' indicates the maximal allowable bulk purchase amount for material 'j' in period 't'. """ - return (0, model.AmountPurchasedUB_Bulk[j,t]) - model.AmountPurchased_Bulk = Var(model.Streams, model.TimePeriods, - bounds=get_bulk_bounds, doc='Amount purchased under bulk contract') + return (0, model.AmountPurchasedUB_Bulk[j, t]) + + model.AmountPurchased_Bulk = Var( + model.Streams, + model.TimePeriods, + bounds=get_bulk_bounds, + doc='Amount purchased under bulk contract', + ) # spl(j, t) in GAMS # Amount purchased under Fixed Duration contract @@ -459,10 +606,14 @@ def get_FD_bounds(model, j, t): A tuple (0, upper_bound), where '0' is the lower bound and upper_bound is taken from 'model.AmountPurchasedUB_FD[j, t]'. 'model.AmountPurchasedUB_FD[j, t]' indicates the maximum permissible purchase quantity for material j under a fixed duration contract in time period t. """ - return (0, model.AmountPurchasedUB_FD[j,t]) - model.AmountPurchased_FD = Var(model.Streams, model.TimePeriods, - bounds=get_FD_bounds, doc='Amount purchased under Fixed Duration contract') + return (0, model.AmountPurchasedUB_FD[j, t]) + model.AmountPurchased_FD = Var( + model.Streams, + model.TimePeriods, + bounds=get_FD_bounds, + doc='Amount purchased under Fixed Duration contract', + ) # costs @@ -484,11 +635,17 @@ def get_CostUBs_FD(model, j, t): Returns ------- tuple - A tuple (0, upper_bound), where '0' is the lower bound, the minimal cost scenario, and 'upper_bound' is retrieved from 'model.CostUB_FD[j, t]', + A tuple (0, upper_bound), where '0' is the lower bound, the minimal cost scenario, and 'upper_bound' is retrieved from 'model.CostUB_FD[j, t]', 'model.CostUB_FD[j, t]' represents the highest permissible cost for purchasing material j under a fixed duration contract in time period t. """ - return (0, model.CostUB_FD[j,t]) - model.Cost_FD = Var(model.Streams, model.TimePeriods, bounds=get_CostUBs_FD, doc='Cost of variable length contract') + return (0, model.CostUB_FD[j, t]) + + model.Cost_FD = Var( + model.Streams, + model.TimePeriods, + bounds=get_CostUBs_FD, + doc='Cost of variable length contract', + ) # costpf(j, t) in GAMS # cost of fixed duration contract @@ -511,8 +668,14 @@ def get_CostUBs_FP(model, j, t): A tuple (0, upper_bound), where '0' is the lower bound and upper_bound is sourced from 'model.CostUB_FP[j, t]'. 'model.CostUB_FP[j, t]' denotes the highest permissible cost for purchasing material 'j' under a fixed price contract in time period 't' """ - return (0, model.CostUB_FP[j,t]) - model.Cost_FP = Var(model.Streams, model.TimePeriods, bounds=get_CostUBs_FP, doc='Cost of fixed price contract') + return (0, model.CostUB_FP[j, t]) + + model.Cost_FP = Var( + model.Streams, + model.TimePeriods, + bounds=get_CostUBs_FP, + doc='Cost of fixed price contract', + ) # costpd(j, t) in GAMS # cost of discount contract @@ -532,12 +695,17 @@ def get_CostUBs_Discount(model, j, t): Returns ------- tuple - A tuple (0, upper bound), where 0' represents the lower bound, indicating the lowest possible cost condition, and upper bound is the 'model.CostUB_Discount[j, t]'. + A tuple (0, upper bound), where 0' represents the lower bound, indicating the lowest possible cost condition, and upper bound is the 'model.CostUB_Discount[j, t]'. 'model.CostUB_Discount[j, t]' represents the maximum allowed cost for purchasing material j under a discount contract in the given time period t. """ - return (0, model.CostUB_Discount[j,t]) - model.Cost_Discount = Var(model.Streams, model.TimePeriods, - bounds=get_CostUBs_Discount, doc='Cost of discount contract') + return (0, model.CostUB_Discount[j, t]) + + model.Cost_Discount = Var( + model.Streams, + model.TimePeriods, + bounds=get_CostUBs_Discount, + doc='Cost of discount contract', + ) # costpb(j, t) in GAMS # cost of bulk contract @@ -560,17 +728,28 @@ def get_CostUBs_Bulk(model, j, t): A tuple with 0 as the lower bound and 'model.CostUB_Bulk[j, t]' as the upper bound. 'model.CostUB_Bulk[j, t]' represents the maximum cost for purchasing material j under a bulk contract in time period t. """ - return (0, model.CostUB_Bulk[j,t]) - model.Cost_Bulk = Var(model.Streams, model.TimePeriods, bounds=get_CostUBs_Bulk, doc='Cost of bulk contract') + return (0, model.CostUB_Bulk[j, t]) + model.Cost_Bulk = Var( + model.Streams, + model.TimePeriods, + bounds=get_CostUBs_Bulk, + doc='Cost of bulk contract', + ) # binary variables - model.BuyFPContract = RangeSet(0,1) # buy fixed price contract - model.BuyDiscountContract = Set(initialize=('BelowMin', 'AboveMin', 'NotSelected'), doc='Buy discount contract') - model.BuyBulkContract = Set(initialize=('BelowMin', 'AboveMin', 'NotSelected'), doc='Buy bulk contract') - model.BuyFDContract = Set(initialize=('1Month', '2Month', '3Month', 'NotSelected'), doc='Buy fixed duration contract') - + model.BuyFPContract = RangeSet(0, 1) # buy fixed price contract + model.BuyDiscountContract = Set( + initialize=('BelowMin', 'AboveMin', 'NotSelected'), doc='Buy discount contract' + ) + model.BuyBulkContract = Set( + initialize=('BelowMin', 'AboveMin', 'NotSelected'), doc='Buy bulk contract' + ) + model.BuyFDContract = Set( + initialize=('1Month', '2Month', '3Month', 'NotSelected'), + doc='Buy fixed duration contract', + ) ################ # CONSTRAINTS @@ -592,24 +771,55 @@ def profit_rule(model): Pyomo.Objective Objective function: maximize profit of the medium-term purchasing contracts problem. """ - salesIncome = sum(model.Prices[j,t] * model.FlowRate[j,t] - for j in model.Products for t in model.TimePeriods) - purchaseCost = sum(model.Cost_FD[j,t] - for j in model.RawMaterials for t in model.TimePeriods) + \ - sum(model.Cost_Discount[j,t] - for j in model.RawMaterials for t in model.TimePeriods) + \ - sum(model.Cost_Bulk[j,t] - for j in model.RawMaterials for t in model.TimePeriods) + \ - sum(model.Cost_FP[j,t] - for j in model.RawMaterials for t in model.TimePeriods) - productionCost = sum(model.OperatingCosts[i,t] * sum(model.FlowRate[j,t] - for j in model.Streams if model.MainProducts[j,i]) - for i in model.Processes for t in model.TimePeriods) - shortfallCost = sum(model.Shortfall[j,t] * model.ShortfallPenalty[j, t] - for j in model.Products for t in model.TimePeriods) - inventoryCost = sum(model.InventoryCost[j,t] * model.InventoryLevel[j,t] - for j in model.Products for t in model.TimePeriods) - return salesIncome - purchaseCost - productionCost - inventoryCost - shortfallCost + salesIncome = sum( + model.Prices[j, t] * model.FlowRate[j, t] + for j in model.Products + for t in model.TimePeriods + ) + purchaseCost = ( + sum( + model.Cost_FD[j, t] + for j in model.RawMaterials + for t in model.TimePeriods + ) + + sum( + model.Cost_Discount[j, t] + for j in model.RawMaterials + for t in model.TimePeriods + ) + + sum( + model.Cost_Bulk[j, t] + for j in model.RawMaterials + for t in model.TimePeriods + ) + + sum( + model.Cost_FP[j, t] + for j in model.RawMaterials + for t in model.TimePeriods + ) + ) + productionCost = sum( + model.OperatingCosts[i, t] + * sum( + model.FlowRate[j, t] for j in model.Streams if model.MainProducts[j, i] + ) + for i in model.Processes + for t in model.TimePeriods + ) + shortfallCost = sum( + model.Shortfall[j, t] * model.ShortfallPenalty[j, t] + for j in model.Products + for t in model.TimePeriods + ) + inventoryCost = sum( + model.InventoryCost[j, t] * model.InventoryLevel[j, t] + for j in model.Products + for t in model.TimePeriods + ) + return ( + salesIncome - purchaseCost - productionCost - inventoryCost - shortfallCost + ) + model.profit = Objective(rule=profit_rule, sense=maximize, doc='Maximize profit') # flow of raw materials is the total amount purchased (accross all contracts) @@ -631,11 +841,20 @@ def raw_material_flow_rule(model, j, t): Pyomo.Constraint An equality constraint ensuring the material flow balance for each raw material j in each time period t. """ - return model.FlowRate[j,t] == model.AmountPurchased_FD[j,t] + \ - model.AmountPurchased_FP[j,t] + model.AmountPurchased_Bulk[j,t] + \ - model.AmountPurchasedTotal_Discount[j,t] - model.raw_material_flow = Constraint(model.RawMaterials, model.TimePeriods, - rule=raw_material_flow_rule, doc='Material flow balance for each raw material j in each time period t') + return ( + model.FlowRate[j, t] + == model.AmountPurchased_FD[j, t] + + model.AmountPurchased_FP[j, t] + + model.AmountPurchased_Bulk[j, t] + + model.AmountPurchasedTotal_Discount[j, t] + ) + + model.raw_material_flow = Constraint( + model.RawMaterials, + model.TimePeriods, + rule=raw_material_flow_rule, + doc='Material flow balance for each raw material j in each time period t', + ) def discount_amount_total_rule(model, j, t): """ @@ -655,18 +874,25 @@ def discount_amount_total_rule(model, j, t): Pyomo.Constraint An equality constraint that ensures the total discounted purchase amount of material j in time period t is the sum of amounts bought below and above the discount threshold. """ - return model.AmountPurchasedTotal_Discount[j,t] == \ - model.AmountPurchasedBelowMin_Discount[j,t] + \ - model.AmountPurchasedAboveMin_Discount[j,t] - model.discount_amount_total_rule = Constraint(model.RawMaterials, model.TimePeriods, - rule=discount_amount_total_rule, doc='Total discounted purchase amount of material j in time period t is the sum of amounts bought below and above the discount threshold') + return ( + model.AmountPurchasedTotal_Discount[j, t] + == model.AmountPurchasedBelowMin_Discount[j, t] + + model.AmountPurchasedAboveMin_Discount[j, t] + ) + + model.discount_amount_total_rule = Constraint( + model.RawMaterials, + model.TimePeriods, + rule=discount_amount_total_rule, + doc='Total discounted purchase amount of material j in time period t is the sum of amounts bought below and above the discount threshold', + ) # mass balance equations for each node # these are specific to the process network in this example. def mass_balance_rule1(model, t): """ Represents the mass balance equation for the first node in the process network. - + Stream 1 is the inlet stream, and streams 2 and 3 are the outlet streams. The mass balance equation states that the total flow rate into the node (stream 1) is equal to the sum of the flow rates out of the node (streams 2 and 3). @@ -683,7 +909,12 @@ def mass_balance_rule1(model, t): A constraint that enforces the mass balance equation for the first node in the process network. """ return model.FlowRate[1, t] == model.FlowRate[2, t] + model.FlowRate[3, t] - model.mass_balance1 = Constraint(model.TimePeriods, rule=mass_balance_rule1, doc='Mass balance equation for the first node in the process network') + + model.mass_balance1 = Constraint( + model.TimePeriods, + rule=mass_balance_rule1, + doc='Mass balance equation for the first node in the process network', + ) def mass_balance_rule2(model, t): """ @@ -704,8 +935,13 @@ def mass_balance_rule2(model, t): Pyomo.Constraint A constraint that enforces the mass balance equation for the second node in the process network. """ - return model.FlowRate[5, t] == model.FlowRate[4, t] + model.FlowRate[8,t] - model.mass_balance2 = Constraint(model.TimePeriods, rule=mass_balance_rule2, doc='Mass balance equation for the second node in the process network') + return model.FlowRate[5, t] == model.FlowRate[4, t] + model.FlowRate[8, t] + + model.mass_balance2 = Constraint( + model.TimePeriods, + rule=mass_balance_rule2, + doc='Mass balance equation for the second node in the process network', + ) def mass_balance_rule3(model, t): """ @@ -727,7 +963,12 @@ def mass_balance_rule3(model, t): A constraint that enforces the mass balance equation for the third node in the process network. """ return model.FlowRate[6, t] == model.FlowRate[7, t] - model.mass_balance3 = Constraint(model.TimePeriods, rule=mass_balance_rule3, doc='Mass balance equation for the third node in the process network') + + model.mass_balance3 = Constraint( + model.TimePeriods, + rule=mass_balance_rule3, + doc='Mass balance equation for the third node in the process network', + ) def mass_balance_rule4(model, t): """ @@ -748,8 +989,13 @@ def mass_balance_rule4(model, t): Pyomo.Constraint A constraint that enforces the mass balance equation for Process 2 in the process network. """ - return model.FlowRate[3, t] == 10*model.FlowRate[5, t] - model.mass_balance4 = Constraint(model.TimePeriods, rule=mass_balance_rule4, doc='Mass balance equation for Process 2 in the process network') + return model.FlowRate[3, t] == 10 * model.FlowRate[5, t] + + model.mass_balance4 = Constraint( + model.TimePeriods, + rule=mass_balance_rule4, + doc='Mass balance equation for Process 2 in the process network', + ) # process input/output constraints # these are also totally specific to the process network @@ -772,7 +1018,12 @@ def process_balance_rule1(model, t): A constraint that enforces the input/output balance equation for Process 1 in the process network. """ return model.FlowRate[9, t] == model.ProcessConstants[1] * model.FlowRate[2, t] - model.process_balance1 = Constraint(model.TimePeriods, rule=process_balance_rule1, doc='Input/output balance equation for Process 1 in the process network') + + model.process_balance1 = Constraint( + model.TimePeriods, + rule=process_balance_rule1, + doc='Input/output balance equation for Process 1 in the process network', + ) def process_balance_rule2(model, t): """ @@ -792,9 +1043,15 @@ def process_balance_rule2(model, t): Pyomo.Constraint A constraint that enforces the input/output balance equation for Process 2 in the process network. """ - return model.FlowRate[10, t] == model.ProcessConstants[2] * \ - (model.FlowRate[5, t] + model.FlowRate[3, t]) - model.process_balance2 = Constraint(model.TimePeriods, rule=process_balance_rule2, doc='Input/output balance equation for Process 2 in the process network') + return model.FlowRate[10, t] == model.ProcessConstants[2] * ( + model.FlowRate[5, t] + model.FlowRate[3, t] + ) + + model.process_balance2 = Constraint( + model.TimePeriods, + rule=process_balance_rule2, + doc='Input/output balance equation for Process 2 in the process network', + ) def process_balance_rule3(model, t): """ @@ -815,9 +1072,16 @@ def process_balance_rule3(model, t): Pyomo.Constraint A constraint that enforces the input/output balance equation for Process 3 in the process network. """ - return model.FlowRate[8, t] == RandomConst_Line264 * \ - model.ProcessConstants[3] * model.FlowRate[7, t] - model.process_balance3 = Constraint(model.TimePeriods, rule=process_balance_rule3, doc='Input/output balance equation 1 for Process 3 in the process network') + return ( + model.FlowRate[8, t] + == RandomConst_Line264 * model.ProcessConstants[3] * model.FlowRate[7, t] + ) + + model.process_balance3 = Constraint( + model.TimePeriods, + rule=process_balance_rule3, + doc='Input/output balance equation 1 for Process 3 in the process network', + ) def process_balance_rule4(model, t): """ @@ -838,9 +1102,16 @@ def process_balance_rule4(model, t): Pyomo.Constraint A constraint that enforces the input/output balance equation for Process 3 in the process network. """ - return model.FlowRate[11, t] == RandomConst_Line265 * \ - model.ProcessConstants[3] * model.FlowRate[7, t] - model.process_balance4 = Constraint(model.TimePeriods, rule=process_balance_rule4, doc='Input/output balance equation 2 for Process 3 in the process network') + return ( + model.FlowRate[11, t] + == RandomConst_Line265 * model.ProcessConstants[3] * model.FlowRate[7, t] + ) + + model.process_balance4 = Constraint( + model.TimePeriods, + rule=process_balance_rule4, + doc='Input/output balance equation 2 for Process 3 in the process network', + ) # process capacity contraints # these are hardcoded based on the three processes and the process flow structure @@ -863,7 +1134,12 @@ def process_capacity_rule1(model, t): A constraint that enforces the capacity constraint for Process 1 in the process network. """ return model.FlowRate[9, t] <= model.Capacity[1] - model.process_capacity1 = Constraint(model.TimePeriods, rule=process_capacity_rule1, doc='Capacity constraint for Process 1 in the process network') + + model.process_capacity1 = Constraint( + model.TimePeriods, + rule=process_capacity_rule1, + doc='Capacity constraint for Process 1 in the process network', + ) def process_capacity_rule2(model, t): """ @@ -884,7 +1160,12 @@ def process_capacity_rule2(model, t): A constraint that enforces the capacity constraint for Process 2 in the process network. """ return model.FlowRate[10, t] <= model.Capacity[2] - model.process_capacity2 = Constraint(model.TimePeriods, rule=process_capacity_rule2, doc='Capacity constraint for Process 2 in the process network') + + model.process_capacity2 = Constraint( + model.TimePeriods, + rule=process_capacity_rule2, + doc='Capacity constraint for Process 2 in the process network', + ) def process_capacity_rule3(model, t): """ @@ -905,7 +1186,12 @@ def process_capacity_rule3(model, t): A constraint that enforces the capacity constraint for Process 3 in the process network. """ return model.FlowRate[11, t] + model.FlowRate[8, t] <= model.Capacity[3] - model.process_capacity3 = Constraint(model.TimePeriods, rule=process_capacity_rule3, doc='Capacity constraint for Process 3 in the process network') + + model.process_capacity3 = Constraint( + model.TimePeriods, + rule=process_capacity_rule3, + doc='Capacity constraint for Process 3 in the process network', + ) # Inventory balance of final products # again, these are hardcoded. @@ -914,7 +1200,7 @@ def inventory_balance1(model, t): """ Maintains inventory balance for the material associated with stream 12 at the first inventory node across time periods. - This constraint ensures that the inventory level of the material at the beginning of each time period t, combined with the incoming flow from stream 9, equals the sum of the outflow to the next process (or demand) represented by stream 12 and the inventory level at the end of the time period. For the initial time period, the previous inventory is assumed to be zero. + This constraint ensures that the inventory level of the material at the beginning of each time period t, combined with the incoming flow from stream 9, equals the sum of the outflow to the next process (or demand) represented by stream 12 and the inventory level at the end of the time period. For the initial time period, the previous inventory is assumed to be zero. This balance is vital for tracking inventory levels accurately, allowing the model to make informed decisions about production, storage, and sales to maximize overall profit. Parameters @@ -929,15 +1215,23 @@ def inventory_balance1(model, t): Pyomo.Constraint A constraint enforcing the balance of inventory levels for the material flowing through stream 12 at the first inventory node, taking into account the material inflows and outflows as well as changes in inventory from the previous to the current time period. """ - prev = 0 if t == min(model.TimePeriods) else model.InventoryLevel[12, t-1] - return prev + model.FlowRate[9, t] == model.FlowRate[12, t] + model.InventoryLevel[12,t] - model.inventory_balance1 = Constraint(model.TimePeriods, rule=inventory_balance1, doc='Inventory balance for material associated with stream 12 at the first inventory node') + prev = 0 if t == min(model.TimePeriods) else model.InventoryLevel[12, t - 1] + return ( + prev + model.FlowRate[9, t] + == model.FlowRate[12, t] + model.InventoryLevel[12, t] + ) + + model.inventory_balance1 = Constraint( + model.TimePeriods, + rule=inventory_balance1, + doc='Inventory balance for material associated with stream 12 at the first inventory node', + ) def inventory_balance_rule2(model, t): """ Ensures inventory balance for the material associated with stream 13 at the second inventory node for the first time period. - This constraint is applied only to the first time period (t=1) and ensures that the sum of incoming flows from streams 10 and 11 equals the sum of the outflow to the next process represented by stream 13 and the inventory level at the end of the period. + This constraint is applied only to the first time period (t=1) and ensures that the sum of incoming flows from streams 10 and 11 equals the sum of the outflow to the next process represented by stream 13 and the inventory level at the end of the period. For periods beyond the first, this constraint is skipped, as the balance for these periods may be governed by other conditions or constraints within the model. This selective application is crucial for accurately modeling the startup phase of the inventory system, where initial conditions significantly impact subsequent operations. Parameters @@ -950,21 +1244,28 @@ def inventory_balance_rule2(model, t): Returns ------- Pyomo.Constraint or Constraint.Skip - A constraint enforcing the inventory balance for the material flowing through stream 13 at the second inventory node during the first time period. + A constraint enforcing the inventory balance for the material flowing through stream 13 at the second inventory node during the first time period. For all other periods, the function returns `Constraint.Skip`, indicating no constraint is applied. """ if t != 1: return Constraint.Skip - return model.FlowRate[10, t] + model.FlowRate[11, t] == \ - model.InventoryLevel[13,t] + model.FlowRate[13, t] - model.inventory_balance2 = Constraint(model.TimePeriods, rule=inventory_balance_rule2, doc='Inventory balance for material associated with stream 13 at the second inventory node') + return ( + model.FlowRate[10, t] + model.FlowRate[11, t] + == model.InventoryLevel[13, t] + model.FlowRate[13, t] + ) + + model.inventory_balance2 = Constraint( + model.TimePeriods, + rule=inventory_balance_rule2, + doc='Inventory balance for material associated with stream 13 at the second inventory node', + ) def inventory_balance_rule3(model, t): """ Maintains the inventory balance for material associated with stream 13 at the second inventory node for all time periods after the first. - This constraint is crucial for modeling the dynamic behavior of inventory levels over time, ensuring that the sum of the previous period's inventory level and the current period's inflows from streams 10 and 11 equals the current period's outflow (through stream 13) and ending inventory level. - It reflects the principle of inventory continuity, accounting for inflows, outflows, and storage from one period to the next. + This constraint is crucial for modeling the dynamic behavior of inventory levels over time, ensuring that the sum of the previous period's inventory level and the current period's inflows from streams 10 and 11 equals the current period's outflow (through stream 13) and ending inventory level. + It reflects the principle of inventory continuity, accounting for inflows, outflows, and storage from one period to the next. The constraint is skipped for the first period (t=1) to accommodate initial conditions or startup behaviors specific to the model's context. Parameters @@ -977,14 +1278,23 @@ def inventory_balance_rule3(model, t): Returns ------- Pyomo.Constraint or Constraint.Skip - A constraint enforcing the inventory balance for the material flowing through stream 13 at the second inventory node from the second period onwards. + A constraint enforcing the inventory balance for the material flowing through stream 13 at the second inventory node from the second period onwards. For the first period, the function returns `Constraint.Skip`, indicating the constraint does not apply. """ if t <= 1: return Constraint.Skip - return model.InventoryLevel[13, t-1] + model.FlowRate[10, t] + \ - model.FlowRate[11,t] == model.InventoryLevel[13, t] + model.FlowRate[13, t] - model.inventory_balance3 = Constraint(model.TimePeriods, rule=inventory_balance_rule3, doc='Inventory balance for material associated with stream 13 at the second inventory node') + return ( + model.InventoryLevel[13, t - 1] + + model.FlowRate[10, t] + + model.FlowRate[11, t] + == model.InventoryLevel[13, t] + model.FlowRate[13, t] + ) + + model.inventory_balance3 = Constraint( + model.TimePeriods, + rule=inventory_balance_rule3, + doc='Inventory balance for material associated with stream 13 at the second inventory node', + ) # Max capacities of inventories def inventory_capacity_rule(model, j, t): @@ -1003,11 +1313,17 @@ def inventory_capacity_rule(model, j, t): Returns ------- Pyomo.Constraint - A constraint that sets the maximum permissible inventory level for material j in time period t, ensuring that the inventory does not exceed the predefined upper bound 'InventoryLevelUB[j, t]'. + A constraint that sets the maximum permissible inventory level for material j in time period t, ensuring that the inventory does not exceed the predefined upper bound 'InventoryLevelUB[j, t]'. This maintains the model's alignment with practical storage limitations. """ - return model.InventoryLevel[j,t] <= model.InventoryLevelUB[j,t] - model.inventory_capacity_rule = Constraint(model.Products, model.TimePeriods, rule=inventory_capacity_rule, doc='Maximum inventory capacity for each material j at each time period t') + return model.InventoryLevel[j, t] <= model.InventoryLevelUB[j, t] + + model.inventory_capacity_rule = Constraint( + model.Products, + model.TimePeriods, + rule=inventory_capacity_rule, + doc='Maximum inventory capacity for each material j at each time period t', + ) # Shortfall calculation def shortfall_rule(model, j, t): @@ -1026,11 +1342,20 @@ def shortfall_rule(model, j, t): Returns ------- Pyomo.Constraint - A constraint defining the shortfall for product j during time period t as the difference between the supply or demand upper bound and the actual flow rate. + A constraint defining the shortfall for product j during time period t as the difference between the supply or demand upper bound and the actual flow rate. This calculation is pivotal for evaluating performance and identifying bottlenecks or excess capacities within the supply chain. """ - return model.Shortfall[j, t] == model.SupplyAndDemandUBs[j, t] - model.FlowRate[j,t] - model.shortfall = Constraint(model.Products, model.TimePeriods, rule=shortfall_rule, doc='Shortfall calculation for each product j in each time period t') + return ( + model.Shortfall[j, t] + == model.SupplyAndDemandUBs[j, t] - model.FlowRate[j, t] + ) + + model.shortfall = Constraint( + model.Products, + model.TimePeriods, + rule=shortfall_rule, + doc='Shortfall calculation for each product j in each time period t', + ) # maximum shortfall allowed def shortfall_max_rule(model, j, t): @@ -1049,11 +1374,17 @@ def shortfall_max_rule(model, j, t): Returns ------- Pyomo.Constraint - A constraint that limits the shortfall for product j during time period t to a maximum value specified by 'ShortfallUB[j, t]'. + A constraint that limits the shortfall for product j during time period t to a maximum value specified by 'ShortfallUB[j, t]'. This constraint is instrumental in aligning the model's solutions with real-world operational constraints and strategic objectives. """ return model.Shortfall[j, t] <= model.ShortfallUB[j, t] - model.shortfall_max = Constraint(model.Products, model.TimePeriods, rule=shortfall_max_rule, doc='Maximum shortfall allowed for each product j in each time period t') + + model.shortfall_max = Constraint( + model.Products, + model.TimePeriods, + rule=shortfall_max_rule, + doc='Maximum shortfall allowed for each product j in each time period t', + ) # maxiumum capacities of suppliers def supplier_capacity_rule(model, j, t): @@ -1072,11 +1403,17 @@ def supplier_capacity_rule(model, j, t): Returns ------- Pyomo.Constraint - A constraint that limits the flow rate of raw material j from suppliers in time period t to not exceed the predefined upper bound 'SupplyAndDemandUBs[j, t]'. + A constraint that limits the flow rate of raw material j from suppliers in time period t to not exceed the predefined upper bound 'SupplyAndDemandUBs[j, t]'. This constraint is crucial for ensuring the feasibility of the supply chain model and its alignment with practical supply capabilities. """ return model.FlowRate[j, t] <= model.SupplyAndDemandUBs[j, t] - model.supplier_capacity = Constraint(model.RawMaterials, model.TimePeriods, rule=supplier_capacity_rule, doc='Maximum supply capacity for each raw material j in each time period t') + + model.supplier_capacity = Constraint( + model.RawMaterials, + model.TimePeriods, + rule=supplier_capacity_rule, + doc='Maximum supply capacity for each raw material j in each time period t', + ) # demand upper bound def demand_UB_rule(model, j, t): @@ -1097,8 +1434,14 @@ def demand_UB_rule(model, j, t): Pyomo.Constraint A constraint limiting the flow rate of product j to not exceed the predefined maximum demand 'SupplyAndDemandUBs[j, t]' in time period t, ensuring production is demand-driven. """ - return model.FlowRate[j, t] <= model.SupplyAndDemandUBs[j,t] - model.demand_UB = Constraint(model.Products, model.TimePeriods, rule=demand_UB_rule, doc='Maximum demand allowed for each product j in each time period t') + return model.FlowRate[j, t] <= model.SupplyAndDemandUBs[j, t] + + model.demand_UB = Constraint( + model.Products, + model.TimePeriods, + rule=demand_UB_rule, + doc='Maximum demand allowed for each product j in each time period t', + ) # demand lower bound def demand_LB_rule(model, j, t): @@ -1119,9 +1462,14 @@ def demand_LB_rule(model, j, t): Pyomo.Constraint A constraint ensuring that the flow rate of product j meets or exceeds the minimum demand 'DemandLB[j, t]' in time period t, supporting effective market engagement. """ - return model.FlowRate[j, t] >= model.DemandLB[j,t] - model.demand_LB = Constraint(model.Products, model.TimePeriods, rule=demand_LB_rule, doc='Minimum demand required for each product j in each time period t') + return model.FlowRate[j, t] >= model.DemandLB[j, t] + model.demand_LB = Constraint( + model.Products, + model.TimePeriods, + rule=demand_LB_rule, + doc='Minimum demand required for each product j in each time period t', + ) # FIXED PRICE CONTRACT @@ -1130,10 +1478,10 @@ def FP_contract_disjunct_rule(disjunct, j, t, buy): """ Defines disjunctive constraints for procurement decisions under a Fixed Price (FP) contract for material j in time period t. - A decision must be made whether to engage in purchasing under the contract terms or not for each material in each time period. - This function encapsulates the disjunctive nature of this decision: if the decision is to buy ('buy' parameter is True), - the amount purchased under the FP contract is limited by a predefined maximum ('MAX_AMOUNT_FP'); - otherwise, no purchase is made under the FP contract for that material and period. + A decision must be made whether to engage in purchasing under the contract terms or not for each material in each time period. + This function encapsulates the disjunctive nature of this decision: if the decision is to buy ('buy' parameter is True), + the amount purchased under the FP contract is limited by a predefined maximum ('MAX_AMOUNT_FP'); + otherwise, no purchase is made under the FP contract for that material and period. This disjunctive approach allows for modeling complex decision-making processes in procurement strategies. Parameters @@ -1153,11 +1501,19 @@ def FP_contract_disjunct_rule(disjunct, j, t, buy): """ model = disjunct.model() if buy: - disjunct.c = Constraint(expr=model.AmountPurchased_FP[j,t] <= MAX_AMOUNT_FP) + disjunct.c = Constraint( + expr=model.AmountPurchased_FP[j, t] <= MAX_AMOUNT_FP + ) else: - disjunct.c = Constraint(expr=model.AmountPurchased_FP[j,t] == 0) - model.FP_contract_disjunct = Disjunct(model.RawMaterials, model.TimePeriods, - model.BuyFPContract, rule=FP_contract_disjunct_rule, doc='Disjunctive constraints for Fixed Price contract buying options') + disjunct.c = Constraint(expr=model.AmountPurchased_FP[j, t] == 0) + + model.FP_contract_disjunct = Disjunct( + model.RawMaterials, + model.TimePeriods, + model.BuyFPContract, + rule=FP_contract_disjunct_rule, + doc='Disjunctive constraints for Fixed Price contract buying options', + ) # Fixed price disjunction def FP_contract_rule(model, j, t): @@ -1180,9 +1536,14 @@ def FP_contract_rule(model, j, t): Pyomo.Disjunction A disjunction that represents the decision-making point for FP contract purchases, contributing to the model's overall procurement strategy. """ - return [model.FP_contract_disjunct[j,t,buy] for buy in model.BuyFPContract] - model.FP_disjunction = Disjunction(model.RawMaterials, model.TimePeriods, - rule=FP_contract_rule, doc='Disjunction for Fixed Price contract buying options') + return [model.FP_contract_disjunct[j, t, buy] for buy in model.BuyFPContract] + + model.FP_disjunction = Disjunction( + model.RawMaterials, + model.TimePeriods, + rule=FP_contract_rule, + doc='Disjunction for Fixed Price contract buying options', + ) # cost constraint for fixed price contract (independent constraint) def FP_contract_cost_rule(model, j, t): @@ -1202,20 +1563,25 @@ def FP_contract_cost_rule(model, j, t): Pyomo.Constraint A constraint equating the FP contract cost for material j in period t to the product of the purchased amount and its price, ensuring accurate financial accounting in the model. """ - return model.Cost_FP[j,t] == model.AmountPurchased_FP[j,t] * \ - model.Prices[j,t] - model.FP_contract_cost = Constraint(model.RawMaterials, model.TimePeriods, - rule=FP_contract_cost_rule, doc='Cost constraint for Fixed Price contract') + return ( + model.Cost_FP[j, t] == model.AmountPurchased_FP[j, t] * model.Prices[j, t] + ) + model.FP_contract_cost = Constraint( + model.RawMaterials, + model.TimePeriods, + rule=FP_contract_cost_rule, + doc='Cost constraint for Fixed Price contract', + ) # DISCOUNT CONTRACT # Disjunction for Discount contract def discount_contract_disjunct_rule(disjunct, j, t, buy): """ - Sets rules for purchasing materials j in time t under discount contracts based on the buying decision 'buy'. + Sets rules for purchasing materials j in time t under discount contracts based on the buying decision 'buy'. - For discount contracts, the decision involves purchasing below or above a minimum amount for a discount, or not selecting the contract at all. + For discount contracts, the decision involves purchasing below or above a minimum amount for a discount, or not selecting the contract at all. This rule reflects these choices by adjusting purchasing amounts and enforcing corresponding constraints. Parameters @@ -1227,30 +1593,42 @@ def discount_contract_disjunct_rule(disjunct, j, t, buy): Index of time period. buy : str Decision on purchasing strategy: 'BelowMin', 'AboveMin', or 'NotSelected'. - + """ model = disjunct.model() if buy == 'BelowMin': disjunct.belowMin = Constraint( - expr=model.AmountPurchasedBelowMin_Discount[j,t] <= \ - model.MinAmount_Discount[j,t]) + expr=model.AmountPurchasedBelowMin_Discount[j, t] + <= model.MinAmount_Discount[j, t] + ) disjunct.aboveMin = Constraint( - expr=model.AmountPurchasedAboveMin_Discount[j,t] == 0) + expr=model.AmountPurchasedAboveMin_Discount[j, t] == 0 + ) elif buy == 'AboveMin': disjunct.belowMin = Constraint( - expr=model.AmountPurchasedBelowMin_Discount[j,t] == \ - model.MinAmount_Discount[j,t]) + expr=model.AmountPurchasedBelowMin_Discount[j, t] + == model.MinAmount_Discount[j, t] + ) disjunct.aboveMin = Constraint( - expr=model.AmountPurchasedAboveMin_Discount[j,t] >= 0) + expr=model.AmountPurchasedAboveMin_Discount[j, t] >= 0 + ) elif buy == 'NotSelected': disjunct.belowMin = Constraint( - expr=model.AmountPurchasedBelowMin_Discount[j,t] == 0) + expr=model.AmountPurchasedBelowMin_Discount[j, t] == 0 + ) disjunct.aboveMin = Constraint( - expr=model.AmountPurchasedAboveMin_Discount[j,t] == 0) + expr=model.AmountPurchasedAboveMin_Discount[j, t] == 0 + ) else: raise RuntimeError("Unrecognized choice for discount contract: %s" % buy) - model.discount_contract_disjunct = Disjunct(model.RawMaterials, model.TimePeriods, - model.BuyDiscountContract, rule=discount_contract_disjunct_rule, doc='Disjunctive constraints for Discount contract buying options') + + model.discount_contract_disjunct = Disjunct( + model.RawMaterials, + model.TimePeriods, + model.BuyDiscountContract, + rule=discount_contract_disjunct_rule, + doc='Disjunctive constraints for Discount contract buying options', + ) # Discount contract disjunction def discount_contract_rule(model, j, t): @@ -1273,10 +1651,17 @@ def discount_contract_rule(model, j, t): Pyomo.Disjunction The disjunction representing the choice among discount contract purchasing strategies. """ - return [model.discount_contract_disjunct[j,t,buy] \ - for buy in model.BuyDiscountContract] - model.discount_contract = Disjunction(model.RawMaterials, model.TimePeriods, - rule=discount_contract_rule, doc='Disjunction for Discount contract buying options') + return [ + model.discount_contract_disjunct[j, t, buy] + for buy in model.BuyDiscountContract + ] + + model.discount_contract = Disjunction( + model.RawMaterials, + model.TimePeriods, + rule=discount_contract_rule, + doc='Disjunction for Discount contract buying options', + ) # cost constraint for discount contract (independent constraint) def discount_cost_rule(model, j, t): @@ -1299,12 +1684,20 @@ def discount_cost_rule(model, j, t): Pyomo.Constraint The constraint that calculates the total cost of purchases under discount contracts. """ - return model.Cost_Discount[j,t] == model.RegPrice_Discount[j,t] * \ - model.AmountPurchasedBelowMin_Discount[j,t] + \ - model.DiscountPrice_Discount[j,t] * model.AmountPurchasedAboveMin_Discount[j,t] - model.discount_cost = Constraint(model.RawMaterials, model.TimePeriods, - rule=discount_cost_rule, doc='Cost constraint for Discount contract') - + return ( + model.Cost_Discount[j, t] + == model.RegPrice_Discount[j, t] + * model.AmountPurchasedBelowMin_Discount[j, t] + + model.DiscountPrice_Discount[j, t] + * model.AmountPurchasedAboveMin_Discount[j, t] + ) + + model.discount_cost = Constraint( + model.RawMaterials, + model.TimePeriods, + rule=discount_cost_rule, + doc='Cost constraint for Discount contract', + ) # BULK CONTRACT @@ -1313,7 +1706,7 @@ def bulk_contract_disjunct_rule(disjunct, j, t, buy): """ Defines conditions for bulk purchases of material j at time t based on the decision 'buy'. - This rule determines how much of a material is bought under a bulk contract and at what price, based on whether purchases are below or above a specified minimum amount, or if the bulk option is not selected. + This rule determines how much of a material is bought under a bulk contract and at what price, based on whether purchases are below or above a specified minimum amount, or if the bulk option is not selected. It enforces different constraints for the amount and cost of materials under these scenarios. Parameters @@ -1331,30 +1724,40 @@ def bulk_contract_disjunct_rule(disjunct, j, t, buy): model = disjunct.model() if buy == 'BelowMin': disjunct.amount = Constraint( - expr=model.AmountPurchased_Bulk[j,t] <= model.MinAmount_Bulk[j,t]) + expr=model.AmountPurchased_Bulk[j, t] <= model.MinAmount_Bulk[j, t] + ) disjunct.price = Constraint( - expr=model.Cost_Bulk[j,t] == model.RegPrice_Bulk[j,t] * \ - model.AmountPurchased_Bulk[j,t]) + expr=model.Cost_Bulk[j, t] + == model.RegPrice_Bulk[j, t] * model.AmountPurchased_Bulk[j, t] + ) elif buy == 'AboveMin': disjunct.amount = Constraint( - expr=model.AmountPurchased_Bulk[j,t] >= model.MinAmount_Bulk[j,t]) + expr=model.AmountPurchased_Bulk[j, t] >= model.MinAmount_Bulk[j, t] + ) disjunct.price = Constraint( - expr=model.Cost_Bulk[j,t] == model.DiscountPrice_Bulk[j,t] * \ - model.AmountPurchased_Bulk[j,t]) + expr=model.Cost_Bulk[j, t] + == model.DiscountPrice_Bulk[j, t] * model.AmountPurchased_Bulk[j, t] + ) elif buy == 'NotSelected': - disjunct.amount = Constraint(expr=model.AmountPurchased_Bulk[j,t] == 0) - disjunct.price = Constraint(expr=model.Cost_Bulk[j,t] == 0) + disjunct.amount = Constraint(expr=model.AmountPurchased_Bulk[j, t] == 0) + disjunct.price = Constraint(expr=model.Cost_Bulk[j, t] == 0) else: raise RuntimeError("Unrecognized choice for bulk contract: %s" % buy) - model.bulk_contract_disjunct = Disjunct(model.RawMaterials, model.TimePeriods, - model.BuyBulkContract, rule=bulk_contract_disjunct_rule, doc='Disjunctive constraints for Bulk contract buying options') + + model.bulk_contract_disjunct = Disjunct( + model.RawMaterials, + model.TimePeriods, + model.BuyBulkContract, + rule=bulk_contract_disjunct_rule, + doc='Disjunctive constraints for Bulk contract buying options', + ) # Bulk contract disjunction def bulk_contract_rule(model, j, t): """ Establishes a decision-making framework for bulk purchases, allowing the model to choose among predefined scenarios. - This function sets up a flexible structure for deciding on bulk purchases. + This function sets up a flexible structure for deciding on bulk purchases. Each material and time period can be evaluated independently, allowing the model to adapt to various conditions and optimize procurement strategies under bulk contracts. Parameters @@ -1371,16 +1774,22 @@ def bulk_contract_rule(model, j, t): Pyomo.Disjunction A set of disjunctive conditions that the model can choose from when making bulk purchasing decisions. """ - return [model.bulk_contract_disjunct[j,t,buy] for buy in model.BuyBulkContract] - model.bulk_contract = Disjunction(model.RawMaterials, model.TimePeriods, - rule=bulk_contract_rule, doc='Disjunction for Bulk contract buying options') + return [ + model.bulk_contract_disjunct[j, t, buy] for buy in model.BuyBulkContract + ] + model.bulk_contract = Disjunction( + model.RawMaterials, + model.TimePeriods, + rule=bulk_contract_rule, + doc='Disjunction for Bulk contract buying options', + ) # FIXED DURATION CONTRACT def FD_1mo_contract(disjunct, j, t): """ - Defines the constraints for engaging in a 1-month fixed duration contract for material j at time t. + Defines the constraints for engaging in a 1-month fixed duration contract for material j at time t. This includes a minimum purchase amount and the cost calculation based on contract-specific prices. Parameters @@ -1393,16 +1802,24 @@ def FD_1mo_contract(disjunct, j, t): Index of time period. """ model = disjunct.model() - disjunct.amount1 = Constraint(expr=model.AmountPurchased_FD[j,t] >= \ - MIN_AMOUNT_FD_1MONTH) - disjunct.price1 = Constraint(expr=model.Cost_FD[j,t] == \ - model.Prices_Length[j,1,t] * model.AmountPurchased_FD[j,t]) + disjunct.amount1 = Constraint( + expr=model.AmountPurchased_FD[j, t] >= MIN_AMOUNT_FD_1MONTH + ) + disjunct.price1 = Constraint( + expr=model.Cost_FD[j, t] + == model.Prices_Length[j, 1, t] * model.AmountPurchased_FD[j, t] + ) + model.FD_1mo_contract = Disjunct( - model.RawMaterials, model.TimePeriods, rule=FD_1mo_contract, doc='1-month fixed duration contract') + model.RawMaterials, + model.TimePeriods, + rule=FD_1mo_contract, + doc='1-month fixed duration contract', + ) def FD_2mo_contract(disjunct, j, t): """ - Establishes conditions for a 2-month fixed duration contract. + Establishes conditions for a 2-month fixed duration contract. This involves a minimum purchase requirement for two consecutive periods and corresponding cost calculations. Parameters @@ -1415,18 +1832,29 @@ def FD_2mo_contract(disjunct, j, t): Index of time period. """ model = disjunct.model() - disjunct.amount1 = Constraint(expr=model.AmountPurchased_FD[j,t] >= \ - model.MinAmount_Length[j,2]) - disjunct.price1 = Constraint(expr=model.Cost_FD[j,t] == \ - model.Prices_Length[j,2,t] * model.AmountPurchased_FD[j,t]) - # only enforce these if we aren't in the last time period + disjunct.amount1 = Constraint( + expr=model.AmountPurchased_FD[j, t] >= model.MinAmount_Length[j, 2] + ) + disjunct.price1 = Constraint( + expr=model.Cost_FD[j, t] + == model.Prices_Length[j, 2, t] * model.AmountPurchased_FD[j, t] + ) + # only enforce these if we aren't in the last time period if t < model.TimePeriods[-1]: - disjunct.amount2 = Constraint(expr=model.AmountPurchased_FD[j, t+1] >= \ - model.MinAmount_Length[j,2]) - disjunct.price2 = Constraint(expr=model.Cost_FD[j,t+1] == \ - model.Prices_Length[j,2,t] * model.AmountPurchased_FD[j, t+1]) + disjunct.amount2 = Constraint( + expr=model.AmountPurchased_FD[j, t + 1] >= model.MinAmount_Length[j, 2] + ) + disjunct.price2 = Constraint( + expr=model.Cost_FD[j, t + 1] + == model.Prices_Length[j, 2, t] * model.AmountPurchased_FD[j, t + 1] + ) + model.FD_2mo_contract = Disjunct( - model.RawMaterials, model.TimePeriods, rule=FD_2mo_contract, doc='2-month fixed duration contract') + model.RawMaterials, + model.TimePeriods, + rule=FD_2mo_contract, + doc='2-month fixed duration contract', + ) def FD_3mo_contract(disjunct, j, t): """ @@ -1444,28 +1872,42 @@ def FD_3mo_contract(disjunct, j, t): model = disjunct.model() # NOTE: I think there is a mistake in the GAMS file in line 327. # they use the bulk minamount rather than the length one. - #I am doing the same here for validation purposes. - disjunct.amount1 = Constraint(expr=model.AmountPurchased_FD[j,t] >= \ - model.MinAmount_Bulk[j,3]) - disjunct.cost1 = Constraint(expr=model.Cost_FD[j,t] == \ - model.Prices_Length[j,3,t] * model.AmountPurchased_FD[j,t]) + # I am doing the same here for validation purposes. + disjunct.amount1 = Constraint( + expr=model.AmountPurchased_FD[j, t] >= model.MinAmount_Bulk[j, 3] + ) + disjunct.cost1 = Constraint( + expr=model.Cost_FD[j, t] + == model.Prices_Length[j, 3, t] * model.AmountPurchased_FD[j, t] + ) # check we aren't in one of the last two time periods if t < model.TimePeriods[-1]: - disjunct.amount2 = Constraint(expr=model.AmountPurchased_FD[j,t+1] >= \ - model.MinAmount_Length[j,3]) - disjunct.cost2 = Constraint(expr=model.Cost_FD[j,t+1] == \ - model.Prices_Length[j,3,t] * model.AmountPurchased_FD[j,t+1]) + disjunct.amount2 = Constraint( + expr=model.AmountPurchased_FD[j, t + 1] >= model.MinAmount_Length[j, 3] + ) + disjunct.cost2 = Constraint( + expr=model.Cost_FD[j, t + 1] + == model.Prices_Length[j, 3, t] * model.AmountPurchased_FD[j, t + 1] + ) if t < model.TimePeriods[-2]: - disjunct.amount3 = Constraint(expr=model.AmountPurchased_FD[j,t+2] >= \ - model.MinAmount_Length[j,3]) - disjunct.cost3 = Constraint(expr=model.Cost_FD[j,t+2] == \ - model.Prices_Length[j,3,t] * model.AmountPurchased_FD[j,t+2]) + disjunct.amount3 = Constraint( + expr=model.AmountPurchased_FD[j, t + 2] >= model.MinAmount_Length[j, 3] + ) + disjunct.cost3 = Constraint( + expr=model.Cost_FD[j, t + 2] + == model.Prices_Length[j, 3, t] * model.AmountPurchased_FD[j, t + 2] + ) + model.FD_3mo_contract = Disjunct( - model.RawMaterials, model.TimePeriods, rule=FD_3mo_contract, doc='3-month fixed duration contract') + model.RawMaterials, + model.TimePeriods, + rule=FD_3mo_contract, + doc='3-month fixed duration contract', + ) def FD_no_contract(disjunct, j, t): """ - Represents the scenario where no fixed duration contract is selected for material j at time t. + Represents the scenario where no fixed duration contract is selected for material j at time t. Ensures no purchases or costs are accounted for under FD contracts. Parameters @@ -1478,16 +1920,21 @@ def FD_no_contract(disjunct, j, t): Index of time period. """ model = disjunct.model() - disjunct.amount1 = Constraint(expr=model.AmountPurchased_FD[j,t] == 0) - disjunct.cost1 = Constraint(expr=model.Cost_FD[j,t] == 0) + disjunct.amount1 = Constraint(expr=model.AmountPurchased_FD[j, t] == 0) + disjunct.cost1 = Constraint(expr=model.Cost_FD[j, t] == 0) if t < model.TimePeriods[-1]: - disjunct.amount2 = Constraint(expr=model.AmountPurchased_FD[j,t+1] == 0) - disjunct.cost2 = Constraint(expr=model.Cost_FD[j,t+1] == 0) + disjunct.amount2 = Constraint(expr=model.AmountPurchased_FD[j, t + 1] == 0) + disjunct.cost2 = Constraint(expr=model.Cost_FD[j, t + 1] == 0) if t < model.TimePeriods[-2]: - disjunct.amount3 = Constraint(expr=model.AmountPurchased_FD[j,t+2] == 0) - disjunct.cost3 = Constraint(expr=model.Cost_FD[j,t+2] == 0) + disjunct.amount3 = Constraint(expr=model.AmountPurchased_FD[j, t + 2] == 0) + disjunct.cost3 = Constraint(expr=model.Cost_FD[j, t + 2] == 0) + model.FD_no_contract = Disjunct( - model.RawMaterials, model.TimePeriods, rule=FD_no_contract, doc='No fixed duration contract') + model.RawMaterials, + model.TimePeriods, + rule=FD_no_contract, + doc='No fixed duration contract', + ) def FD_contract(model, j, t): """ @@ -1507,10 +1954,19 @@ def FD_contract(model, j, t): Pyomo.Disjunction The disjunctive decision structure for FD contracts. """ - return [ model.FD_1mo_contract[j,t], model.FD_2mo_contract[j,t], - model.FD_3mo_contract[j,t], model.FD_no_contract[j,t], ] - model.FD_contract = Disjunction(model.RawMaterials, model.TimePeriods, - rule=FD_contract, doc='Fixed duration contract scenarios') + return [ + model.FD_1mo_contract[j, t], + model.FD_2mo_contract[j, t], + model.FD_3mo_contract[j, t], + model.FD_no_contract[j, t], + ] + + model.FD_contract = Disjunction( + model.RawMaterials, + model.TimePeriods, + rule=FD_contract, + doc='Fixed duration contract scenarios', + ) return model @@ -1524,11 +1980,15 @@ def build_concrete(): Pyomo.ConcreteModel A concrete model for the medium-term purchasing contracts problem. """ - return build_model().create_instance(join(this_file_dir(), 'med_term_purchasing.dat')) + return build_model().create_instance( + join(this_file_dir(), 'med_term_purchasing.dat') + ) if __name__ == "__main__": m = build_concrete() TransformationFactory('gdp.bigm').apply_to(m) - SolverFactory('gams').solve(m, solver='baron', tee=True, add_options=['option optcr=1e-6;']) + SolverFactory('gams').solve( + m, solver='baron', tee=True, add_options=['option optcr=1e-6;'] + ) m.profit.display() From d93a4663b679cc1437f2719ed90bf422605dfaf6 Mon Sep 17 00:00:00 2001 From: Albert Lee Date: Tue, 23 Apr 2024 12:19:54 -0400 Subject: [PATCH 55/99] black formatting --- gdplib/pyomo_examples/med_term_purchasing.py | 28 ++++++++------------ 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/gdplib/pyomo_examples/med_term_purchasing.py b/gdplib/pyomo_examples/med_term_purchasing.py index 4e05b5f..c2a0bab 100644 --- a/gdplib/pyomo_examples/med_term_purchasing.py +++ b/gdplib/pyomo_examples/med_term_purchasing.py @@ -34,24 +34,18 @@ def build_model(): """ - Build a Pyomo abstract model for the medium-term purchasing contracts problem. + Build a Pyomo abstract model for the medium-term purchasing contracts problem. - Returns - ------- - Pyomo.AbstractModel - Pyomo abstract model for medium-term purchasing contracts problem. - - References - ---------- -<<<<<<< HEAD - [1] Vecchietti, A., & Grossmann, I. (2004). Computational experience with logmip solving linear and nonlinear disjunctive programming problems. Proc. of FOCAPD, 587–590. - [2] Vecchietti, A., Lee, S., & Grossmann, I. E. (2003). Modeling of discrete/continuous optimization problems: characterization and formulation of disjunctions and their relaxations. Computers & Chemical Engineering, 27(3), 433–448. DOI: 10.1016/S0098-1354(02)00220-X - [3] Park, M., Park, S., Mele, F. D., & Grossmann, I. E. (2006). Modeling of purchase and sales contracts in supply chain optimization. Industrial and Engineering Chemistry Research, 45(14), 5013–5026. DOI: 10.1021/ie0513144 -======= - [1] Vecchietti, Aldo, and I. Grossmann. "Computational experience with logmip solving linear and nonlinear disjunctive programming problems." In Proc. of FOCAPD, pp. 587-590. 2004. - [2] Vecchietti, A., S. Lee and I.E. Grossmann, “Modeling of Discrete/Continuous Optimization Problems: Characterization and Formulation of Disjunctions and their Relaxations,” Computers and Chemical Engineering 27, 433-448 (2003). https://doi.org/10.1016/S0098-1354(02)00220-X - [3] Park, M., Park, S., Mele, F. D., & Grossmann, I. E. (2006). Modeling of purchase and sales contracts in supply chain optimization. Industrial and Engineering Chemistry Research, 45(14), 5013–5026. https://doi.org/10.1021/ie0513144 ->>>>>>> a04daf9 (Added References) + Returns + ------- + Pyomo.AbstractModel + Pyomo abstract model for medium-term purchasing contracts problem. + + References + ---------- + [1] Vecchietti, Aldo, and I. Grossmann. "Computational experience with logmip solving linear and nonlinear disjunctive programming problems." In Proc. of FOCAPD, pp. 587-590. 2004. + [2] Vecchietti, A., S. Lee and I.E. Grossmann, “Modeling of Discrete/Continuous Optimization Problems: Characterization and Formulation of Disjunctions and their Relaxations,” Computers and Chemical Engineering 27, 433-448 (2003). https://doi.org/10.1016/S0098-1354(02)00220-X + [3] Park, M., Park, S., Mele, F. D., & Grossmann, I. E. (2006). Modeling of purchase and sales contracts in supply chain optimization. Industrial and Engineering Chemistry Research, 45(14), 5013–5026. https://doi.org/10.1021/ie0513144 """ model = AbstractModel() From 67ddd73b2efbfeaa207b29bf4fb9c3a760b5f979 Mon Sep 17 00:00:00 2001 From: Albert Lee Date: Tue, 23 Apr 2024 14:26:19 -0400 Subject: [PATCH 56/99] Added Reference DOI --- gdplib/pyomo_examples/batch_processing.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gdplib/pyomo_examples/batch_processing.py b/gdplib/pyomo_examples/batch_processing.py index d481199..04600e8 100644 --- a/gdplib/pyomo_examples/batch_processing.py +++ b/gdplib/pyomo_examples/batch_processing.py @@ -45,8 +45,8 @@ def build_model(): References ---------- - Ravemark, E. Optimization models for design and operation of chemical batch processes. Ph.D. Thesis, ETH Zurich, 1995. - Vecchietti, A., & Grossmann, I. E. (1999). LOGMIP: a disjunctive 0–1 non-linear optimizer for process system models. Computers & chemical engineering, 23(4-5), 555-565. + [1] Ravemark, E. Optimization models for design and operation of chemical batch processes. Ph.D. Thesis, ETH Zurich, 1995. https://doi.org/10.3929/ethz-a-001591449 + [2] Vecchietti, A., & Grossmann, I. E. (1999). LOGMIP: a disjunctive 0–1 non-linear optimizer for process system models. Computers & chemical engineering, 23(4-5), 555-565. https://doi.org/10.1016/S0098-1354(97)87539-4 """ model = AbstractModel() From c122d1d8010197d091e3eb5d36a2ca2a6efdbce4 Mon Sep 17 00:00:00 2001 From: Albert Lee Date: Tue, 23 Apr 2024 15:08:52 -0400 Subject: [PATCH 57/99] Add doc on the Parameters --- gdplib/pyomo_examples/batch_processing.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/gdplib/pyomo_examples/batch_processing.py b/gdplib/pyomo_examples/batch_processing.py index 04600e8..55cae74 100644 --- a/gdplib/pyomo_examples/batch_processing.py +++ b/gdplib/pyomo_examples/batch_processing.py @@ -108,14 +108,14 @@ def filter_out_last(model, j): model.Beta1 = Param(doc='Exponent Parameter of the units') model.Beta2 = Param(doc='Exponent Parameter of the intermediate storage tanks') - model.ProductionAmount = Param(model.PRODUCTS) - model.ProductSizeFactor = Param(model.PRODUCTS, model.STAGES) - model.ProcessingTime = Param(model.PRODUCTS, model.STAGES) + model.ProductionAmount = Param(model.PRODUCTS, doc='Production Amount') + model.ProductSizeFactor = Param(model.PRODUCTS, model.STAGES, doc='Product Size Factor') + model.ProcessingTime = Param(model.PRODUCTS, model.STAGES, doc='Processing Time') # These are hard-coded in the GAMS file, hence the defaults - model.StorageTankSizeFactor = Param(model.STAGES, default=StorageTankSizeFactor) + model.StorageTankSizeFactor = Param(model.STAGES, default=StorageTankSizeFactor, doc='Storage Tank Size Factor') model.StorageTankSizeFactorByProd = Param(model.PRODUCTS, model.STAGES, - default=StorageTankSizeFactorByProd) + default=StorageTankSizeFactorByProd, doc='Storage Tank Size Factor by Product') # TODO: bonmin wasn't happy and I think it might have something to do with this? # or maybe issues with convexity or a lack thereof... I don't know yet. From 64202f40904c973f7157907e649fb7ca2a27854e Mon Sep 17 00:00:00 2001 From: Albert Lee Date: Tue, 23 Apr 2024 19:54:50 -0400 Subject: [PATCH 58/99] Set up the parameters of the distance and the coordinates of the sup mkt and site --- gdplib/biofuel/model.py | 734 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 728 insertions(+), 6 deletions(-) diff --git a/gdplib/biofuel/model.py b/gdplib/biofuel/model.py index 9ed8908..58a97b0 100644 --- a/gdplib/biofuel/model.py +++ b/gdplib/biofuel/model.py @@ -11,21 +11,43 @@ def build_model(): + """_summary_ + + 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. + """ m = ConcreteModel() m.bigM = Suffix(direction=Suffix.LOCAL) m.time = RangeSet(0, 120, doc="months in 10 years") - m.suppliers = RangeSet(10) - m.markets = RangeSet(10) - m.potential_sites = RangeSet(12) - m.discount_rate = Param(initialize=0.08, doc="8%") + m.suppliers = RangeSet(10) # 10 suppliers + m.markets = RangeSet(10) # 10 markets + m.potential_sites = RangeSet(12) # 12 facility sites + m.discount_rate = Param(initialize=0.08, doc="discount rate [8%]") m.conv_setup_time = Param(initialize=12) m.modular_setup_time = Param(initialize=3) m.modular_teardown_time = Param(initialize=3) - m.teardown_value = Param(initialize=0.30, doc="30%") - m.conventional_salvage_value = Param(initialize=0.05, doc="5%") + m.teardown_value = Param(initialize=0.30, doc="tear down value [30%]") + m.conventional_salvage_value = Param(initialize=0.05, doc="salvage value [5%]") @m.Param(m.time) def discount_factor(m, t): + """ + Calculate the discount factor for a given time period 't', based on a monthly compounding interest rate. + + Parameters + ---------- + m : Pyomo.ConcreteModel + Pyomo concrete model which descibes the multiperiod location-allocation optimization model + t : int + Index of time in months from 0 to 120 (10 years) + + Returns + ------- + Pyomo.Parameter + The discount factor for month 't', calculated using the formula (1 + r/12)**(-t/12) where 'r' is the annual discount rate. + """ return (1 + m.discount_rate / 12) ** (-t / 12) xls_data = pd.read_excel( @@ -35,6 +57,23 @@ def discount_factor(m, t): @m.Param(m.markets, m.time, doc="Market demand [thousand ton/month]") def market_demand(m, mkt, t): + """ + Calculate the market demand for a given market 'mkt' at time 't', based on the demand data provided in the Excel file. + + Parameters + ---------- + m : Pyomo.ConcreteModel + Pyomo concrete model which descibes the multiperiod location-allocation optimization model + mkt : int + Index of the market from 1 to 10 + t : int + Index of time in months from 0 to 120 (10 years) + + Returns + ------- + Pyomo.Parameter + If the conversion setup time is less than or equal to 't' and 't' is less than the maximum time period minus 3 months, return the market demand in thousand tons per month, otherwise return 0. + """ if m.conv_setup_time <= t <= max(m.time) - 3: return float(xls_data["markets"]["demand"][mkt]) / 1000 / 12 else: @@ -42,6 +81,23 @@ def market_demand(m, mkt, t): @m.Param(m.suppliers, m.time, doc="Raw material supply [thousand ton/month]") def available_supply(m, sup, t): + """ + Calculate the available supply of raw materials for a given supplier 'sup' at time 't', based on the supply data provided in the Excel file. + + Parameters + ---------- + m : Pyomo.ConcreteModel + Pyomo concrete model which descibes the multiperiod location-allocation optimization model + sup : int + Index of the supplier from 1 to 10 + t : int + Index of time in months from 0 to 120 (10 years) + + Returns + ------- + Pyomo.Parameter + If 't' is before the growth period or after the decay period, return 0, otherwise return the available supply in thousand tons per month. + """ # If t is before supply available or after supply decayed, then no # supply if t < float(xls_data["sources"]["growth"][sup]): @@ -53,35 +109,159 @@ def available_supply(m, sup, t): @m.Param(m.suppliers) def supplier_x(m, sup): + """ + Get the x-coordinate of the supplier location in miles from the Excel data. + + Parameters + ---------- + m : Pyomo.ConcreteModel + Pyomo concrete model which descibes the multiperiod location-allocation optimization model + sup : int + Index of the supplier from 1 to 10 + + Returns + ------- + Pyomo.Parameter + x-coordinate of the supplier location in miles + """ return float(xls_data["sources"]["x"][sup]) @m.Param(m.suppliers) def supplier_y(m, sup): + """ + Get the y-coordinate of the supplier location in miles from the Excel data. + + Parameters + ---------- + m : Pyomo.ConcreteModel + Pyomo concrete model which descibes the multiperiod location-allocation optimization model + sup : int + Index of the supplier from 1 to 10 + + Returns + ------- + Pyomo.Parameter + y-coordinate of the supplier location in miles + """ return float(xls_data["sources"]["y"][sup]) @m.Param(m.markets) def market_x(m, mkt): + """ + Get the x-coordinate of the market location in miles from the Excel data. + + Parameters + ---------- + m : Pyomo.ConcreteModel + Pyomo concrete model which descibes the multiperiod location-allocation optimization model + mkt : int + Index of the market from 1 to 10 + + Returns + ------- + Pyomo.Parameter + x-coordinate of the market location in miles + """ return float(xls_data["markets"]["x"][mkt]) @m.Param(m.markets) def market_y(m, mkt): + """ + Get the y-coordinate of the market location in miles from the Excel data. + + Parameters + ---------- + m : Pyomo.ConcreteModel + Pyomo concrete model which descibes the multiperiod location-allocation optimization model + mkt : int + Index of the market from 1 to 10 + + Returns + ------- + Pyomo.Parameter + y-coordinate of the market location in miles + """ return float(xls_data["markets"]["y"][mkt]) @m.Param(m.potential_sites) def site_x(m, site): + """ + Get the x-coordinate of the facility site location in miles from the Excel data. + + Parameters + ---------- + m : Pyomo.ConcreteModel + Pyomo concrete model which descibes the multiperiod location-allocation optimization model + site : int + Index of the facility site from 1 to 12 + + Returns + ------- + Pyomo.Parameter + x-coordinate of the facility site location in miles + """ return float(xls_data["sites"]["x"][site]) @m.Param(m.potential_sites) def site_y(m, site): + """ + Get the y-coordinate of the facility site location in miles from the Excel data. + + Parameters + ---------- + m : Pyomo.ConcreteModel + Pyomo concrete model which descibes the multiperiod location-allocation optimization model + site : int + Index of the facility site from 1 to 12 + + Returns + ------- + Pyomo.Parameter + y-coordinate of the facility site location in miles + """ return float(xls_data["sites"]["y"][site]) @m.Param(m.suppliers, m.potential_sites, doc="Miles") def dist_supplier_to_site(m, sup, site): + """ + Calculate the distance in miles between a supplier 'sup' and a facility site 'site' using the Euclidean distance formula. + + Parameters + ---------- + m : Pyomo.ConcreteModel + Pyomo concrete model which descibes the multiperiod location-allocation optimization model + sup : int + Index of the supplier from 1 to 10 + site : int + Index of the facility site from 1 to 12 + + Returns + ------- + Pyomo.Parameter + The distance in miles between the supplier and the facility site + """ return sqrt((m.supplier_x[sup] - m.site_x[site]) ** 2 + (m.supplier_y[sup] - m.site_y[site]) ** 2) @m.Param(m.potential_sites, m.markets, doc="Miles") def dist_site_to_market(m, site, mkt): + """ + Calculate the distance in miles between a facility site 'site' and a market 'mkt' using the Euclidean distance formula. + + Parameters + ---------- + m : Pyomo.ConcreteModel + Pyomo concrete model which descibes the multiperiod location-allocation optimization model + site : int + Index of the facility site from 1 to 12 + mkt : int + Index of the market from 1 to 10 + + Returns + ------- + Pyomo.Parameter + The distance in miles between the facility site and the market + """ return sqrt((m.site_x[site] - m.market_x[mkt]) ** 2 + (m.site_y[site] - m.market_y[mkt]) ** 2) @@ -109,26 +289,110 @@ def dist_site_to_market(m, site, mkt): @m.Param(m.suppliers, m.time) def raw_material_unit_cost(m, sup, t): + """_summary_ + + Parameters + ---------- + m : Pyomo.ConcreteModel + Pyomo concrete model which descibes the multiperiod location-allocation optimization model + sup : _type_ + _description_ + t : int + Index of time in months from 0 to 120 (10 years) + + Returns + ------- + _type_ + _description_ + """ return float(xls_data["sources"]["cost"][sup]) * m.discount_factor[t] @m.Param(m.time) def module_unit_cost(m, t): + """_summary_ + + Parameters + ---------- + m : Pyomo.ConcreteModel + Pyomo concrete model which descibes the multiperiod location-allocation optimization model + t : int + Index of time in months from 0 to 120 (10 years) + + Returns + ------- + _type_ + _description_ + """ return m.module_base_cost * m.discount_factor[t] @m.Param(m.time, doc="$/ton") def unit_production_cost(m, t): + """_summary_ + + Parameters + ---------- + m : Pyomo.ConcreteModel + _description_ + t : int + Index of time in months from 0 to 120 (10 years) + + Returns + ------- + _type_ + _description_ + """ return 300 * m.discount_factor[t] @m.Param(doc="thousand $") def transport_fixed_cost(m): + """_summary_ + + Parameters + ---------- + m : Pyomo.ConcreteModel + Pyomo concrete model which descibes the multiperiod location-allocation optimization model + + Returns + ------- + _type_ + _description_ + """ return 125 @m.Param(m.time, doc="$/ton-mile") def unit_product_transport_cost(m, t): + """_summary_ + + Parameters + ---------- + m : Pyomo.ConcreteModel + Pyomo concrete model which descibes the multiperiod location-allocation optimization model + t : int + Index of time in months from 0 to 120 (10 years) + + Returns + ------- + _type_ + _description_ + """ return 0.13 * m.discount_factor[t] @m.Param(m.time, doc="$/ton-mile") def unit_raw_material_transport_cost(m, t): + """_summary_ + + Parameters + ---------- + m : Pyomo.ConcreteModel + Pyomo concrete model which descibes the multiperiod location-allocation optimization model + t : int + Index of time in months from 0 to 120 (10 years) + + Returns + ------- + _type_ + _description_ + """ return 2 * m.discount_factor[t] m.supply_shipments = Var( @@ -140,21 +404,85 @@ def unit_raw_material_transport_cost(m, t): @m.Constraint(m.suppliers, m.time) def supply_limits(m, sup, t): + """_summary_ + + Parameters + ---------- + m : Pyomo.ConcreteModel + Pyomo concrete model which descibes the multiperiod location-allocation optimization model + sup : _type_ + _description_ + t : int + Index of time in months from 0 to 120 (10 years) + + Returns + ------- + _type_ + _description_ + """ return sum(m.supply_shipments[sup, site, t] for site in m.potential_sites) <= m.available_supply[sup, t] @m.Constraint(m.markets, m.time) def demand_satisfaction(m, mkt, t): + """_summary_ + + Parameters + ---------- + m : Pyomo.ConcreteModel + Pyomo concrete model which descibes the multiperiod location-allocation optimization model + mkt : _type_ + _description_ + t : int + Index of time in months from 0 to 120 (10 years) + + Returns + ------- + _type_ + _description_ + """ return sum(m.product_shipments[site, mkt, t] for site in m.potential_sites) == m.market_demand[mkt, t] @m.Constraint(m.potential_sites, m.time) def product_balance(m, site, t): + """_summary_ + + Parameters + ---------- + m : Pyomo.ConcreteModel + Pyomo concrete model which descibes the multiperiod location-allocation optimization model + site : _type_ + _description_ + t : int + Index of time in months from 0 to 120 (10 years) + + Returns + ------- + _type_ + _description_ + """ return m.production[site, t] == sum(m.product_shipments[site, mkt, t] for mkt in m.markets) @m.Constraint(m.potential_sites, m.time) def require_raw_materials(m, site, t): + """_summary_ + + Parameters + ---------- + m : Pyomo.ConcreteModel + Pyomo concrete model which descibes the multiperiod location-allocation optimization model + site : _type_ + _description_ + t : int + Index of time in months from 0 to 120 (10 years) + + Returns + ------- + _type_ + _description_ + """ return m.production[site, t] <= m.conversion * m.supply[site, t] m.modular = Disjunct(m.potential_sites, rule=_build_modular_disjunct) @@ -169,18 +497,80 @@ def require_raw_materials(m, site, t): @m.Disjunction(m.potential_sites) def site_type(m, site): + """_summary_ + + Parameters + ---------- + m : Pyomo.ConcreteModel + Pyomo concrete model which descibes the multiperiod location-allocation optimization model + site : _type_ + _description_ + + Returns + ------- + _type_ + _description_ + """ return [m.modular[site], m.conventional[site], m.site_inactive[site]] @m.Disjunction(m.suppliers, m.potential_sites) def supply_route_active_or_not(m, sup, site): + """_summary_ + + Parameters + ---------- + m : Pyomo.ConcreteModel + Pyomo concrete model which descibes the multiperiod location-allocation optimization model + sup : _type_ + _description_ + site : _type_ + _description_ + + Returns + ------- + _type_ + _description_ + """ return [m.supply_route_active[sup, site], m.supply_route_inactive[sup, site]] @m.Disjunction(m.potential_sites, m.markets) def product_route_active_or_not(m, site, mkt): + """_summary_ + + Parameters + ---------- + m : Pyomo.ConcreteModel + Pyomo concrete model which descibes the multiperiod location-allocation optimization model + site : _type_ + _description_ + mkt : _type_ + _description_ + + Returns + ------- + _type_ + _description_ + """ return [m.product_route_active[site, mkt], m.product_route_inactive[site, mkt]] @m.Expression(m.suppliers, m.potential_sites, doc="million $") def raw_material_transport_cost(m, sup, site): + """_summary_ + + Parameters + ---------- + m : Pyomo.ConcreteModel + Pyomo concrete model which descibes the multiperiod location-allocation optimization model + sup : _type_ + _description_ + site : _type_ + _description_ + + Returns + ------- + _type_ + _description_ + """ return sum( m.supply_shipments[sup, site, t] * m.unit_raw_material_transport_cost[t] @@ -189,6 +579,18 @@ def raw_material_transport_cost(m, sup, site): @m.Expression(doc="million $") def raw_material_fixed_transport_cost(m): + """_summary_ + + Parameters + ---------- + m : Pyomo.ConcreteModel + Pyomo concrete model which descibes the multiperiod location-allocation optimization model + + Returns + ------- + _type_ + _description_ + """ return ( sum(m.supply_route_active[sup, site].binary_indicator_var for sup in m.suppliers for site in m.potential_sites) @@ -196,6 +598,22 @@ def raw_material_fixed_transport_cost(m): @m.Expression(m.potential_sites, m.markets, doc="million $") def product_transport_cost(m, site, mkt): + """_summary_ + + Parameters + ---------- + m : Pyomo.ConcreteModel + Pyomo concrete model which descibes the multiperiod location-allocation optimization model + site : _type_ + _description_ + mkt : _type_ + _description_ + + Returns + ------- + _type_ + _description_ + """ return sum( m.product_shipments[site, mkt, t] * m.unit_product_transport_cost[t] @@ -204,6 +622,18 @@ def product_transport_cost(m, site, mkt): @m.Expression(doc="million $") def product_fixed_transport_cost(m): + """_summary_ + + Parameters + ---------- + m : Pyomo.ConcreteModel + Pyomo concrete model which descibes the multiperiod location-allocation optimization model + + Returns + ------- + _type_ + _description_ + """ return ( sum(m.product_route_active[site, mkt].binary_indicator_var for site in m.potential_sites for mkt in m.markets) @@ -211,14 +641,60 @@ def product_fixed_transport_cost(m): @m.Expression(m.potential_sites, m.time, doc="Cost of module setups in each month [million $]") def module_setup_cost(m, site, t): + """_summary_ + + Parameters + ---------- + m : Pyomo.ConcreteModel + Pyomo concrete model which descibes the multiperiod location-allocation optimization model + site : _type_ + _description_ + t : int + _description_ + + Returns + ------- + _type_ + _description_ + """ return m.modules_purchased[site, t] * m.module_unit_cost[t] @m.Expression(m.potential_sites, m.time, doc="Value of module teardowns in each month [million $]") def module_teardown_credit(m, site, t): + """_summary_ + + Parameters + ---------- + m : Pyomo.ConcreteModel + Pyomo concrete model which descibes the multiperiod location-allocation optimization model + site : _type_ + _description_ + t : int + Index of time in months from 0 to 120 (10 years) + + Returns + ------- + _type_ + _description_ + """ return m.modules_sold[site, t] * m.module_unit_cost[t] * m.teardown_value @m.Expression(m.potential_sites, doc="Conventional site salvage value") def conv_salvage_value(m, site): + """_summary_ + + Parameters + ---------- + m : Pyomo.ConcreteModel + Pyomo concrete model which descibes the multiperiod location-allocation optimization model + site : _type_ + _description_ + + Returns + ------- + _type_ + _description_ + """ return m.conv_build_cost[site] * m.discount_factor[m.time.last()] * m.conventional_salvage_value m.total_cost = Objective( @@ -238,22 +714,86 @@ def conv_salvage_value(m, site): def _build_site_inactive_disjunct(disj, site): + """_summary_ + + Parameters + ---------- + disj : _type_ + _description_ + site : _type_ + _description_ + + Returns + ------- + _type_ + _description_ + """ m = disj.model() @disj.Constraint() def no_modules(disj): + """_summary_ + + Parameters + ---------- + disj : _type_ + _description_ + + Returns + ------- + _type_ + _description_ + """ return sum(m.num_modules[...]) + sum(m.modules_purchased[...]) + sum(m.modules_sold[...]) == 0 @disj.Constraint() def no_production(disj): + """_summary_ + + Parameters + ---------- + disj : _type_ + _description_ + + Returns + ------- + _type_ + _description_ + """ return sum(m.production[site, t] for t in m.time) == 0 @disj.Constraint() def no_supply(disj): + """_summary_ + + Parameters + ---------- + disj : _type_ + _description_ + + Returns + ------- + _type_ + _description_ + """ return sum(m.supply[site, t] for t in m.time) == 0 def _build_conventional_disjunct(disj, site): + """_summary_ + + Parameters + ---------- + disj : _type_ + _description_ + site : _type_ + _description_ + + Returns + ------- + _type_ + _description_ + """ m = disj.model() disj.cost_calc = Constraint( @@ -263,11 +803,39 @@ def _build_conventional_disjunct(disj, site): @disj.Constraint(m.time) def supply_balance(disj, t): + """_summary_ + + Parameters + ---------- + disj : _type_ + _description_ + t : int + Index of time in months from 0 to 120 (10 years) + + Returns + ------- + _type_ + _description_ + """ return m.supply[site, t] == sum( m.supply_shipments[sup, site, t] for sup in m.suppliers) @disj.Constraint(m.time) def conv_production_limit(conv_disj, t): + """_summary_ + + Parameters + ---------- + conv_disj : _type_ + _description_ + t : int + Index of time in months from 0 to 120 (10 years) + + Returns + ------- + _type_ + _description_ + """ if t < m.conv_setup_time: return m.production[site, t] == 0 else: @@ -275,19 +843,73 @@ def conv_production_limit(conv_disj, t): @disj.Constraint() def no_modules(disj): + """_summary_ + + Parameters + ---------- + disj : _type_ + _description_ + + Returns + ------- + _type_ + _description_ + """ return sum(m.num_modules[...]) + sum(m.modules_purchased[...]) + sum(m.modules_sold[...]) == 0 def _build_modular_disjunct(disj, site): + """_summary_ + + Parameters + ---------- + disj : _type_ + _description_ + site : _type_ + _description_ + + Returns + ------- + _type_ + _description_ + """ m = disj.model() @disj.Constraint(m.time) def supply_balance(disj, t): + """_summary_ + + Parameters + ---------- + disj : _type_ + _description_ + t : int + Index of time in months from 0 to 120 (10 years) + + Returns + ------- + _type_ + _description_ + """ return m.supply[site, t] == sum( m.supply_shipments[sup, site, t] for sup in m.suppliers) @disj.Constraint(m.time) def module_balance(disj, t): + """_summary_ + + Parameters + ---------- + disj : _type_ + _description_ + t : int + Index of time in months from 0 to 120 (10 years) + + Returns + ------- + _type_ + _description_ + """ existing_modules = 0 if t == m.time.first() else m.num_modules[site, t - 1] new_modules = 0 if t < m.modular_setup_time else m.modules_purchased[site, t - m.modular_setup_time] sold_modules = m.modules_sold[site, t] @@ -298,34 +920,134 @@ def module_balance(disj, t): @disj.Constraint(m.time) def modular_production_limit(mod_disj, t): + """_summary_ + + Parameters + ---------- + mod_disj : _type_ + _description_ + t : int + Index of time in months from 0 to 120 (10 years) + + Returns + ------- + _type_ + _description_ + """ return m.production[site, t] <= 10 * m.num_modules[site, t] def _build_supply_route_active(disj, sup, site): + """_summary_ + + Parameters + ---------- + disj : _type_ + _description_ + sup : _type_ + _description_ + site : _type_ + _description_ + """ m = disj.model() def _build_supply_route_inactive(disj, sup, site): + """_summary_ + + Parameters + ---------- + disj : _type_ + _description_ + sup : _type_ + _description_ + site : _type_ + _description_ + + Returns + ------- + _type_ + _description_ + """ m = disj.model() @disj.Constraint() def no_supply(disj): + """_summary_ + + Parameters + ---------- + disj : _type_ + _description_ + + Returns + ------- + _type_ + _description_ + """ return sum(m.supply_shipments[sup, site, t] for t in m.time) == 0 def _build_product_route_active(disj, site, mkt): + """_summary_ + + Parameters + ---------- + disj : _type_ + _description_ + site : _type_ + _description_ + mkt : _type_ + _description_ + """ m = disj.model() def _build_product_route_inactive(disj, site, mkt): + """_summary_ + + Parameters + ---------- + disj : _type_ + _description_ + site : _type_ + _description_ + mkt : _type_ + _description_ + + Returns + ------- + _type_ + _description_ + """ m = disj.model() @disj.Constraint() def no_product(disj): + """_summary_ + + Parameters + ---------- + disj : _type_ + _description_ + + Returns + ------- + _type_ + _description_ + """ return sum(m.product_shipments[site, mkt, t] for t in m.time) == 0 def print_nonzeros(var): + """ + Print the nonzero values of a Pyomo variable + + Parameters + ---------- + var : pyomo.Var + Pyomo variable of the model + """ for i in var: if var[i].value != 0: print("%7s : %10f : %10f : %10f" % (i, var[i].lb, var[i].value, var[i].ub)) From 8e6ed91e9ba85958f51741d4b1cd3d9faaf1d040 Mon Sep 17 00:00:00 2001 From: Albert Lee Date: Tue, 23 Apr 2024 21:12:48 -0400 Subject: [PATCH 59/99] Added documentation on the Parameter and References --- gdplib/biofuel/model.py | 59 ++++++++++++++++++++++++----------------- 1 file changed, 35 insertions(+), 24 deletions(-) diff --git a/gdplib/biofuel/model.py b/gdplib/biofuel/model.py index 58a97b0..305b20b 100644 --- a/gdplib/biofuel/model.py +++ b/gdplib/biofuel/model.py @@ -17,6 +17,11 @@ def build_model(): ------- 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. + + References + ---------- + [1] Lara, C. L., Trespalacios, F., & Grossmann, I. E. (2018). Global optimization algorithm for capacitated multi-facility continuous location-allocation problems. Journal of Global Optimization, 71(4), 871-889. https://doi.org/10.1007/s10898-018-0621-6 + [2] Chen, Q., & Grossmann, I. E. (2019). Effective generalized disjunctive programming models for modular process synthesis. Industrial & Engineering Chemistry Research, 58(15), 5873-5886. https://doi.org/10.1021/acs.iecr.8b04600 """ m = ConcreteModel() m.bigM = Suffix(direction=Suffix.LOCAL) @@ -278,9 +283,9 @@ def dist_site_to_market(m, site, mkt): m.supply = Var(m.potential_sites, m.time, bounds=(0, 120 / 12 / 0.26 * 10), doc="thousand ton/mo") m.production = Var(m.potential_sites, m.time, bounds=(0, 120 / 12 * 10), doc="thousand ton/mo") - m.num_modules = Var(m.potential_sites, m.time, domain=Integers, bounds=(0, 10)) - m.modules_purchased = Var(m.potential_sites, m.time, domain=Integers, bounds=(0, 10)) - m.modules_sold = Var(m.potential_sites, m.time, domain=Integers, bounds=(0, 10)) + m.num_modules = Var(m.potential_sites, m.time, domain=Integers, bounds=(0, 10), doc="Number of modules") + m.modules_purchased = Var(m.potential_sites, m.time, domain=Integers, bounds=(0, 10), doc="Modules purchased") + m.modules_sold = Var(m.potential_sites, m.time, domain=Integers, bounds=(0, 10), doc="Modules sold") m.conv_build_cost = Var( m.potential_sites, @@ -289,27 +294,29 @@ def dist_site_to_market(m, site, mkt): @m.Param(m.suppliers, m.time) def raw_material_unit_cost(m, sup, t): - """_summary_ + """ + Calculate the unit cost of raw materials for a given supplier 'sup' at time 't', based on the cost data provided in the Excel file. Parameters ---------- m : Pyomo.ConcreteModel Pyomo concrete model which descibes the multiperiod location-allocation optimization model - sup : _type_ - _description_ + sup : int + Index of the supplier from 1 to 10 t : int Index of time in months from 0 to 120 (10 years) Returns ------- - _type_ - _description_ + Pyomo.Parameter + The unit cost of raw materials for the supplier at time 't', calculated as the cost from the Excel data multiplied by the discount factor for time 't'. """ return float(xls_data["sources"]["cost"][sup]) * m.discount_factor[t] @m.Param(m.time) def module_unit_cost(m, t): - """_summary_ + """ + Calculate the unit cost of modules at time 't', based on the cost data provided in the Excel file. Parameters ---------- @@ -320,32 +327,34 @@ def module_unit_cost(m, t): Returns ------- - _type_ - _description_ + Pyomo.Parameter + The unit cost of modules at time 't', calculated as the cost from the Excel data multiplied by the discount factor for time 't'. """ return m.module_base_cost * m.discount_factor[t] @m.Param(m.time, doc="$/ton") def unit_production_cost(m, t): - """_summary_ + """ + Calculate the unit production cost at time 't', the production cost is 300 $/ton multiplied by the discount factor for time 't'. Parameters ---------- m : Pyomo.ConcreteModel - _description_ + Pyomo concrete model which descibes the multiperiod location-allocation optimization model t : int Index of time in months from 0 to 120 (10 years) Returns ------- - _type_ - _description_ + Pyomo.Parameter + The unit production cost at time 't', calculated as 300 $/ton multiplied by the discount factor for time 't'. """ return 300 * m.discount_factor[t] @m.Param(doc="thousand $") def transport_fixed_cost(m): - """_summary_ + """ + Fixed cost of transportation in thousand dollars. Parameters ---------- @@ -354,14 +363,15 @@ def transport_fixed_cost(m): Returns ------- - _type_ - _description_ + Pyomo.Parameter + The fixed cost of transportation in thousand dollars, the cost is 125 thousand dollars. """ return 125 @m.Param(m.time, doc="$/ton-mile") def unit_product_transport_cost(m, t): - """_summary_ + """ + Calculate the unit product transport cost at time 't', the cost is 0.13 $/ton-mile multiplied by the discount factor for time 't'. Parameters ---------- @@ -372,14 +382,15 @@ def unit_product_transport_cost(m, t): Returns ------- - _type_ - _description_ + Pyomo.Parameter + The unit product transport cost at time 't', calculated as 0.13 $/ton-mile multiplied by the discount factor for time 't'. """ return 0.13 * m.discount_factor[t] @m.Param(m.time, doc="$/ton-mile") def unit_raw_material_transport_cost(m, t): - """_summary_ + """ + Calculate the unit raw material transport cost at time 't', the cost is 2 $/ton-mile multiplied by the discount factor for time 't'. Parameters ---------- @@ -390,8 +401,8 @@ def unit_raw_material_transport_cost(m, t): Returns ------- - _type_ - _description_ + Pyomo.Parameter + The unit raw material transport cost at time 't', calculated as 2 $/ton-mile multiplied by the discount factor for time 't'. """ return 2 * m.discount_factor[t] From 2d0af2fa1d4d456eba57f3b7a359aefbb51df704 Mon Sep 17 00:00:00 2001 From: Albert Lee Date: Tue, 23 Apr 2024 22:03:31 -0400 Subject: [PATCH 60/99] Update documentation for supply and demand constraints in biofuel model --- gdplib/biofuel/model.py | 44 ++++++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/gdplib/biofuel/model.py b/gdplib/biofuel/model.py index 305b20b..72e217c 100644 --- a/gdplib/biofuel/model.py +++ b/gdplib/biofuel/model.py @@ -415,84 +415,88 @@ def unit_raw_material_transport_cost(m, t): @m.Constraint(m.suppliers, m.time) def supply_limits(m, sup, t): - """_summary_ + """ + Ensure that the total supply from a supplier 'sup' at time 't' does not exceed the available supply from the supplier. Parameters ---------- m : Pyomo.ConcreteModel Pyomo concrete model which descibes the multiperiod location-allocation optimization model - sup : _type_ - _description_ + sup : int + Index of the supplier from 1 to 10 t : int Index of time in months from 0 to 120 (10 years) Returns ------- - _type_ - _description_ + Pyomo.Constraint + The total supply from the supplier 'sup' at time 't' should not exceed the available supply from the supplier. """ return sum(m.supply_shipments[sup, site, t] for site in m.potential_sites) <= m.available_supply[sup, t] @m.Constraint(m.markets, m.time) def demand_satisfaction(m, mkt, t): - """_summary_ + """ + Ensure that the total product shipments to a market 'mkt' at time 't' meets the market demand for the product. Parameters ---------- m : Pyomo.ConcreteModel Pyomo concrete model which descibes the multiperiod location-allocation optimization model - mkt : _type_ - _description_ + mkt : int + Index of the market from 1 to 10 t : int Index of time in months from 0 to 120 (10 years) Returns ------- - _type_ - _description_ + Pyomo.Constraint + The total product shipments to the market 'mkt' at time 't' should meet the market demand for the product. """ return sum(m.product_shipments[site, mkt, t] for site in m.potential_sites) == m.market_demand[mkt, t] @m.Constraint(m.potential_sites, m.time) def product_balance(m, site, t): - """_summary_ + """ + Ensure that the total product shipments from a facility site 'site' at time 't' meets the production from the site. Parameters ---------- m : Pyomo.ConcreteModel Pyomo concrete model which descibes the multiperiod location-allocation optimization model - site : _type_ - _description_ + site : int + Index of the facility site from 1 to 12 t : int Index of time in months from 0 to 120 (10 years) Returns ------- - _type_ - _description_ + Pyomo.Constraint + The total product shipments from the facility site 'site' at time 't' should meet the production from the site. """ return m.production[site, t] == sum(m.product_shipments[site, mkt, t] for mkt in m.markets) @m.Constraint(m.potential_sites, m.time) def require_raw_materials(m, site, t): - """_summary_ + """ + Ensure that the raw materials required for production at a facility site 'site' at time 't' are available from the suppliers. Parameters ---------- m : Pyomo.ConcreteModel Pyomo concrete model which descibes the multiperiod location-allocation optimization model - site : _type_ - _description_ + site : int + Index of the facility site from 1 to 12 t : int Index of time in months from 0 to 120 (10 years) Returns ------- - _type_ - _description_ + Pyomo.Constraint + The production at the facility site 'site' at time 't' should not exceed the raw materials available from the suppliers which is the supply multiplied by the conversion factor. """ return m.production[site, t] <= m.conversion * m.supply[site, t] From 1a6995bec6fd8488a671c8fd2718f096805339f5 Mon Sep 17 00:00:00 2001 From: Albert Lee Date: Tue, 23 Apr 2024 22:03:52 -0400 Subject: [PATCH 61/99] Update documentation for supply and demand constraints in biofuel model --- gdplib/biofuel/model.py | 130 ++++++++++++++++++++-------------------- 1 file changed, 65 insertions(+), 65 deletions(-) diff --git a/gdplib/biofuel/model.py b/gdplib/biofuel/model.py index 72e217c..2f44757 100644 --- a/gdplib/biofuel/model.py +++ b/gdplib/biofuel/model.py @@ -500,15 +500,15 @@ def require_raw_materials(m, site, t): """ return m.production[site, t] <= m.conversion * m.supply[site, t] - m.modular = Disjunct(m.potential_sites, rule=_build_modular_disjunct) - m.conventional = Disjunct(m.potential_sites, rule=_build_conventional_disjunct) - m.site_inactive = Disjunct(m.potential_sites, rule=_build_site_inactive_disjunct) + m.modular = Disjunct(m.potential_sites, rule=_build_modular_disjunct, doc="Disjunct for modular site") + m.conventional = Disjunct(m.potential_sites, rule=_build_conventional_disjunct, doc="Disjunct for conventional site") + m.site_inactive = Disjunct(m.potential_sites, rule=_build_site_inactive_disjunct, doc="Disjunct for inactive site") - m.supply_route_active = Disjunct(m.suppliers, m.potential_sites, rule=_build_supply_route_active) - m.supply_route_inactive = Disjunct(m.suppliers, m.potential_sites, rule=_build_supply_route_inactive) + m.supply_route_active = Disjunct(m.suppliers, m.potential_sites, rule=_build_supply_route_active, doc="Disjunct for active supply route") + m.supply_route_inactive = Disjunct(m.suppliers, m.potential_sites, rule=_build_supply_route_inactive, doc="Disjunct for inactive supply route") - m.product_route_active = Disjunct(m.potential_sites, m.markets, rule=_build_product_route_active) - m.product_route_inactive = Disjunct(m.potential_sites, m.markets, rule=_build_product_route_inactive) + m.product_route_active = Disjunct(m.potential_sites, m.markets, rule=_build_product_route_active, doc="Disjunct for active product route") + m.product_route_inactive = Disjunct(m.potential_sites, m.markets, rule=_build_product_route_inactive, doc="Disjunct for inactive product route") @m.Disjunction(m.potential_sites) def site_type(m, site): @@ -518,8 +518,8 @@ def site_type(m, site): ---------- m : Pyomo.ConcreteModel Pyomo concrete model which descibes the multiperiod location-allocation optimization model - site : _type_ - _description_ + site : int + Index of the facility site from 1 to 12 Returns ------- @@ -536,10 +536,10 @@ def supply_route_active_or_not(m, sup, site): ---------- m : Pyomo.ConcreteModel Pyomo concrete model which descibes the multiperiod location-allocation optimization model - sup : _type_ - _description_ - site : _type_ - _description_ + sup : int + Index of the supplier from 1 to 10 + site : int + Index of the facility site from 1 to 12 Returns ------- @@ -556,9 +556,9 @@ def product_route_active_or_not(m, site, mkt): ---------- m : Pyomo.ConcreteModel Pyomo concrete model which descibes the multiperiod location-allocation optimization model - site : _type_ - _description_ - mkt : _type_ + site : int + Index of the facility site from 1 to 12 + mkt : int _description_ Returns @@ -578,8 +578,8 @@ def raw_material_transport_cost(m, sup, site): Pyomo concrete model which descibes the multiperiod location-allocation optimization model sup : _type_ _description_ - site : _type_ - _description_ + site : int + Index of the facility site from 1 to 12 Returns ------- @@ -603,7 +603,7 @@ def raw_material_fixed_transport_cost(m): Returns ------- - _type_ + Pyomo.Expression _description_ """ return ( @@ -619,9 +619,9 @@ def product_transport_cost(m, site, mkt): ---------- m : Pyomo.ConcreteModel Pyomo concrete model which descibes the multiperiod location-allocation optimization model - site : _type_ - _description_ - mkt : _type_ + site : int + Index of the facility site from 1 to 12 + mkt : int _description_ Returns @@ -662,10 +662,10 @@ def module_setup_cost(m, site, t): ---------- m : Pyomo.ConcreteModel Pyomo concrete model which descibes the multiperiod location-allocation optimization model - site : _type_ - _description_ + site : int + Index of the facility site from 1 to 12 t : int - _description_ + Index of time in months from 0 to 120 (10 years) Returns ------- @@ -682,8 +682,8 @@ def module_teardown_credit(m, site, t): ---------- m : Pyomo.ConcreteModel Pyomo concrete model which descibes the multiperiod location-allocation optimization model - site : _type_ - _description_ + site : int + Index of the facility site from 1 to 12 t : int Index of time in months from 0 to 120 (10 years) @@ -702,8 +702,8 @@ def conv_salvage_value(m, site): ---------- m : Pyomo.ConcreteModel Pyomo concrete model which descibes the multiperiod location-allocation optimization model - site : _type_ - _description_ + site : int + Index of the facility site from 1 to 12 Returns ------- @@ -733,10 +733,10 @@ def _build_site_inactive_disjunct(disj, site): Parameters ---------- - disj : _type_ - _description_ - site : _type_ + disj : Pyomo.Disjunct _description_ + site : int + Index of the facility site from 1 to 12 Returns ------- @@ -751,7 +751,7 @@ def no_modules(disj): Parameters ---------- - disj : _type_ + disj : Pyomo.Disjunct _description_ Returns @@ -767,7 +767,7 @@ def no_production(disj): Parameters ---------- - disj : _type_ + disj : Pyomo.Disjunct _description_ Returns @@ -783,7 +783,7 @@ def no_supply(disj): Parameters ---------- - disj : _type_ + disj : Pyomo.Disjunct _description_ Returns @@ -799,10 +799,10 @@ def _build_conventional_disjunct(disj, site): Parameters ---------- - disj : _type_ - _description_ - site : _type_ + disj : Pyomo.Disjunct _description_ + site : int + Index of the facility site from 1 to 12 Returns ------- @@ -822,7 +822,7 @@ def supply_balance(disj, t): Parameters ---------- - disj : _type_ + disj : Pyomo.Disjunct _description_ t : int Index of time in months from 0 to 120 (10 years) @@ -841,7 +841,7 @@ def conv_production_limit(conv_disj, t): Parameters ---------- - conv_disj : _type_ + conv_disj : Pyomo.Disjunct _description_ t : int Index of time in months from 0 to 120 (10 years) @@ -862,7 +862,7 @@ def no_modules(disj): Parameters ---------- - disj : _type_ + disj : Pyomo.Disjunct _description_ Returns @@ -878,10 +878,10 @@ def _build_modular_disjunct(disj, site): Parameters ---------- - disj : _type_ - _description_ - site : _type_ + disj : Pyomo.Disjunct _description_ + site : int + Index of the facility site from 1 to 12 Returns ------- @@ -896,7 +896,7 @@ def supply_balance(disj, t): Parameters ---------- - disj : _type_ + disj : Pyomo.Disjunct _description_ t : int Index of time in months from 0 to 120 (10 years) @@ -915,7 +915,7 @@ def module_balance(disj, t): Parameters ---------- - disj : _type_ + disj : Pyomo.Disjunct _description_ t : int Index of time in months from 0 to 120 (10 years) @@ -939,7 +939,7 @@ def modular_production_limit(mod_disj, t): Parameters ---------- - mod_disj : _type_ + mod_disj : Pyomo.Disjunct _description_ t : int Index of time in months from 0 to 120 (10 years) @@ -957,12 +957,12 @@ def _build_supply_route_active(disj, sup, site): Parameters ---------- - disj : _type_ + disj : Pyomo.Disjunct _description_ sup : _type_ _description_ - site : _type_ - _description_ + site : int + Index of the facility site from 1 to 12 """ m = disj.model() @@ -972,12 +972,12 @@ def _build_supply_route_inactive(disj, sup, site): Parameters ---------- - disj : _type_ + disj : Pyomo.Disjunct _description_ sup : _type_ _description_ - site : _type_ - _description_ + site : int + Index of the facility site from 1 to 12 Returns ------- @@ -992,7 +992,7 @@ def no_supply(disj): Parameters ---------- - disj : _type_ + disj : Pyomo.Disjunct _description_ Returns @@ -1008,12 +1008,12 @@ def _build_product_route_active(disj, site, mkt): Parameters ---------- - disj : _type_ - _description_ - site : _type_ - _description_ - mkt : _type_ + disj : Pyomo.Disjunct _description_ + site : int + Index of the facility site from 1 to 12 + mkt : int + Index of the market from 1 to 10 """ m = disj.model() @@ -1023,12 +1023,12 @@ def _build_product_route_inactive(disj, site, mkt): Parameters ---------- - disj : _type_ - _description_ - site : _type_ - _description_ - mkt : _type_ + disj : Pyomo.Disjunct _description_ + site : int + Index of the facility site from 1 to 12 + mkt : int + Index of the market from 1 to 10 Returns ------- @@ -1043,7 +1043,7 @@ def no_product(disj): Parameters ---------- - disj : _type_ + disj : Pyomo.Disjunct _description_ Returns From 1e0e806d2743e51da662f794c31463fc4df5c75c Mon Sep 17 00:00:00 2001 From: Albert Lee Date: Tue, 23 Apr 2024 23:44:40 -0400 Subject: [PATCH 62/99] Add documentation of the disjunct and the disjunctions --- gdplib/biofuel/model.py | 253 ++++++++++++++++++++++------------------ 1 file changed, 141 insertions(+), 112 deletions(-) diff --git a/gdplib/biofuel/model.py b/gdplib/biofuel/model.py index 2f44757..0cfd8f1 100644 --- a/gdplib/biofuel/model.py +++ b/gdplib/biofuel/model.py @@ -512,7 +512,8 @@ def require_raw_materials(m, site, t): @m.Disjunction(m.potential_sites) def site_type(m, site): - """_summary_ + """ + Define the disjunction for the facility site type, which can be modular, conventional, or inactive. Parameters ---------- @@ -523,14 +524,15 @@ def site_type(m, site): Returns ------- - _type_ - _description_ + Pyomo.Disjunction + The disjunction for the facility site type, which can be modular, conventional, or inactive. """ return [m.modular[site], m.conventional[site], m.site_inactive[site]] @m.Disjunction(m.suppliers, m.potential_sites) def supply_route_active_or_not(m, sup, site): - """_summary_ + """ + Define the disjunction for the supply route between a supplier and a facility site, which can be active or inactive. Parameters ---------- @@ -543,14 +545,15 @@ def supply_route_active_or_not(m, sup, site): Returns ------- - _type_ - _description_ + Pyomo.Disjunction + The disjunction for the supply route between a supplier and a facility site, which can be active or inactive. """ return [m.supply_route_active[sup, site], m.supply_route_inactive[sup, site]] @m.Disjunction(m.potential_sites, m.markets) def product_route_active_or_not(m, site, mkt): - """_summary_ + """ + Define the disjunction for the product route between a facility site and a market, which can be active or inactive. Parameters ---------- @@ -559,42 +562,44 @@ def product_route_active_or_not(m, site, mkt): site : int Index of the facility site from 1 to 12 mkt : int - _description_ + Index of the market from 1 to 10 Returns ------- - _type_ - _description_ + Pyomo.Disjunction + The disjunction for the product route between a facility site and a market, which can be active or inactive. """ return [m.product_route_active[site, mkt], m.product_route_inactive[site, mkt]] @m.Expression(m.suppliers, m.potential_sites, doc="million $") def raw_material_transport_cost(m, sup, site): - """_summary_ + """ + Calculate the cost of transporting raw materials from a supplier 'sup' to a facility site 'site' at each time period using the unit raw material transport cost, the supply shipments, and the distance between the supplier and the site. Parameters ---------- m : Pyomo.ConcreteModel Pyomo concrete model which descibes the multiperiod location-allocation optimization model - sup : _type_ + sup : int _description_ site : int Index of the facility site from 1 to 12 Returns ------- - _type_ - _description_ + Pyomo.Expression + Total transportation cost considering the quantity of shipments, unit cost per time period, and distance between suppliers and sites. """ return sum( - m.supply_shipments[sup, site, t] - * m.unit_raw_material_transport_cost[t] - * m.dist_supplier_to_site[sup, site] / 1000 + m.supply_shipments[sup, site, t] # [1000 ton/month] + * m.unit_raw_material_transport_cost[t] # [$/ton-mile] + * m.dist_supplier_to_site[sup, site] / 1000 # [mile], [million/1000] for t in m.time) @m.Expression(doc="million $") def raw_material_fixed_transport_cost(m): - """_summary_ + """ + Calculate the fixed cost of transporting raw materials to the facility sites based on the total number of active supply routes and the fixed transportation cost. Parameters ---------- @@ -604,16 +609,17 @@ def raw_material_fixed_transport_cost(m): Returns ------- Pyomo.Expression - _description_ + Sum of fixed transport costs, accounting for the activation of each route. """ return ( sum(m.supply_route_active[sup, site].binary_indicator_var for sup in m.suppliers for site in m.potential_sites) - * m.transport_fixed_cost / 1000) + * m.transport_fixed_cost / 1000) # [thousand $] [million/1000] @m.Expression(m.potential_sites, m.markets, doc="million $") def product_transport_cost(m, site, mkt): - """_summary_ + """ + Calculate the cost of transporting products from a facility site 'site' to a market 'mkt' at each time period using the unit product transport cost, the product shipments, and the distance between the site and the market. Parameters ---------- @@ -626,18 +632,19 @@ def product_transport_cost(m, site, mkt): Returns ------- - _type_ - _description_ + Pyomo.Expression + Total transportation cost considering the quantity of shipments, unit cost per time period, and distance between sites and markets. """ return sum( - m.product_shipments[site, mkt, t] - * m.unit_product_transport_cost[t] - * m.dist_site_to_market[site, mkt] / 1000 + m.product_shipments[site, mkt, t] # [1000 ton/month] + * m.unit_product_transport_cost[t] # [$/ton-mile] + * m.dist_site_to_market[site, mkt] / 1000 # [mile], [million/1000] for t in m.time) @m.Expression(doc="million $") def product_fixed_transport_cost(m): - """_summary_ + """ + Calculate the fixed cost of transporting products to the markets based on the total number of active product routes and the fixed transportation cost. Parameters ---------- @@ -646,17 +653,18 @@ def product_fixed_transport_cost(m): Returns ------- - _type_ - _description_ + Pyomo.Expression + Sum of fixed transport costs, accounting for the activation of each route. """ return ( sum(m.product_route_active[site, mkt].binary_indicator_var for site in m.potential_sites for mkt in m.markets) - * m.transport_fixed_cost / 1000) + * m.transport_fixed_cost / 1000) # [thousand $] [million/1000] @m.Expression(m.potential_sites, m.time, doc="Cost of module setups in each month [million $]") def module_setup_cost(m, site, t): - """_summary_ + """ + Calculate the cost of setting up modules at a facility site 'site' at each time period using the unit module cost and the number of modules purchased. Parameters ---------- @@ -669,14 +677,15 @@ def module_setup_cost(m, site, t): Returns ------- - _type_ - _description_ + Pyomo.Expression + Total setup cost considering the quantity of modules purchased and the unit cost per time period. """ return m.modules_purchased[site, t] * m.module_unit_cost[t] @m.Expression(m.potential_sites, m.time, doc="Value of module teardowns in each month [million $]") def module_teardown_credit(m, site, t): - """_summary_ + """ + Calculate the value of tearing down modules at a facility site 'site' at each time period using the unit module cost and the number of modules sold. Parameters ---------- @@ -689,14 +698,15 @@ def module_teardown_credit(m, site, t): Returns ------- - _type_ - _description_ + Pyomo.Expression + Total teardown value considering the quantity of modules sold and the unit cost per time period. """ return m.modules_sold[site, t] * m.module_unit_cost[t] * m.teardown_value @m.Expression(m.potential_sites, doc="Conventional site salvage value") def conv_salvage_value(m, site): - """_summary_ + """ + Calculate the salvage value of a conventional facility site 'site' using the build cost, the discount factor for the last time period, and the conventional salvage value. Parameters ---------- @@ -707,8 +717,8 @@ def conv_salvage_value(m, site): Returns ------- - _type_ - _description_ + Pyomo.Expression + Salvage value of the conventional facility site 'site' considering the build cost, discount factor, and salvage value. """ return m.conv_build_cost[site] * m.discount_factor[m.time.last()] * m.conventional_salvage_value @@ -723,133 +733,140 @@ def conv_salvage_value(m, site): + summation(m.product_transport_cost) + summation(m.product_fixed_transport_cost) + 0, - sense=minimize) + sense=minimize, doc="Total cost [million $]") return m def _build_site_inactive_disjunct(disj, site): - """_summary_ + """ + Configure the disjunct for a facility site marked as inactive. Parameters ---------- disj : Pyomo.Disjunct - _description_ + Pyomo disjunct for inactive site site : int Index of the facility site from 1 to 12 Returns ------- - _type_ - _description_ + None + None, but adds constraints to the disjunct """ m = disj.model() @disj.Constraint() def no_modules(disj): - """_summary_ + """ + Ensure that there are no modules at the inactive site. Parameters ---------- disj : Pyomo.Disjunct - _description_ + The disjunct object defining constraints for the inactive site Returns ------- - _type_ - _description_ + Pyomo.Constraint + The constraint that there are no modules at the inactive site """ return sum(m.num_modules[...]) + sum(m.modules_purchased[...]) + sum(m.modules_sold[...]) == 0 @disj.Constraint() def no_production(disj): - """_summary_ + """ + Ensure that there is no production at the inactive site. Parameters ---------- disj : Pyomo.Disjunct - _description_ + The disjunct object defining constraints for the inactive site Returns ------- - _type_ - _description_ + Pyomo.Constraint + The constraint that there is no production at the inactive site. """ return sum(m.production[site, t] for t in m.time) == 0 @disj.Constraint() def no_supply(disj): - """_summary_ + """ + Ensure that there is no supply at the inactive site. Parameters ---------- disj : Pyomo.Disjunct - _description_ + The disjunct object defining constraints for the inactive site Returns ------- - _type_ - _description_ + Pyomo.Constraint + The constraint that there is no supply at the inactive site. """ return sum(m.supply[site, t] for t in m.time) == 0 def _build_conventional_disjunct(disj, site): - """_summary_ + """ + Configure the disjunct for a conventional facility site. Parameters ---------- disj : Pyomo.Disjunct - _description_ + The disjunct object associated with a conventional site. site : int Index of the facility site from 1 to 12 Returns ------- - _type_ - _description_ + None + None, but adds constraints to the disjunct """ m = disj.model() disj.cost_calc = Constraint( expr=m.conv_build_cost[site] == ( - m.conv_base_cost * (m.conv_site_size[site] / 10) ** m.conv_exponent)) + m.conv_base_cost * (m.conv_site_size[site] / 10) ** m.conv_exponent), doc="the build cost for the conventional facility") # m.bigM[disj.cost_calc] = 7000 @disj.Constraint(m.time) def supply_balance(disj, t): - """_summary_ + """ + Ensure that the supply at the conventional site meets the supply shipments from the suppliers. Parameters ---------- disj : Pyomo.Disjunct - _description_ + The disjunct object for a conventional site. t : int Index of time in months from 0 to 120 (10 years) Returns ------- - _type_ - _description_ + Pyomo.Constraint + A constraint that the total supply at the site during each time period equals the total shipments received. """ return m.supply[site, t] == sum( m.supply_shipments[sup, site, t] for sup in m.suppliers) @disj.Constraint(m.time) def conv_production_limit(conv_disj, t): - """_summary_ + """ + Limit the production at the site based on its capacity. No production is allowed before the setup time. Parameters ---------- conv_disj : Pyomo.Disjunct - _description_ + The disjunct object for a conventional site. t : int Index of time in months from 0 to 120 (10 years) Returns ------- - _type_ - _description_ + Pyomo.Constraint + A constraint that limits production to the site's capacity after setup and prohibits production before setup. """ if t < m.conv_setup_time: return m.production[site, t] == 0 @@ -858,109 +875,116 @@ def conv_production_limit(conv_disj, t): @disj.Constraint() def no_modules(disj): - """_summary_ + """ + Ensure no modular units are present, purchased, or sold at the conventional site. Parameters ---------- disj : Pyomo.Disjunct - _description_ + The disjunct object for a conventional site. Returns ------- - _type_ - _description_ + Pyomo.Constraint + A constraint that the number of modules (present, purchased, sold) at the site is zero. """ return sum(m.num_modules[...]) + sum(m.modules_purchased[...]) + sum(m.modules_sold[...]) == 0 def _build_modular_disjunct(disj, site): - """_summary_ + """ + Configure the disjunct for a modular facility site. Parameters ---------- disj : Pyomo.Disjunct - _description_ + The disjunct object associated with a modular site. site : int Index of the facility site from 1 to 12 Returns ------- - _type_ - _description_ + None + None, but adds constraints to the disjunct """ m = disj.model() @disj.Constraint(m.time) def supply_balance(disj, t): - """_summary_ + """ + Ensure that the supply at the modular site meets the supply shipments from the suppliers. Parameters ---------- disj : Pyomo.Disjunct - _description_ + The disjunct object for a modular site. t : int Index of time in months from 0 to 120 (10 years) Returns ------- - _type_ - _description_ + Pyomo.Constraint + A constraint that the total supply at the site during each time period equals the total shipments received. """ return m.supply[site, t] == sum( m.supply_shipments[sup, site, t] for sup in m.suppliers) @disj.Constraint(m.time) def module_balance(disj, t): - """_summary_ + """ + Ensure that the number of modules at the site is consistent with the number of modules purchased and sold. Parameters ---------- disj : Pyomo.Disjunct - _description_ + The disjunct object for a modular site. t : int Index of time in months from 0 to 120 (10 years) Returns ------- - _type_ - _description_ + Pyomo.Constraint + A constraint that maintains the number of modules based on previous balances, new purchases, and modules sold. """ existing_modules = 0 if t == m.time.first() else m.num_modules[site, t - 1] new_modules = 0 if t < m.modular_setup_time else m.modules_purchased[site, t - m.modular_setup_time] sold_modules = m.modules_sold[site, t] return m.num_modules[site, t] == existing_modules + new_modules - sold_modules + # Fix the number of modules to zero during the setup time for t in range(value(m.modular_setup_time)): m.num_modules[site, t].fix(0) @disj.Constraint(m.time) def modular_production_limit(mod_disj, t): - """_summary_ + """ + Limit the production at the site based on the number of modules present. No production is allowed before the setup time. Parameters ---------- mod_disj : Pyomo.Disjunct - _description_ + The disjunct object for a modular site. t : int Index of time in months from 0 to 120 (10 years) Returns ------- - _type_ - _description_ + Pyomo.Constraint + A constraint that limits production to the site's capacity after setup and prohibits production before setup. """ return m.production[site, t] <= 10 * m.num_modules[site, t] def _build_supply_route_active(disj, sup, site): - """_summary_ + """ + Build the disjunct for an active supply route from a supplier to a facility site. Parameters ---------- disj : Pyomo.Disjunct - _description_ - sup : _type_ - _description_ + The disjunct object for an active supply route + sup : int + Index of the supplier from 1 to 10 site : int Index of the facility site from 1 to 12 """ @@ -968,48 +992,51 @@ def _build_supply_route_active(disj, sup, site): def _build_supply_route_inactive(disj, sup, site): - """_summary_ + """ + Build the disjunct for an inactive supply route from a supplier to a facility site. Parameters ---------- disj : Pyomo.Disjunct - _description_ - sup : _type_ - _description_ + The disjunct object for an inactive supply route + sup : int + Index of the supplier from 1 to 10 site : int Index of the facility site from 1 to 12 Returns ------- - _type_ - _description_ + None + None, but adds constraints to the disjunct """ m = disj.model() @disj.Constraint() def no_supply(disj): - """_summary_ + """ + Ensure that there are no supply shipments from the supplier to the site. Parameters ---------- disj : Pyomo.Disjunct - _description_ + The disjunct object for an inactive supply route Returns ------- - _type_ - _description_ + Pyomo.Constraint + A constraint that there are no supply shipments from the supplier to the site. """ return sum(m.supply_shipments[sup, site, t] for t in m.time) == 0 def _build_product_route_active(disj, site, mkt): - """_summary_ + """ + Build the disjunct for an active product route from a facility site to a market. Parameters ---------- disj : Pyomo.Disjunct - _description_ + The disjunct object for an active product route site : int Index of the facility site from 1 to 12 mkt : int @@ -1019,12 +1046,13 @@ def _build_product_route_active(disj, site, mkt): def _build_product_route_inactive(disj, site, mkt): - """_summary_ + """ + Build the disjunct for an inactive product route from a facility site to a market. Parameters ---------- disj : Pyomo.Disjunct - _description_ + The disjunct object for an inactive product route site : int Index of the facility site from 1 to 12 mkt : int @@ -1032,14 +1060,15 @@ def _build_product_route_inactive(disj, site, mkt): Returns ------- - _type_ - _description_ + None + None, but adds constraints to the disjunct """ m = disj.model() @disj.Constraint() def no_product(disj): - """_summary_ + """ + Ensure that there are no product shipments from the site to the market. Parameters ---------- @@ -1048,8 +1077,8 @@ def no_product(disj): Returns ------- - _type_ - _description_ + Pyomo.Constraint + A constraint that there are no product shipments from the site to the market. """ return sum(m.product_shipments[site, mkt, t] for t in m.time) == 0 From 9c442c6a3486beeaf5b382ffacb73137a841a0f4 Mon Sep 17 00:00:00 2001 From: Albert Lee Date: Tue, 23 Apr 2024 23:50:44 -0400 Subject: [PATCH 63/99] Black Formatting --- gdplib/biofuel/model.py | 431 +++++++++++++++++++++++++++++----------- 1 file changed, 316 insertions(+), 115 deletions(-) diff --git a/gdplib/biofuel/model.py b/gdplib/biofuel/model.py index 0cfd8f1..05eeb16 100644 --- a/gdplib/biofuel/model.py +++ b/gdplib/biofuel/model.py @@ -5,8 +5,22 @@ import pandas as pd from pyomo.environ import ( - ConcreteModel, Constraint, Integers, minimize, NonNegativeReals, Objective, Param, RangeSet, SolverFactory, sqrt, - Suffix, summation, TransformationFactory, value, Var, ) + ConcreteModel, + Constraint, + Integers, + minimize, + NonNegativeReals, + Objective, + Param, + RangeSet, + SolverFactory, + sqrt, + Suffix, + summation, + TransformationFactory, + value, + Var, +) from pyomo.gdp import Disjunct @@ -26,9 +40,9 @@ def build_model(): m = ConcreteModel() m.bigM = Suffix(direction=Suffix.LOCAL) m.time = RangeSet(0, 120, doc="months in 10 years") - m.suppliers = RangeSet(10) # 10 suppliers - m.markets = RangeSet(10) # 10 markets - m.potential_sites = RangeSet(12) # 12 facility sites + m.suppliers = RangeSet(10) # 10 suppliers + m.markets = RangeSet(10) # 10 markets + m.potential_sites = RangeSet(12) # 12 facility sites m.discount_rate = Param(initialize=0.08, doc="discount rate [8%]") m.conv_setup_time = Param(initialize=12) m.modular_setup_time = Param(initialize=3) @@ -58,7 +72,8 @@ def discount_factor(m, t): xls_data = pd.read_excel( os.path.join(os.path.dirname(__file__), "problem_data.xlsx"), sheet_name=["sources", "markets", "sites", "growth", "decay"], - index_col=0) + index_col=0, + ) @m.Param(m.markets, m.time, doc="Market demand [thousand ton/month]") def market_demand(m, mkt, t): @@ -245,8 +260,10 @@ def dist_supplier_to_site(m, sup, site): Pyomo.Parameter The distance in miles between the supplier and the facility site """ - return sqrt((m.supplier_x[sup] - m.site_x[site]) ** 2 + - (m.supplier_y[sup] - m.site_y[site]) ** 2) + return sqrt( + (m.supplier_x[sup] - m.site_x[site]) ** 2 + + (m.supplier_y[sup] - m.site_y[site]) ** 2 + ) @m.Param(m.potential_sites, m.markets, doc="Miles") def dist_site_to_market(m, site, mkt): @@ -267,30 +284,61 @@ def dist_site_to_market(m, site, mkt): Pyomo.Parameter The distance in miles between the facility site and the market """ - return sqrt((m.site_x[site] - m.market_x[mkt]) ** 2 + - (m.site_y[site] - m.market_y[mkt]) ** 2) + return sqrt( + (m.site_x[site] - m.market_x[mkt]) ** 2 + + (m.site_y[site] - m.market_y[mkt]) ** 2 + ) m.conversion = Param(initialize=0.26, doc="overall conversion to product") m.conv_site_size = Var( m.potential_sites, - bounds=(120 / 12 / 10, 120 / 12), initialize=1, - doc="Product capacity of site [thousand ton/mo]") + bounds=(120 / 12 / 10, 120 / 12), + initialize=1, + doc="Product capacity of site [thousand ton/mo]", + ) - m.conv_base_cost = Param(initialize=268.4, doc="Cost for size 120k per year [million $]") - m.module_base_cost = Param(initialize=268.4, doc="Cost for size 120k per year [million $]") + m.conv_base_cost = Param( + initialize=268.4, doc="Cost for size 120k per year [million $]" + ) + m.module_base_cost = Param( + initialize=268.4, doc="Cost for size 120k per year [million $]" + ) m.conv_exponent = Param(initialize=0.7) - m.supply = Var(m.potential_sites, m.time, bounds=(0, 120 / 12 / 0.26 * 10), doc="thousand ton/mo") - m.production = Var(m.potential_sites, m.time, bounds=(0, 120 / 12 * 10), doc="thousand ton/mo") - m.num_modules = Var(m.potential_sites, m.time, domain=Integers, bounds=(0, 10), doc="Number of modules") - m.modules_purchased = Var(m.potential_sites, m.time, domain=Integers, bounds=(0, 10), doc="Modules purchased") - m.modules_sold = Var(m.potential_sites, m.time, domain=Integers, bounds=(0, 10), doc="Modules sold") + m.supply = Var( + m.potential_sites, + m.time, + bounds=(0, 120 / 12 / 0.26 * 10), + doc="thousand ton/mo", + ) + m.production = Var( + m.potential_sites, m.time, bounds=(0, 120 / 12 * 10), doc="thousand ton/mo" + ) + m.num_modules = Var( + m.potential_sites, + m.time, + domain=Integers, + bounds=(0, 10), + doc="Number of modules", + ) + m.modules_purchased = Var( + m.potential_sites, + m.time, + domain=Integers, + bounds=(0, 10), + doc="Modules purchased", + ) + m.modules_sold = Var( + m.potential_sites, m.time, domain=Integers, bounds=(0, 10), doc="Modules sold" + ) m.conv_build_cost = Var( m.potential_sites, doc="Cost of building conventional facility [milllion $]", - bounds=(0, 1350 * 10), initialize=0) + bounds=(0, 1350 * 10), + initialize=0, + ) @m.Param(m.suppliers, m.time) def raw_material_unit_cost(m, sup, t): @@ -407,11 +455,21 @@ def unit_raw_material_transport_cost(m, t): return 2 * m.discount_factor[t] m.supply_shipments = Var( - m.suppliers, m.potential_sites, m.time, domain=NonNegativeReals, - bounds=(0, 120 / 12 / 0.26), doc="thousand ton/mo") + m.suppliers, + m.potential_sites, + m.time, + domain=NonNegativeReals, + bounds=(0, 120 / 12 / 0.26), + doc="thousand ton/mo", + ) m.product_shipments = Var( - m.potential_sites, m.markets, m.time, domain=NonNegativeReals, - bounds=(0, 120 / 12), doc="thousand ton/mo") + m.potential_sites, + m.markets, + m.time, + domain=NonNegativeReals, + bounds=(0, 120 / 12), + doc="thousand ton/mo", + ) @m.Constraint(m.suppliers, m.time) def supply_limits(m, sup, t): @@ -432,8 +490,10 @@ def supply_limits(m, sup, t): Pyomo.Constraint The total supply from the supplier 'sup' at time 't' should not exceed the available supply from the supplier. """ - return sum(m.supply_shipments[sup, site, t] - for site in m.potential_sites) <= m.available_supply[sup, t] + return ( + sum(m.supply_shipments[sup, site, t] for site in m.potential_sites) + <= m.available_supply[sup, t] + ) @m.Constraint(m.markets, m.time) def demand_satisfaction(m, mkt, t): @@ -454,8 +514,10 @@ def demand_satisfaction(m, mkt, t): Pyomo.Constraint The total product shipments to the market 'mkt' at time 't' should meet the market demand for the product. """ - return sum(m.product_shipments[site, mkt, t] - for site in m.potential_sites) == m.market_demand[mkt, t] + return ( + sum(m.product_shipments[site, mkt, t] for site in m.potential_sites) + == m.market_demand[mkt, t] + ) @m.Constraint(m.potential_sites, m.time) def product_balance(m, site, t): @@ -476,8 +538,9 @@ def product_balance(m, site, t): Pyomo.Constraint The total product shipments from the facility site 'site' at time 't' should meet the production from the site. """ - return m.production[site, t] == sum(m.product_shipments[site, mkt, t] - for mkt in m.markets) + return m.production[site, t] == sum( + m.product_shipments[site, mkt, t] for mkt in m.markets + ) @m.Constraint(m.potential_sites, m.time) def require_raw_materials(m, site, t): @@ -500,15 +563,45 @@ def require_raw_materials(m, site, t): """ return m.production[site, t] <= m.conversion * m.supply[site, t] - m.modular = Disjunct(m.potential_sites, rule=_build_modular_disjunct, doc="Disjunct for modular site") - m.conventional = Disjunct(m.potential_sites, rule=_build_conventional_disjunct, doc="Disjunct for conventional site") - m.site_inactive = Disjunct(m.potential_sites, rule=_build_site_inactive_disjunct, doc="Disjunct for inactive site") + m.modular = Disjunct( + m.potential_sites, rule=_build_modular_disjunct, doc="Disjunct for modular site" + ) + m.conventional = Disjunct( + m.potential_sites, + rule=_build_conventional_disjunct, + doc="Disjunct for conventional site", + ) + m.site_inactive = Disjunct( + m.potential_sites, + rule=_build_site_inactive_disjunct, + doc="Disjunct for inactive site", + ) - m.supply_route_active = Disjunct(m.suppliers, m.potential_sites, rule=_build_supply_route_active, doc="Disjunct for active supply route") - m.supply_route_inactive = Disjunct(m.suppliers, m.potential_sites, rule=_build_supply_route_inactive, doc="Disjunct for inactive supply route") + m.supply_route_active = Disjunct( + m.suppliers, + m.potential_sites, + rule=_build_supply_route_active, + doc="Disjunct for active supply route", + ) + m.supply_route_inactive = Disjunct( + m.suppliers, + m.potential_sites, + rule=_build_supply_route_inactive, + doc="Disjunct for inactive supply route", + ) - m.product_route_active = Disjunct(m.potential_sites, m.markets, rule=_build_product_route_active, doc="Disjunct for active product route") - m.product_route_inactive = Disjunct(m.potential_sites, m.markets, rule=_build_product_route_inactive, doc="Disjunct for inactive product route") + m.product_route_active = Disjunct( + m.potential_sites, + m.markets, + rule=_build_product_route_active, + doc="Disjunct for active product route", + ) + m.product_route_inactive = Disjunct( + m.potential_sites, + m.markets, + rule=_build_product_route_inactive, + doc="Disjunct for inactive product route", + ) @m.Disjunction(m.potential_sites) def site_type(m, site): @@ -591,10 +684,12 @@ def raw_material_transport_cost(m, sup, site): Total transportation cost considering the quantity of shipments, unit cost per time period, and distance between suppliers and sites. """ return sum( - m.supply_shipments[sup, site, t] # [1000 ton/month] - * m.unit_raw_material_transport_cost[t] # [$/ton-mile] - * m.dist_supplier_to_site[sup, site] / 1000 # [mile], [million/1000] - for t in m.time) + m.supply_shipments[sup, site, t] # [1000 ton/month] + * m.unit_raw_material_transport_cost[t] # [$/ton-mile] + * m.dist_supplier_to_site[sup, site] + / 1000 # [mile], [million/1000] + for t in m.time + ) @m.Expression(doc="million $") def raw_material_fixed_transport_cost(m): @@ -612,9 +707,14 @@ def raw_material_fixed_transport_cost(m): Sum of fixed transport costs, accounting for the activation of each route. """ return ( - sum(m.supply_route_active[sup, site].binary_indicator_var - for sup in m.suppliers for site in m.potential_sites) - * m.transport_fixed_cost / 1000) # [thousand $] [million/1000] + sum( + m.supply_route_active[sup, site].binary_indicator_var + for sup in m.suppliers + for site in m.potential_sites + ) + * m.transport_fixed_cost + / 1000 + ) # [thousand $] [million/1000] @m.Expression(m.potential_sites, m.markets, doc="million $") def product_transport_cost(m, site, mkt): @@ -636,10 +736,12 @@ def product_transport_cost(m, site, mkt): Total transportation cost considering the quantity of shipments, unit cost per time period, and distance between sites and markets. """ return sum( - m.product_shipments[site, mkt, t] # [1000 ton/month] - * m.unit_product_transport_cost[t] # [$/ton-mile] - * m.dist_site_to_market[site, mkt] / 1000 # [mile], [million/1000] - for t in m.time) + m.product_shipments[site, mkt, t] # [1000 ton/month] + * m.unit_product_transport_cost[t] # [$/ton-mile] + * m.dist_site_to_market[site, mkt] + / 1000 # [mile], [million/1000] + for t in m.time + ) @m.Expression(doc="million $") def product_fixed_transport_cost(m): @@ -657,11 +759,18 @@ def product_fixed_transport_cost(m): Sum of fixed transport costs, accounting for the activation of each route. """ return ( - sum(m.product_route_active[site, mkt].binary_indicator_var - for site in m.potential_sites for mkt in m.markets) - * m.transport_fixed_cost / 1000) # [thousand $] [million/1000] - - @m.Expression(m.potential_sites, m.time, doc="Cost of module setups in each month [million $]") + sum( + m.product_route_active[site, mkt].binary_indicator_var + for site in m.potential_sites + for mkt in m.markets + ) + * m.transport_fixed_cost + / 1000 + ) # [thousand $] [million/1000] + + @m.Expression( + m.potential_sites, m.time, doc="Cost of module setups in each month [million $]" + ) def module_setup_cost(m, site, t): """ Calculate the cost of setting up modules at a facility site 'site' at each time period using the unit module cost and the number of modules purchased. @@ -682,7 +791,11 @@ def module_setup_cost(m, site, t): """ return m.modules_purchased[site, t] * m.module_unit_cost[t] - @m.Expression(m.potential_sites, m.time, doc="Value of module teardowns in each month [million $]") + @m.Expression( + m.potential_sites, + m.time, + doc="Value of module teardowns in each month [million $]", + ) def module_teardown_credit(m, site, t): """ Calculate the value of tearing down modules at a facility site 'site' at each time period using the unit module cost and the number of modules sold. @@ -720,7 +833,11 @@ def conv_salvage_value(m, site): Pyomo.Expression Salvage value of the conventional facility site 'site' considering the build cost, discount factor, and salvage value. """ - return m.conv_build_cost[site] * m.discount_factor[m.time.last()] * m.conventional_salvage_value + return ( + m.conv_build_cost[site] + * m.discount_factor[m.time.last()] + * m.conventional_salvage_value + ) m.total_cost = Objective( expr=0 @@ -733,7 +850,9 @@ def conv_salvage_value(m, site): + summation(m.product_transport_cost) + summation(m.product_fixed_transport_cost) + 0, - sense=minimize, doc="Total cost [million $]") + sense=minimize, + doc="Total cost [million $]", + ) return m @@ -771,7 +890,12 @@ def no_modules(disj): Pyomo.Constraint The constraint that there are no modules at the inactive site """ - 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 + ) @disj.Constraint() def no_production(disj): @@ -827,8 +951,10 @@ def _build_conventional_disjunct(disj, site): m = disj.model() disj.cost_calc = Constraint( - expr=m.conv_build_cost[site] == ( - m.conv_base_cost * (m.conv_site_size[site] / 10) ** m.conv_exponent), doc="the build cost for the conventional facility") + expr=m.conv_build_cost[site] + == (m.conv_base_cost * (m.conv_site_size[site] / 10) ** m.conv_exponent), + doc="the build cost for the conventional facility", + ) # m.bigM[disj.cost_calc] = 7000 @disj.Constraint(m.time) @@ -849,7 +975,8 @@ def supply_balance(disj, t): A constraint that the total supply at the site during each time period equals the total shipments received. """ return m.supply[site, t] == sum( - m.supply_shipments[sup, site, t] for sup in m.suppliers) + m.supply_shipments[sup, site, t] for sup in m.suppliers + ) @disj.Constraint(m.time) def conv_production_limit(conv_disj, t): @@ -888,7 +1015,12 @@ def no_modules(disj): Pyomo.Constraint A constraint that the number of modules (present, purchased, sold) at the site is zero. """ - 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(disj, site): @@ -927,7 +1059,8 @@ def supply_balance(disj, t): A constraint that the total supply at the site during each time period equals the total shipments received. """ return m.supply[site, t] == sum( - m.supply_shipments[sup, site, t] for sup in m.suppliers) + m.supply_shipments[sup, site, t] for sup in m.suppliers + ) @disj.Constraint(m.time) def module_balance(disj, t): @@ -947,7 +1080,11 @@ def module_balance(disj, t): A constraint that maintains the number of modules based on previous balances, new purchases, and modules sold. """ existing_modules = 0 if t == m.time.first() else m.num_modules[site, t - 1] - new_modules = 0 if t < m.modular_setup_time else m.modules_purchased[site, t - m.modular_setup_time] + new_modules = ( + 0 + if t < m.modular_setup_time + else m.modules_purchased[site, t - m.modular_setup_time] + ) sold_modules = m.modules_sold[site, t] return m.num_modules[site, t] == existing_modules + new_modules - sold_modules @@ -1107,45 +1244,72 @@ def print_nonzeros(var): TransformationFactory('gdp.bigm').apply_to(m, bigM=7000) # res = SolverFactory('gurobi').solve(m, tee=True) res = SolverFactory('gams').solve( - m, tee=True, + m, + tee=True, solver='scip', # solver='gurobi', # add_options=['option reslim = 1200;', 'option optcr=0.0001;'], - add_options=[ - 'option reslim = 1200;', - 'OPTION threads=4;', - 'option optcr=0.01', - ], - ) + add_options=['option reslim = 1200;', 'OPTION threads=4;', 'option optcr=0.01'], + ) # res = SolverFactory('gdpopt').solve( # m, tee=True, # iterlim=2, # mip_solver='gams', # mip_solver_args=dict(add_options=['option reslim = 30;'])) - results = pd.DataFrame([ - ['Total Cost', value(m.total_cost)], - ['Conv Build Cost', value(summation(m.conv_build_cost))], - ['Conv Salvage Value', value(summation(m.conv_salvage_value))], - ['Module Build Cost', value(summation(m.module_setup_cost))], - ['Module Salvage Value', value(summation(m.module_teardown_credit))], - ['Raw Material Transport', value(summation(m.raw_material_transport_cost) + summation(m.raw_material_fixed_transport_cost))], - ['Product Transport', value(summation(m.product_transport_cost) + summation(m.product_fixed_transport_cost))] - ], columns=['Quantity', 'Value [million $]']).set_index('Quantity').round(0) + results = ( + pd.DataFrame( + [ + ['Total Cost', value(m.total_cost)], + ['Conv Build Cost', value(summation(m.conv_build_cost))], + ['Conv Salvage Value', value(summation(m.conv_salvage_value))], + ['Module Build Cost', value(summation(m.module_setup_cost))], + ['Module Salvage Value', value(summation(m.module_teardown_credit))], + [ + 'Raw Material Transport', + value( + summation(m.raw_material_transport_cost) + + summation(m.raw_material_fixed_transport_cost) + ), + ], + [ + 'Product Transport', + value( + summation(m.product_transport_cost) + + summation(m.product_fixed_transport_cost) + ), + ], + ], + columns=['Quantity', 'Value [million $]'], + ) + .set_index('Quantity') + .round(0) + ) print(results) - df = pd.DataFrame([ + df = pd.DataFrame( [ - site, t, - value(m.num_modules[site, t]), - value(m.modules_purchased[site, t]), - value(m.modules_sold[site, t]), - value(m.module_setup_cost[site, t]), - value(m.module_teardown_credit[site, t]), - value(m.production[site, t])] for site, t in m.potential_sites * m.time + [ + site, + t, + value(m.num_modules[site, t]), + value(m.modules_purchased[site, t]), + value(m.modules_sold[site, t]), + value(m.module_setup_cost[site, t]), + value(m.module_teardown_credit[site, t]), + value(m.production[site, t]), + ] + for site, t in m.potential_sites * m.time ], - columns=("Site", "Month", "Num Modules", "Buy Modules", - "Sell Modules", - "Setup Cost", "Teardown Credit", "Production") + columns=( + "Site", + "Month", + "Num Modules", + "Buy Modules", + "Sell Modules", + "Setup Cost", + "Teardown Credit", + "Production", + ), ) df.to_excel("facility_config.xlsx") @@ -1153,42 +1317,79 @@ def print_nonzeros(var): # exit() import matplotlib.pyplot as plt - plt.plot([x for x in m.site_x.values()], - [y for y in m.site_y.values()], 'k.', markersize=12) - plt.plot([x for x in m.market_x.values()], - [y for y in m.market_y.values()], 'b.', markersize=12) - plt.plot([x for x in m.supplier_x.values()], - [y for y in m.supplier_y.values()], 'r.', markersize=12) + plt.plot( + [x for x in m.site_x.values()], + [y for y in m.site_y.values()], + 'k.', + markersize=12, + ) + plt.plot( + [x for x in m.market_x.values()], + [y for y in m.market_y.values()], + 'b.', + markersize=12, + ) + plt.plot( + [x for x in m.supplier_x.values()], + [y for y in m.supplier_y.values()], + 'r.', + markersize=12, + ) for mkt in m.markets: - plt.annotate('m%s' % mkt, (m.market_x[mkt], m.market_y[mkt]), - (m.market_x[mkt] + 2, m.market_y[mkt] + 2), - fontsize='x-small') + plt.annotate( + 'm%s' % mkt, + (m.market_x[mkt], m.market_y[mkt]), + (m.market_x[mkt] + 2, m.market_y[mkt] + 2), + fontsize='x-small', + ) for site in m.potential_sites: if m.site_inactive[site].binary_indicator_var.value == 0: plt.annotate( - 'p%s' % site, (m.site_x[site], m.site_y[site]), + 'p%s' % site, + (m.site_x[site], m.site_y[site]), (m.site_x[site] + 2, m.site_y[site] + 2), - fontsize='x-small') + fontsize='x-small', + ) else: plt.annotate( - 'x%s' % site, (m.site_x[site], m.site_y[site]), + 'x%s' % site, + (m.site_x[site], m.site_y[site]), (m.site_x[site] + 2, m.site_y[site] + 2), - fontsize='x-small') + fontsize='x-small', + ) for sup in m.suppliers: plt.annotate( - 's%s' % sup, (m.supplier_x[sup], m.supplier_y[sup]), + 's%s' % sup, + (m.supplier_x[sup], m.supplier_y[sup]), (m.supplier_x[sup] + 2, m.supplier_y[sup] + 2), - fontsize='x-small') + fontsize='x-small', + ) for sup, site in m.suppliers * m.potential_sites: - if fabs(m.supply_route_active[sup, site].binary_indicator_var.value - 1) <= 1E-3: - plt.arrow(m.supplier_x[sup], m.supplier_y[sup], - m.site_x[site] - m.supplier_x[sup], - m.site_y[site] - m.supplier_y[sup], - width=0.8, length_includes_head=True, color='b') + if ( + fabs(m.supply_route_active[sup, site].binary_indicator_var.value - 1) + <= 1e-3 + ): + plt.arrow( + m.supplier_x[sup], + m.supplier_y[sup], + m.site_x[site] - m.supplier_x[sup], + m.site_y[site] - m.supplier_y[sup], + width=0.8, + length_includes_head=True, + color='b', + ) for site, mkt in m.potential_sites * m.markets: - if fabs(m.product_route_active[site, mkt].binary_indicator_var.value - 1) <= 1E-3: - plt.arrow(m.site_x[site], m.site_y[site], - m.market_x[mkt] - m.site_x[site], - m.market_y[mkt] - m.site_y[site], - width=0.8, length_includes_head=True, color='r') + if ( + fabs(m.product_route_active[site, mkt].binary_indicator_var.value - 1) + <= 1e-3 + ): + plt.arrow( + m.site_x[site], + m.site_y[site], + m.market_x[mkt] - m.site_x[site], + m.market_y[mkt] - m.site_y[site], + width=0.8, + length_includes_head=True, + color='r', + ) plt.show() From a43dcb20f1c58647b5050e16f3e126744945c80c Mon Sep 17 00:00:00 2001 From: Albert Lee Date: Wed, 24 Apr 2024 12:12:33 -0400 Subject: [PATCH 64/99] Reform the documentation of cafaro_approx.py into NumPy Style. --- gdplib/mod_hens/cafaro_approx.py | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/gdplib/mod_hens/cafaro_approx.py b/gdplib/mod_hens/cafaro_approx.py index 89d4491..28e4518 100644 --- a/gdplib/mod_hens/cafaro_approx.py +++ b/gdplib/mod_hens/cafaro_approx.py @@ -1,7 +1,7 @@ """Cafaro approximation parameter estimation. Rather than use the cost relation (1), Cafaro & Grossmann, 2014 (DOI: -10.1016/j.compchemeng.2013.10.001) proposes using (2), which has much better +https://doi.org/10.1016/j.compchemeng.2013.10.001) proposes using (2), which has much better behaved derivative values near x=0. However, we need to use parameter estimation in order to derive the correct values of k and b. @@ -17,7 +17,8 @@ def calculate_cafaro_coefficients(area1, area2, exponent): - """Calculate the coefficients for the Cafaro approximation. + """ + Calculate the coefficients for the Cafaro approximation. Gives the coefficients k and b to approximate a function x^exponent such that at the given areas, the following relations apply: @@ -25,10 +26,23 @@ def calculate_cafaro_coefficients(area1, area2, exponent): area1 ^ exponent = k * ln(b * area1 + 1) area2 ^ exponent = k * ln(b * area2 + 1) - Args: - area1 (float): area to use as the first regression point - area2 (float): area to use as the second regression point - exponent (float): exponent to approximate + Parameters + ---------- + area1 : float + The area to use as the first regression point. + area2 : float + The area to use as the second regression point. + exponent : float + The exponent to approximate. + + Returns + ------- + 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 """ m = ConcreteModel() m.k = Var(domain=NonNegativeReals) From 99f9460e25aac343f0af9fa472c303ddf51fdf5a Mon Sep 17 00:00:00 2001 From: Albert Lee Date: Thu, 25 Apr 2024 12:12:52 -0400 Subject: [PATCH 65/99] Set up parameters documentatioin on common.py --- gdplib/mod_hens/common.py | 75 ++++++++++++++++++++++++++------- gdplib/mod_hens/conventional.py | 14 ++++++ 2 files changed, 73 insertions(+), 16 deletions(-) diff --git a/gdplib/mod_hens/common.py b/gdplib/mod_hens/common.py index c3203c7..e6c0634 100644 --- a/gdplib/mod_hens/common.py +++ b/gdplib/mod_hens/common.py @@ -17,21 +17,36 @@ def build_model(use_cafaro_approximation, num_stages): - """Build the model.""" + """ + Constructs a Pyomo concrete model for heat integration optimization. This model incorporates various components including process and utility streams, heat exchangers, and stages of heat exchange, with optional application of the Cafaro approximation for certain calculations. + + Parameters + ---------- + use_cafaro_approximation : bool + A Boolean flag indicating whether the Cafaro approximation method should be used + to calculate certain coefficients in the model + num_stages : int + The number of stages in the heat exchange model. + + Returns + ------- + Pyomo.ConcreteModel + A Pyomo concrete model representing the heat exchange model based on the specified number of stages and the use of Cafaro approximation, if applicable. The model is ready to be solved using an optimization solver to determine optimal heat integration strategies. + """ m = ConcreteModel() - m.hot_process_streams = Set(initialize=['H1', 'H2']) - m.cold_process_streams = Set(initialize=['C1', 'C2']) - m.process_streams = m.hot_process_streams | m.cold_process_streams - m.hot_utility_streams = Set(initialize=['steam']) - m.cold_utility_streams = Set(initialize=['water']) + m.hot_process_streams = Set(initialize=['H1', 'H2'], doc="Hot process streams") + m.cold_process_streams = Set(initialize=['C1', 'C2'], doc="Cold process streams") + m.process_streams = m.hot_process_streams | m.cold_process_streams # All process streams + m.hot_utility_streams = Set(initialize=['steam'], doc="Hot utility streams") + m.cold_utility_streams = Set(initialize=['water'], doc="Cold utility streams") m.hot_streams = Set( - initialize=m.hot_process_streams | m.hot_utility_streams) + initialize=m.hot_process_streams | m.hot_utility_streams, doc="Hot streams") m.cold_streams = Set( - initialize=m.cold_process_streams | m.cold_utility_streams) + initialize=m.cold_process_streams | m.cold_utility_streams, doc="Cold streams") m.utility_streams = Set( - initialize=m.hot_utility_streams | m.cold_utility_streams) + initialize=m.hot_utility_streams | m.cold_utility_streams, doc="Utility streams") m.streams = Set( - initialize=m.process_streams | m.utility_streams) + initialize=m.process_streams | m.utility_streams, doc="All streams") m.valid_matches = Set( initialize=(m.hot_process_streams * m.cold_streams) | (m.hot_utility_streams * m.cold_process_streams), @@ -42,7 +57,7 @@ def build_model(use_cafaro_approximation, num_stages): # Unused right now, but could be used for variable bound tightening # in the LMTD calculation. - m.stages = RangeSet(num_stages) + m.stages = RangeSet(num_stages, doc="Number of stages") m.T_in = Param( m.streams, doc="Inlet temperature of stream [K]", @@ -80,13 +95,13 @@ def build_model(use_cafaro_approximation, num_stages): domain=NonNegativeReals, initialize=1, bounds=(0, 5000)) m.stage_entry_T = Var( m.streams, m.stages, - doc="Temperature of stream at stage entry.", + doc="Temperature of stream at stage entry [K].", initialize=350, bounds=(293, 450) # TODO set to be equal to min and max temps ) m.stage_exit_T = Var( m.streams, m.stages, - doc="Temperature of stream at stage exit.", + doc="Temperature of stream at stage exit [K].", initialize=350, bounds=(293, 450) # TODO set to be equal to min and max temps ) @@ -117,7 +132,7 @@ def build_model(use_cafaro_approximation, num_stages): doc="Annual unit cost of utilities [$/kW]", initialize={'steam': 80, 'water': 20}) - m.module_sizes = Set(initialize=[10, 50, 100]) + m.module_sizes = Set(initialize=[10, 50, 100], doc="Available module sizes.") m.max_num_modules = Param(m.module_sizes, initialize={ # 5: 100, 10: 50, @@ -127,14 +142,14 @@ def build_model(use_cafaro_approximation, num_stages): }, doc="maximum number of each module size available.") m.exchanger_fixed_unit_cost = Param( - m.valid_matches, default=2000) + m.valid_matches, default=2000, doc="exchanger fixed cost [$/kW]",) m.exchanger_area_cost_factor = Param( m.valid_matches, default=1000, initialize={ ('steam', cold): 1200 for cold in m.cold_process_streams}, doc="1200 for heaters. 1000 for all other exchangers.") - m.area_cost_exponent = Param(default=0.6) + m.area_cost_exponent = Param(default=0.6, doc="Area cost exponent.") if use_cafaro_approximation: k, b = calculate_cafaro_coefficients(10, 500, m.area_cost_exponent) @@ -144,6 +159,24 @@ def build_model(use_cafaro_approximation, num_stages): @m.Param(m.valid_matches, m.module_sizes, doc="Area cost factor for modular exchangers.") def module_area_cost_factor(m, hot, cold, area): + """_summary_ + + Parameters + ---------- + m : Pyomo.ConcreteModel + + hot : _type_ + _description_ + cold : _type_ + _description_ + area : _type_ + _description_ + + Returns + ------- + _type_ + _description_ + """ if hot == 'steam': return 1300 else: @@ -397,6 +430,16 @@ def utility_cost(m, strm): def _fix_and_bound(var, val): + """ + Fix a Pyomo variable to a value and set bounds to that value. + + Parameters + ---------- + var : Pyomo.Var + The Pyomo variable to be fixed. + val : float + The value to fix the variable to. This value will also be used to set both the lower and upper bounds of the variable. + """ var.fix(val) var.setlb(val) var.setub(val) diff --git a/gdplib/mod_hens/conventional.py b/gdplib/mod_hens/conventional.py index 00046a9..9550d10 100644 --- a/gdplib/mod_hens/conventional.py +++ b/gdplib/mod_hens/conventional.py @@ -15,6 +15,20 @@ def build_conventional(cafaro_approx, num_stages): + """_summary_ + + Parameters + ---------- + cafaro_approx : _type_ + _description_ + num_stages : _type_ + _description_ + + Returns + ------- + _type_ + _description_ + """ return build_model(cafaro_approx, num_stages) From a57b32dbcf3a4a3797ee515bd9e3c66ad0eeced0 Mon Sep 17 00:00:00 2001 From: Albert Lee Date: Thu, 25 Apr 2024 12:25:30 -0400 Subject: [PATCH 66/99] Revert Back into Carolina's reference documentation and black formatting. --- gdplib/pyomo_examples/med_term_purchasing.py | 22 ++++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/gdplib/pyomo_examples/med_term_purchasing.py b/gdplib/pyomo_examples/med_term_purchasing.py index c2a0bab..47af318 100644 --- a/gdplib/pyomo_examples/med_term_purchasing.py +++ b/gdplib/pyomo_examples/med_term_purchasing.py @@ -34,18 +34,18 @@ def build_model(): """ - Build a Pyomo abstract model for the medium-term purchasing contracts problem. + Build a Pyomo abstract model for the medium-term purchasing contracts problem. - Returns - ------- - Pyomo.AbstractModel - Pyomo abstract model for medium-term purchasing contracts problem. - - References - ---------- - [1] Vecchietti, Aldo, and I. Grossmann. "Computational experience with logmip solving linear and nonlinear disjunctive programming problems." In Proc. of FOCAPD, pp. 587-590. 2004. - [2] Vecchietti, A., S. Lee and I.E. Grossmann, “Modeling of Discrete/Continuous Optimization Problems: Characterization and Formulation of Disjunctions and their Relaxations,” Computers and Chemical Engineering 27, 433-448 (2003). https://doi.org/10.1016/S0098-1354(02)00220-X - [3] Park, M., Park, S., Mele, F. D., & Grossmann, I. E. (2006). Modeling of purchase and sales contracts in supply chain optimization. Industrial and Engineering Chemistry Research, 45(14), 5013–5026. https://doi.org/10.1021/ie0513144 + Returns + ------- + Pyomo.AbstractModel + Pyomo abstract model for medium-term purchasing contracts problem. + + References + ---------- + [1] Vecchietti, A., & Grossmann, I. (2004). Computational experience with logmip solving linear and nonlinear disjunctive programming problems. Proc. of FOCAPD, 587–590. + [2] Vecchietti, A., Lee, S., & Grossmann, I. E. (2003). Modeling of discrete/continuous optimization problems: characterization and formulation of disjunctions and their relaxations. Computers & Chemical Engineering, 27(3), 433–448. DOI: 10.1016/S0098-1354(02)00220-X + [3] Park, M., Park, S., Mele, F. D., & Grossmann, I. E. (2006). Modeling of purchase and sales contracts in supply chain optimization. Industrial and Engineering Chemistry Research, 45(14), 5013–5026. DOI: 10.1021/ie0513144 """ model = AbstractModel() From 56ff1d84f3fca9c289fd02a02b7bff2dca507717 Mon Sep 17 00:00:00 2001 From: Albert Lee Date: Thu, 25 Apr 2024 23:12:51 -0400 Subject: [PATCH 67/99] Update models to account for Pyomo logical expression system and Boolean indicator_vars --- gdplib/mod_hens/common.py | 300 ++++++++++++++++++++++++++++++++++---- 1 file changed, 273 insertions(+), 27 deletions(-) diff --git a/gdplib/mod_hens/common.py b/gdplib/mod_hens/common.py index e6c0634..3177227 100644 --- a/gdplib/mod_hens/common.py +++ b/gdplib/mod_hens/common.py @@ -151,7 +151,7 @@ def build_model(use_cafaro_approximation, num_stages): doc="1200 for heaters. 1000 for all other exchangers.") m.area_cost_exponent = Param(default=0.6, doc="Area cost exponent.") - if use_cafaro_approximation: + if use_cafaro_approximation: # Use Cafaro approximation for coefficients if True k, b = calculate_cafaro_coefficients(10, 500, m.area_cost_exponent) m.cafaro_k = Param(default=k) m.cafaro_b = Param(default=b) @@ -159,35 +159,55 @@ def build_model(use_cafaro_approximation, num_stages): @m.Param(m.valid_matches, m.module_sizes, doc="Area cost factor for modular exchangers.") def module_area_cost_factor(m, hot, cold, area): - """_summary_ + """ + Determines the area cost factor for modular exchangers within the heat integration model. The cost factor is based on the specified module size and stream pair, with different values for steam and other hot streams. The unit is [$/(m^2)^0.6]. Parameters ---------- m : Pyomo.ConcreteModel - - hot : _type_ - _description_ - cold : _type_ - _description_ - area : _type_ - _description_ + A Pyomo concrete model representing the heat exchange model. + hot : str + The index for the hot stream involved in the heat exchanger. + cold : str + The index for the cold stream involved in the heat exchanger. + area : float + The modular area size of the heat exchanger. Returns ------- - _type_ - _description_ + Pyomo.Parameter + The area cost factor for the specified module size and stream pair. It returns a higher value for steam (1300) compared to other hot streams (1100), reflecting specific cost adjustments based on utility type. """ if hot == 'steam': return 1300 else: return 1100 - m.module_fixed_unit_cost = Param(default=0) - m.module_area_cost_exponent = Param(default=0.6) + m.module_fixed_unit_cost = Param(default=0, doc="Fixed cost for a module.") + m.module_area_cost_exponent = Param(default=0.6, doc="Area cost exponent.") @m.Param(m.valid_matches, m.module_sizes, doc="Cost of a module with a particular area.") def module_area_cost(m, hot, cold, area): + """ + Determines the cost of a module with a specified area size for a given hot and cold stream pair. The cost is calculated based on the area cost factor and exponent for the module size and stream pair. The unit is [$]. + + Parameters + ---------- + m : Pyomo.ConcreteModel + A Pyomo concrete model representing the heat exchange model. + hot : str + The index for the hot stream involved in the heat exchanger. + cold : str + The index for the cold stream involved in the heat exchanger. + area : float + The modular area size of the heat exchanger. + + Returns + ------- + Pyomo.Parameter + The cost of a module with the specified area size for the given hot and cold stream pair. The cost is calculated based on the area cost factor and exponent for the module size and stream pair. + """ return (m.module_area_cost_factor[hot, cold, area] * area ** m.module_area_cost_exponent) @@ -198,18 +218,18 @@ def module_area_cost(m, hot, cold, area): ('steam', cold): 1.2 for cold in m.cold_process_streams}, doc="Overall heat transfer coefficient." - "1.2 for heaters. 0.8 for everything else.") + "1.2 for heaters. 0.8 for everything else. The unit is [kW/m^2/K].") m.exchanger_hot_side_approach_T = Var( m.valid_matches, m.stages, doc="Temperature difference between the hot stream inlet and cold " - "stream outlet of the exchanger.", + "stream outlet of the exchanger. The unit is [K].", bounds=(0.1, 500), initialize=10 ) m.exchanger_cold_side_approach_T = Var( m.valid_matches, m.stages, doc="Temperature difference between the hot stream outlet and cold " - "stream inlet of the exchanger.", + "stream inlet of the exchanger. The unit is [K].", bounds=(0.1, 500), initialize=10 ) m.LMTD = Var( @@ -252,18 +272,63 @@ def module_area_cost(m, hot, cold, area): @m.Constraint(m.hot_process_streams) def overall_hot_stream_heat_balance(m, strm): + """ + Enforces the heat balance for a hot process stream within the model. This constraint ensures that the total heat loss from the hot stream equals the sum of heat transferred to all paired cold streams across all stages. The heat loss is calculated based on the temperature difference between the stream outlet and inlet, multiplied by the overall flow times heat capacity of the stream. + + Parameters + ---------- + m : Pyomo.ConcreteModel + A Pyomo concrete model representing the heat exchange model. + strm : str + The index for the hot stream involved in the heat exchanger. + + Returns + ------- + Pyomo.Constraint + A constraint object that ensures the heat balance across the specified hot stream over all stages and cold stream interactions. + """ return (m.T_in[strm] - m.T_out[strm]) * m.overall_FCp[strm] == ( sum(m.heat_exchanged[strm, cold, stg] for cold in m.cold_streams for stg in m.stages)) @m.Constraint(m.cold_process_streams) def overall_cold_stream_heat_balance(m, strm): + """ + Enforces the heat balance for a cold process stream within the model. This constraint ensures that the total heat gain for the cold stream equals the sum of heat received from all paired hot streams across all stages. The heat gain is calculated based on the temperature difference between the stream outlet and inlet, multiplied by the overall flow times heat capacity of the stream. + + Parameters + ---------- + m : Pyomo.ConcreteModel + A Pyomo concrete model representing the heat exchange model. + strm : str + The index for the cold stream involved in the heat exchanger. + + Returns + ------- + Pyomo.Constraint + A constraint object that ensures the heat balance across the specified cold stream over all stages and hot stream interactions. + """ return (m.T_out[strm] - m.T_in[strm]) * m.overall_FCp[strm] == ( sum(m.heat_exchanged[hot, strm, stg] for hot in m.hot_streams for stg in m.stages)) @m.Constraint(m.utility_streams) def overall_utility_stream_usage(m, strm): + """ + Ensures the total utility usage for each utility stream matches the sum of heat exchanged involving that utility across all stages. This constraint separates the calculations for hot and cold utility streams. For cold utility streams, it sums the heat exchanged from all hot process streams to the utility, and for hot utility streams, it sums the heat exchanged from the utility to all cold process streams. + + Parameters + ---------- + m : Pyomo.ConcreteModel + A Pyomo concrete model representing the heat exchange model. + strm : str + The index for the utility stream involved in the heat exchanger. This can be a hot or cold utility, and the constraint dynamically adjusts to sum the appropriate heat transfers based on this classification. + + Returns + ------- + Pyomo.Constraint + A constraint object that ensures the total calculated utility usage for the specified utility stream accurately reflects the sum of relevant heat exchanges in the system. This helps maintain energy balance specifically for utility streams within the overall heat exchange model. + """ return m.utility_usage[strm] == ( sum(m.heat_exchanged[hot, strm, stg] for hot in m.hot_process_streams @@ -278,6 +343,23 @@ def overall_utility_stream_usage(m, strm): @m.Constraint(m.stages, m.hot_process_streams, doc="Hot side overall heat balance for a stage.") def hot_stage_overall_heat_balance(m, stg, strm): + """ + Establishes an overall heat balance for a specific hot stream within a particular stage of the heat exchange process. This constraint ensures that the heat loss from the hot stream, calculated as the product of the temperature drop across the stage and the flow capacity of the stream, equals the total heat transferred to all corresponding cold streams within the same stage. + + Parameters + ---------- + m : Pyomo.ConcreteModel + A Pyomo concrete model representing the heat exchange model. + stg : int + The index for the stage involved in the heat exchanger. + strm : str + The index for the hot stream involved in the heat exchanger. + + Returns + ------- + Pyomo.Constraint + A constraint object that enforces the heat balance for the specified hot stream at the given stage. This ensures that the heat output from this stream is appropriately accounted for and matched by heat intake by the cold streams, promoting efficient energy use. + """ return ((m.stage_entry_T[strm, stg] - m.stage_exit_T[strm, stg]) * m.overall_FCp[strm]) == sum( m.heat_exchanged[strm, cold, stg] @@ -286,6 +368,23 @@ def hot_stage_overall_heat_balance(m, stg, strm): @m.Constraint(m.stages, m.cold_process_streams, doc="Cold side overall heat balance for a stage.") def cold_stage_overall_heat_balance(m, stg, strm): + """ + Establishes an overall heat balance for a specific cold stream within a particular stage of the heat exchange process. This constraint ensures that the heat gain for the cold stream, calculated as the product of the temperature increase across the stage and the flow capacity of the stream, equals the total heat received from all corresponding hot streams within the same stage. + + Parameters + ---------- + m : Pyomo.ConcreteModel + A Pyomo concrete model representing the heat exchange model. + stg : int + The index for the stage involved in the heat exchanger. + strm : str + The index for the cold stream involved in the heat exchanger. + + Returns + ------- + Pyomo.Constraint + A constraint object that enforces the heat balance for the specified cold stream at the given stage. This ensures that the heat intake by this stream is appropriately accounted for and matched by heat output from the hot streams, promoting efficient energy use. + """ return ((m.stage_exit_T[strm, stg] - m.stage_entry_T[strm, stg]) * m.overall_FCp[strm]) == sum( m.heat_exchanged[hot, strm, stg] @@ -293,26 +392,113 @@ def cold_stage_overall_heat_balance(m, stg, strm): @m.Constraint(m.stages, m.hot_process_streams) def hot_stream_monotonic_T_decrease(m, stg, strm): + """ + Ensures that the temperature of a hot stream decreases monotonically across a given stage. This constraint is critical for modeling realistic heat exchange scenarios where hot streams naturally cool down as they transfer heat to colder streams. It enforces that the exit temperature of the hot stream from any stage is less than or equal to its entry temperature for that stage. + + Parameters + ---------- + m : Pyomo.ConcreteModel + A Pyomo concrete model representing the heat exchange model. + stg : int + The index for the stage involved in the heat exchanger. + strm : str + The index for the hot stream involved in the heat exchanger. + + Returns + ------- + Pyomo.Constraint + A constraint object that ensures the temperature of the hot stream does not increase as it passes through the stage, which is essential for maintaining the physical feasibility of the heat exchange process. + """ return m.stage_exit_T[strm, stg] <= m.stage_entry_T[strm, stg] @m.Constraint(m.stages, m.cold_process_streams) def cold_stream_monotonic_T_increase(m, stg, strm): + """ + Ensures that the temperature of a cold stream increases monotonically across a given stage. This constraint is essential for modeling realistic heat exchange scenarios where cold streams naturally warm up as they absorb heat from hotter streams. It enforces that the exit temperature of the cold stream from any stage is greater than or equal to its entry temperature for that stage. + + Parameters + ---------- + m : Pyomo.ConcreteModel + A Pyomo concrete model representing the heat exchange model. + stg : int + The index for the stage involved in the heat exchanger. + strm : str + The index for the cold stream involved in the heat exchanger. + + Returns + ------- + Pyomo.Constraint + A constraint object that ensures the temperature of the cold stream increases as it passes through the stage, reflecting the natural heat absorption process and maintaining the physical feasibility of the heat exchange model. + """ return m.stage_exit_T[strm, stg] >= m.stage_entry_T[strm, stg] @m.Constraint(m.stages, m.hot_process_streams) def hot_stream_stage_T_link(m, stg, strm): + """ + Links the exit temperature of a hot stream from one stage to the entry temperature of the same stream in the subsequent stage, ensuring continuity and consistency in temperature progression across stages. This constraint is vital for maintaining a coherent thermal profile within each hot stream as it progresses through the heat exchange stages. For the final stage, no constraint is applied since there is no subsequent stage. + + Parameters + ---------- + m : Pyomo.ConcreteModel + A Pyomo concrete model representing the heat exchange model. + stg : int + The index for the stage involved in the heat exchanger. + strm : str + The index for the hot stream involved in the heat exchanger. + + Returns + ------- + Pyomo.Constraint + A constraint object that ensures the exit temperature at the end of one stage matches the entry temperature at the beginning of the next stage for the hot streams. In the final stage, where there is no subsequent stage, no constraint is applied. + """ return ( m.stage_exit_T[strm, stg] == m.stage_entry_T[strm, stg + 1] ) if stg < num_stages else Constraint.NoConstraint @m.Constraint(m.stages, m.cold_process_streams) def cold_stream_stage_T_link(m, stg, strm): + """ + Ensures continuity in the temperature profiles of cold streams across stages in the heat exchange model by linking the exit temperature of a cold stream in one stage to its entry temperature in the following stage. This constraint is crucial for maintaining consistent and logical heat absorption sequences within the cold streams as they move through successive stages. For the final stage, no constraint is applied since there is no subsequent stage. + + Parameters + ---------- + m : Pyomo.ConcreteModel + A Pyomo concrete model representing the heat exchange model. + stg : int + The index for the stage involved in the heat exchanger. + strm : str + The index for the cold stream involved in the heat exchanger. + + Returns + ------- + Pyomo.Constraint + A constraint object that ensures the exit temperature at the end of one stage matches the entry temperature at the beginning of the next stage for cold streams. In the final stage, where there is no subsequent stage, no constraint is applied, reflecting the end of the process sequence. + """ return ( m.stage_entry_T[strm, stg] == m.stage_exit_T[strm, stg + 1] ) if stg < num_stages else Constraint.NoConstraint @m.Expression(m.valid_matches, m.stages) def exchanger_capacity(m, hot, cold, stg): + """ + Calculates the heat transfer capacity of an exchanger for a given hot stream, cold stream, and stage combination. This capacity is derived from the exchanger's area, the overall heat transfer coefficient, and the geometric mean of the approach temperatures at both sides of the exchanger. This expression is used to estimate the efficiency and effectiveness of heat transfer in each stage of the heat exchange process. + + Parameters + ---------- + m : Pyomo.ConcreteModel + A Pyomo concrete model representing the heat exchange model. + hot : str + The index for the hot stream involved in the heat exchanger. + cold : str + The index for the cold stream involved in the heat exchanger. + stg : int + The index for the stage involved in the heat exchanger. + + Returns + ------- + Pyomo.Expression + A Pyomo expression that quantifies the heat transfer capacity of the exchanger. This value is crucial for optimizing the heat exchange system, ensuring that each stage is designed to maximize heat recovery while adhering to operational constraints and physical laws. + """ return m.exchanger_area[stg, hot, cold] * ( m.U[hot, cold] * ( m.exchanger_hot_side_approach_T[hot, cold, stg] * @@ -322,6 +508,20 @@ def exchanger_capacity(m, hot, cold, stg): ) ** (1 / 3)) def _exchanger_exists(disj, hot, cold, stg): + """ + Defines the conditions and constraints for the existence of an exchanger between a specified hot and cold stream at a given stage. This function sets the disjunct's indicator variable to true and configures constraints that model the physical behavior of the heat exchanger, including the log mean temperature difference and approach temperatures. + + Parameters + ---------- + disj : Pyomo.Disjunct + The disjunct object representing a potential heat exchanger scenario between the specified hot and cold streams. + hot : str + The index for the hot stream involved in the heat exchanger. + cold : str + The index for the cold stream involved in the heat exchanger. + stg : int + The index for the stage involved in the heat exchanger. + """ disj.indicator_var.value = True # Log mean temperature difference calculation @@ -347,37 +547,51 @@ def _exchanger_exists(disj, hot, cold, stg): if hot in m.hot_utility_streams: disj.stage_hot_approach_temperature = Constraint( expr=m.exchanger_hot_side_approach_T[hot, cold, stg] <= - m.T_in[hot] - m.stage_exit_T[cold, stg]) + m.T_in[hot] - m.stage_exit_T[cold, stg], doc="Hot utility: hot side limit.") disj.stage_cold_approach_temperature = Constraint( expr=m.exchanger_cold_side_approach_T[hot, cold, stg] <= - m.T_out[hot] - m.stage_entry_T[cold, stg]) + m.T_out[hot] - m.stage_entry_T[cold, stg], doc="Hot utility: cold side limit.") elif cold in m.cold_utility_streams: disj.stage_hot_approach_temperature = Constraint( expr=m.exchanger_hot_side_approach_T[hot, cold, stg] <= - m.stage_entry_T[hot, stg] - m.T_out[cold]) + m.stage_entry_T[hot, stg] - m.T_out[cold], doc="Cold utility: hot side limit.") disj.stage_cold_approach_temperature = Constraint( expr=m.exchanger_cold_side_approach_T[hot, cold, stg] <= - m.stage_exit_T[hot, stg] - m.T_in[cold]) + m.stage_exit_T[hot, stg] - m.T_in[cold], doc="Cold utility: cold side limit.") else: disj.stage_hot_approach_temperature = Constraint( expr=m.exchanger_hot_side_approach_T[hot, cold, stg] <= m.stage_entry_T[hot, stg] - - m.stage_exit_T[cold, stg]) + - m.stage_exit_T[cold, stg], doc="Process stream: hot side limit.") disj.stage_cold_approach_temperature = Constraint( expr=m.exchanger_cold_side_approach_T[hot, cold, stg] <= m.stage_exit_T[hot, stg] - - m.stage_entry_T[cold, stg]) + - m.stage_entry_T[cold, stg], doc="Process stream: cold side limit.") def _exchanger_absent(disj, hot, cold, stg): + """ + Defines the conditions for the absence of a heat exchanger between a specified hot and cold stream at a given stage. This function sets the disjunct's indicator variable to false and ensures that all associated costs and heat exchanged values are set to zero, effectively removing the exchanger from the model for this configuration. + + Parameters + ---------- + disj : Pyomo.Disjunct + The disjunct object representing a scenario where no heat exchanger is present between the specified hot and cold streams at the given stage. + hot : str + The index for the hot stream involved in the heat exchanger. + cold : str + The index for the cold stream involved in the heat exchanger. + stg : int + The index for the stage involved in the heat exchanger. + """ disj.indicator_var.value = False disj.no_match_exchanger_cost = Constraint( - expr=m.exchanger_area_cost[stg, hot, cold] == 0) + expr=m.exchanger_area_cost[stg, hot, cold] == 0, doc="No exchanger cost.") disj.no_match_exchanger_area = Constraint( - expr=m.exchanger_area[stg, hot, cold] == 0) + expr=m.exchanger_area[stg, hot, cold] == 0, doc="No exchanger area.") disj.no_match_exchanger_fixed_cost = Constraint( - expr=m.exchanger_fixed_cost[stg, hot, cold] == 0) + expr=m.exchanger_fixed_cost[stg, hot, cold] == 0, doc="No exchanger fixed cost.") disj.no_heat_exchange = Constraint( - expr=m.heat_exchanged[hot, cold, stg] == 0) + expr=m.heat_exchanged[hot, cold, stg] == 0, doc="No heat exchange.") m.exchanger_exists = Disjunct( m.valid_matches, m.stages, @@ -389,6 +603,24 @@ def _exchanger_absent(disj, hot, cold, stg): "hot stream and a cold stream at a stage.", rule=_exchanger_absent) def _exchanger_exists_or_absent(m, hot, cold, stg): + """_summary_ + + Parameters + ---------- + m : Pyomo.ConcreteModel + A Pyomo concrete model representing the heat exchange model. + hot : str + The index for the hot stream involved in the heat exchanger. + cold : str + The index for the cold stream involved in the heat exchanger. + stg : int + The index for the stage involved in the heat exchanger. + + Returns + ------- + _type_ + _description_ + """ return [m.exchanger_exists[hot, cold, stg], m.exchanger_absent[hot, cold, stg]] m.exchanger_exists_or_absent = Disjunction( @@ -413,6 +645,20 @@ def _exchanger_exists_or_absent(m, hot, cold, stg): @m.Expression(m.utility_streams) def utility_cost(m, strm): + """_summary_ + + Parameters + ---------- + m : Pyomo.ConcreteModel + A Pyomo concrete model representing the heat exchange model. + strm : str + The index for the utility stream involved in the heat exchanger. This can be a hot or cold utility. + + Returns + ------- + Pyomo.Expression + _description_ + """ return m.utility_unit_cost[strm] * m.utility_usage[strm] m.total_cost = Objective( @@ -423,7 +669,7 @@ def utility_cost(m, strm): + sum(m.exchanger_area_cost[stg, hot, cold] for stg in m.stages for hot, cold in m.valid_matches), - sense=minimize + sense=minimize, doc="Total cost of the heat exchanger network." ) return m From 02d354fc1e1232ebfc2eb4674f1338bc4e632c8b Mon Sep 17 00:00:00 2001 From: Albert Lee Date: Thu, 25 Apr 2024 23:16:27 -0400 Subject: [PATCH 68/99] Update models to account for Pyomo logical expression system and Boolean indicator_vars --- gdplib/mod_hens/common.py | 461 ++++++++++++++++++++++++-------------- 1 file changed, 292 insertions(+), 169 deletions(-) diff --git a/gdplib/mod_hens/common.py b/gdplib/mod_hens/common.py index 3177227..8374b87 100644 --- a/gdplib/mod_hens/common.py +++ b/gdplib/mod_hens/common.py @@ -10,7 +10,18 @@ from __future__ import division from pyomo.environ import ( - ConcreteModel, Constraint, minimize, NonNegativeReals, Objective, Param, RangeSet, Set, Suffix, value, Var, ) + ConcreteModel, + Constraint, + minimize, + NonNegativeReals, + Objective, + Param, + RangeSet, + Set, + Suffix, + value, + Var, +) from pyomo.gdp import Disjunct, Disjunction from .cafaro_approx import calculate_cafaro_coefficients @@ -36,22 +47,27 @@ def build_model(use_cafaro_approximation, num_stages): m = ConcreteModel() m.hot_process_streams = Set(initialize=['H1', 'H2'], doc="Hot process streams") m.cold_process_streams = Set(initialize=['C1', 'C2'], doc="Cold process streams") - m.process_streams = m.hot_process_streams | m.cold_process_streams # All process streams + m.process_streams = ( + m.hot_process_streams | m.cold_process_streams + ) # All process streams m.hot_utility_streams = Set(initialize=['steam'], doc="Hot utility streams") m.cold_utility_streams = Set(initialize=['water'], doc="Cold utility streams") m.hot_streams = Set( - initialize=m.hot_process_streams | m.hot_utility_streams, doc="Hot streams") + initialize=m.hot_process_streams | m.hot_utility_streams, doc="Hot streams" + ) m.cold_streams = Set( - initialize=m.cold_process_streams | m.cold_utility_streams, doc="Cold streams") + initialize=m.cold_process_streams | m.cold_utility_streams, doc="Cold streams" + ) m.utility_streams = Set( - initialize=m.hot_utility_streams | m.cold_utility_streams, doc="Utility streams") - m.streams = Set( - initialize=m.process_streams | m.utility_streams, doc="All streams") + initialize=m.hot_utility_streams | m.cold_utility_streams, doc="Utility streams" + ) + m.streams = Set(initialize=m.process_streams | m.utility_streams, doc="All streams") m.valid_matches = Set( - initialize=(m.hot_process_streams * m.cold_streams) | - (m.hot_utility_streams * m.cold_process_streams), + initialize=(m.hot_process_streams * m.cold_streams) + | (m.hot_utility_streams * m.cold_process_streams), doc="Match all hot streams to cold streams, but exclude " - "matches between hot and cold utilities.") + "matches between hot and cold utilities.", + ) # m.EMAT = Param(doc="Exchanger minimum approach temperature [K]", # initialize=1) # Unused right now, but could be used for variable bound tightening @@ -60,61 +76,71 @@ def build_model(use_cafaro_approximation, num_stages): m.stages = RangeSet(num_stages, doc="Number of stages") m.T_in = Param( - m.streams, doc="Inlet temperature of stream [K]", - initialize={'H1': 443, - 'H2': 423, - 'C1': 293, - 'C2': 353, - 'steam': 450, - 'water': 293}) + m.streams, + doc="Inlet temperature of stream [K]", + initialize={ + 'H1': 443, + 'H2': 423, + 'C1': 293, + 'C2': 353, + 'steam': 450, + 'water': 293, + }, + ) m.T_out = Param( - m.streams, doc="Outlet temperature of stream [K]", - initialize={'H1': 333, - 'H2': 303, - 'C1': 408, - 'C2': 413, - 'steam': 450, - 'water': 313}) + m.streams, + doc="Outlet temperature of stream [K]", + initialize={ + 'H1': 333, + 'H2': 303, + 'C1': 408, + 'C2': 413, + 'steam': 450, + 'water': 313, + }, + ) m.heat_exchanged = Var( - m.valid_matches, m.stages, + m.valid_matches, + m.stages, domain=NonNegativeReals, doc="Heat exchanged from hot stream to cold stream in stage [kW]", - initialize=1, bounds=(0, 5000)) + initialize=1, + bounds=(0, 5000), + ) m.overall_FCp = Param( m.process_streams, doc="Flow times heat capacity of stream [kW / K]", - initialize={'H1': 30, - 'H2': 15, - 'C1': 20, - 'C2': 40}) + initialize={'H1': 30, 'H2': 15, 'C1': 20, 'C2': 40}, + ) m.utility_usage = Var( m.utility_streams, doc="Hot or cold utility used [kW]", - domain=NonNegativeReals, initialize=1, bounds=(0, 5000)) + domain=NonNegativeReals, + initialize=1, + bounds=(0, 5000), + ) m.stage_entry_T = Var( - m.streams, m.stages, + m.streams, + m.stages, doc="Temperature of stream at stage entry [K].", initialize=350, - bounds=(293, 450) # TODO set to be equal to min and max temps + bounds=(293, 450), # TODO set to be equal to min and max temps ) m.stage_exit_T = Var( - m.streams, m.stages, + m.streams, + m.stages, doc="Temperature of stream at stage exit [K].", initialize=350, - bounds=(293, 450) # TODO set to be equal to min and max temps + bounds=(293, 450), # TODO set to be equal to min and max temps ) # Improve bounds on stage entry and exit temperatures for strm, stg in m.process_streams * m.stages: - m.stage_entry_T[strm, stg].setlb( - min(value(m.T_in[strm]), value(m.T_out[strm]))) - m.stage_exit_T[strm, stg].setlb( - min(value(m.T_in[strm]), value(m.T_out[strm]))) - m.stage_entry_T[strm, stg].setub( - max(value(m.T_in[strm]), value(m.T_out[strm]))) - m.stage_exit_T[strm, stg].setub( - max(value(m.T_in[strm]), value(m.T_out[strm]))) + m.stage_entry_T[strm, stg].setlb(min(value(m.T_in[strm]), value(m.T_out[strm]))) + m.stage_exit_T[strm, stg].setlb(min(value(m.T_in[strm]), value(m.T_out[strm]))) + m.stage_entry_T[strm, stg].setub(max(value(m.T_in[strm]), value(m.T_out[strm]))) + m.stage_exit_T[strm, stg].setub(max(value(m.T_in[strm]), value(m.T_out[strm]))) for strm, stg in m.utility_streams * m.stages: _fix_and_bound(m.stage_entry_T[strm, stg], m.T_in[strm]) _fix_and_bound(m.stage_exit_T[strm, stg], m.T_out[strm]) @@ -130,34 +156,41 @@ def build_model(use_cafaro_approximation, num_stages): m.utility_unit_cost = Param( m.utility_streams, doc="Annual unit cost of utilities [$/kW]", - initialize={'steam': 80, 'water': 20}) + initialize={'steam': 80, 'water': 20}, + ) m.module_sizes = Set(initialize=[10, 50, 100], doc="Available module sizes.") - m.max_num_modules = Param(m.module_sizes, initialize={ - # 5: 100, - 10: 50, - 50: 10, - 100: 5, - # 250: 2 - }, doc="maximum number of each module size available.") + m.max_num_modules = Param( + m.module_sizes, + initialize={ + # 5: 100, + 10: 50, + 50: 10, + 100: 5, + # 250: 2 + }, + doc="maximum number of each module size available.", + ) m.exchanger_fixed_unit_cost = Param( - m.valid_matches, default=2000, doc="exchanger fixed cost [$/kW]",) + m.valid_matches, default=2000, doc="exchanger fixed cost [$/kW]" + ) m.exchanger_area_cost_factor = Param( - m.valid_matches, default=1000, - initialize={ - ('steam', cold): 1200 - for cold in m.cold_process_streams}, - doc="1200 for heaters. 1000 for all other exchangers.") + m.valid_matches, + default=1000, + initialize={('steam', cold): 1200 for cold in m.cold_process_streams}, + doc="1200 for heaters. 1000 for all other exchangers.", + ) m.area_cost_exponent = Param(default=0.6, doc="Area cost exponent.") - if use_cafaro_approximation: # Use Cafaro approximation for coefficients if True + if use_cafaro_approximation: # Use Cafaro approximation for coefficients if True k, b = calculate_cafaro_coefficients(10, 500, m.area_cost_exponent) m.cafaro_k = Param(default=k) m.cafaro_b = Param(default=b) - @m.Param(m.valid_matches, m.module_sizes, - doc="Area cost factor for modular exchangers.") + @m.Param( + m.valid_matches, m.module_sizes, doc="Area cost factor for modular exchangers." + ) def module_area_cost_factor(m, hot, cold, area): """ Determines the area cost factor for modular exchangers within the heat integration model. The cost factor is based on the specified module size and stream pair, with different values for steam and other hot streams. The unit is [$/(m^2)^0.6]. @@ -186,8 +219,9 @@ def module_area_cost_factor(m, hot, cold, area): m.module_fixed_unit_cost = Param(default=0, doc="Fixed cost for a module.") m.module_area_cost_exponent = Param(default=0.6, doc="Area cost exponent.") - @m.Param(m.valid_matches, m.module_sizes, - doc="Cost of a module with a particular area.") + @m.Param( + m.valid_matches, m.module_sizes, doc="Cost of a module with a particular area." + ) def module_area_cost(m, hot, cold, area): """ Determines the cost of a module with a specified area size for a given hot and cold stream pair. The cost is calculated based on the area cost factor and exponent for the module size and stream pair. The unit is [$]. @@ -208,67 +242,90 @@ def module_area_cost(m, hot, cold, area): Pyomo.Parameter The cost of a module with the specified area size for the given hot and cold stream pair. The cost is calculated based on the area cost factor and exponent for the module size and stream pair. """ - return (m.module_area_cost_factor[hot, cold, area] - * area ** m.module_area_cost_exponent) + return ( + m.module_area_cost_factor[hot, cold, area] + * area**m.module_area_cost_exponent + ) m.U = Param( m.valid_matches, default=0.8, - initialize={ - ('steam', cold): 1.2 - for cold in m.cold_process_streams}, + initialize={('steam', cold): 1.2 for cold in m.cold_process_streams}, doc="Overall heat transfer coefficient." - "1.2 for heaters. 0.8 for everything else. The unit is [kW/m^2/K].") + "1.2 for heaters. 0.8 for everything else. The unit is [kW/m^2/K].", + ) m.exchanger_hot_side_approach_T = Var( - m.valid_matches, m.stages, + m.valid_matches, + m.stages, doc="Temperature difference between the hot stream inlet and cold " "stream outlet of the exchanger. The unit is [K].", - bounds=(0.1, 500), initialize=10 + bounds=(0.1, 500), + initialize=10, ) m.exchanger_cold_side_approach_T = Var( - m.valid_matches, m.stages, + m.valid_matches, + m.stages, doc="Temperature difference between the hot stream outlet and cold " "stream inlet of the exchanger. The unit is [K].", - bounds=(0.1, 500), initialize=10 + bounds=(0.1, 500), + initialize=10, ) m.LMTD = Var( - m.valid_matches, m.stages, + m.valid_matches, + m.stages, doc="Log mean temperature difference across the exchanger.", - bounds=(1, 500), initialize=10 + bounds=(1, 500), + initialize=10, ) # Improve LMTD bounds based on T values for hot, cold, stg in m.valid_matches * m.stages: - hot_side_dT_LB = max(0, value( - m.stage_entry_T[hot, stg].lb - m.stage_exit_T[cold, stg].ub)) - hot_side_dT_UB = max(0, value( - m.stage_entry_T[hot, stg].ub - m.stage_exit_T[cold, stg].lb)) - cold_side_dT_LB = max(0, value( - m.stage_exit_T[hot, stg].lb - m.stage_entry_T[cold, stg].ub)) - cold_side_dT_UB = max(0, value( - m.stage_exit_T[hot, stg].ub - m.stage_entry_T[cold, stg].lb)) - m.LMTD[hot, cold, stg].setlb(( - hot_side_dT_LB * cold_side_dT_LB * ( - hot_side_dT_LB + cold_side_dT_LB) / 2) ** (1 / 3) + hot_side_dT_LB = max( + 0, value(m.stage_entry_T[hot, stg].lb - m.stage_exit_T[cold, stg].ub) + ) + hot_side_dT_UB = max( + 0, value(m.stage_entry_T[hot, stg].ub - m.stage_exit_T[cold, stg].lb) ) - m.LMTD[hot, cold, stg].setub(( - hot_side_dT_UB * cold_side_dT_UB * ( - hot_side_dT_UB + cold_side_dT_UB) / 2) ** (1 / 3) + cold_side_dT_LB = max( + 0, value(m.stage_exit_T[hot, stg].lb - m.stage_entry_T[cold, stg].ub) + ) + cold_side_dT_UB = max( + 0, value(m.stage_exit_T[hot, stg].ub - m.stage_entry_T[cold, stg].lb) + ) + m.LMTD[hot, cold, stg].setlb( + (hot_side_dT_LB * cold_side_dT_LB * (hot_side_dT_LB + cold_side_dT_LB) / 2) + ** (1 / 3) + ) + m.LMTD[hot, cold, stg].setub( + (hot_side_dT_UB * cold_side_dT_UB * (hot_side_dT_UB + cold_side_dT_UB) / 2) + ** (1 / 3) ) m.exchanger_fixed_cost = Var( - m.stages, m.valid_matches, + m.stages, + m.valid_matches, doc="Fixed cost for an exchanger between a hot and cold stream.", - domain=NonNegativeReals, bounds=(0, 1E5), initialize=0) + domain=NonNegativeReals, + bounds=(0, 1e5), + initialize=0, + ) m.exchanger_area = Var( - m.stages, m.valid_matches, + m.stages, + m.valid_matches, doc="Area for an exchanger between a hot and cold stream.", - domain=NonNegativeReals, bounds=(0, 500), initialize=5) + domain=NonNegativeReals, + bounds=(0, 500), + initialize=5, + ) m.exchanger_area_cost = Var( - m.stages, m.valid_matches, + m.stages, + m.valid_matches, doc="Capital cost contribution from exchanger area.", - domain=NonNegativeReals, bounds=(0, 1E5), initialize=1000) + domain=NonNegativeReals, + bounds=(0, 1e5), + initialize=1000, + ) @m.Constraint(m.hot_process_streams) def overall_hot_stream_heat_balance(m, strm): @@ -288,8 +345,12 @@ def overall_hot_stream_heat_balance(m, strm): A constraint object that ensures the heat balance across the specified hot stream over all stages and cold stream interactions. """ return (m.T_in[strm] - m.T_out[strm]) * m.overall_FCp[strm] == ( - sum(m.heat_exchanged[strm, cold, stg] - for cold in m.cold_streams for stg in m.stages)) + sum( + m.heat_exchanged[strm, cold, stg] + for cold in m.cold_streams + for stg in m.stages + ) + ) @m.Constraint(m.cold_process_streams) def overall_cold_stream_heat_balance(m, strm): @@ -309,8 +370,12 @@ def overall_cold_stream_heat_balance(m, strm): A constraint object that ensures the heat balance across the specified cold stream over all stages and hot stream interactions. """ return (m.T_out[strm] - m.T_in[strm]) * m.overall_FCp[strm] == ( - sum(m.heat_exchanged[hot, strm, stg] - for hot in m.hot_streams for stg in m.stages)) + sum( + m.heat_exchanged[hot, strm, stg] + for hot in m.hot_streams + for stg in m.stages + ) + ) @m.Constraint(m.utility_streams) def overall_utility_stream_usage(m, strm): @@ -330,18 +395,27 @@ def overall_utility_stream_usage(m, strm): A constraint object that ensures the total calculated utility usage for the specified utility stream accurately reflects the sum of relevant heat exchanges in the system. This helps maintain energy balance specifically for utility streams within the overall heat exchange model. """ return m.utility_usage[strm] == ( - sum(m.heat_exchanged[hot, strm, stg] + sum( + m.heat_exchanged[hot, strm, stg] for hot in m.hot_process_streams for stg in m.stages - ) if strm in m.cold_utility_streams else 0 + - sum(m.heat_exchanged[strm, cold, stg] + ) + 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 - ) if strm in m.hot_utility_streams else 0 + ) + if strm in m.hot_utility_streams + else 0 ) - @m.Constraint(m.stages, m.hot_process_streams, - doc="Hot side overall heat balance for a stage.") + @m.Constraint( + m.stages, + m.hot_process_streams, + doc="Hot side overall heat balance for a stage.", + ) def hot_stage_overall_heat_balance(m, stg, strm): """ Establishes an overall heat balance for a specific hot stream within a particular stage of the heat exchange process. This constraint ensures that the heat loss from the hot stream, calculated as the product of the temperature drop across the stage and the flow capacity of the stream, equals the total heat transferred to all corresponding cold streams within the same stage. @@ -360,13 +434,16 @@ def hot_stage_overall_heat_balance(m, stg, strm): Pyomo.Constraint A constraint object that enforces the heat balance for the specified hot stream at the given stage. This ensures that the heat output from this stream is appropriately accounted for and matched by heat intake by the cold streams, promoting efficient energy use. """ - return ((m.stage_entry_T[strm, stg] - m.stage_exit_T[strm, stg]) - * m.overall_FCp[strm]) == sum( - m.heat_exchanged[strm, cold, stg] - for cold in m.cold_streams) - - @m.Constraint(m.stages, m.cold_process_streams, - doc="Cold side overall heat balance for a stage.") + return ( + (m.stage_entry_T[strm, stg] - m.stage_exit_T[strm, stg]) + * m.overall_FCp[strm] + ) == sum(m.heat_exchanged[strm, cold, stg] for cold in m.cold_streams) + + @m.Constraint( + m.stages, + m.cold_process_streams, + doc="Cold side overall heat balance for a stage.", + ) def cold_stage_overall_heat_balance(m, stg, strm): """ Establishes an overall heat balance for a specific cold stream within a particular stage of the heat exchange process. This constraint ensures that the heat gain for the cold stream, calculated as the product of the temperature increase across the stage and the flow capacity of the stream, equals the total heat received from all corresponding hot streams within the same stage. @@ -385,10 +462,10 @@ def cold_stage_overall_heat_balance(m, stg, strm): Pyomo.Constraint A constraint object that enforces the heat balance for the specified cold stream at the given stage. This ensures that the heat intake by this stream is appropriately accounted for and matched by heat output from the hot streams, promoting efficient energy use. """ - return ((m.stage_exit_T[strm, stg] - m.stage_entry_T[strm, stg]) - * m.overall_FCp[strm]) == sum( - m.heat_exchanged[hot, strm, stg] - for hot in m.hot_streams) + return ( + (m.stage_exit_T[strm, stg] - m.stage_entry_T[strm, stg]) + * m.overall_FCp[strm] + ) == sum(m.heat_exchanged[hot, strm, stg] for hot in m.hot_streams) @m.Constraint(m.stages, m.hot_process_streams) def hot_stream_monotonic_T_decrease(m, stg, strm): @@ -452,8 +529,10 @@ def hot_stream_stage_T_link(m, stg, strm): A constraint object that ensures the exit temperature at the end of one stage matches the entry temperature at the beginning of the next stage for the hot streams. In the final stage, where there is no subsequent stage, no constraint is applied. """ return ( - m.stage_exit_T[strm, stg] == m.stage_entry_T[strm, stg + 1] - ) if stg < num_stages else Constraint.NoConstraint + (m.stage_exit_T[strm, stg] == m.stage_entry_T[strm, stg + 1]) + if stg < num_stages + else Constraint.NoConstraint + ) @m.Constraint(m.stages, m.cold_process_streams) def cold_stream_stage_T_link(m, stg, strm): @@ -475,8 +554,10 @@ def cold_stream_stage_T_link(m, stg, strm): A constraint object that ensures the exit temperature at the end of one stage matches the entry temperature at the beginning of the next stage for cold streams. In the final stage, where there is no subsequent stage, no constraint is applied, reflecting the end of the process sequence. """ return ( - m.stage_entry_T[strm, stg] == m.stage_exit_T[strm, stg + 1] - ) if stg < num_stages else Constraint.NoConstraint + (m.stage_entry_T[strm, stg] == m.stage_exit_T[strm, stg + 1]) + if stg < num_stages + else Constraint.NoConstraint + ) @m.Expression(m.valid_matches, m.stages) def exchanger_capacity(m, hot, cold, stg): @@ -500,12 +581,18 @@ def exchanger_capacity(m, hot, cold, stg): A Pyomo expression that quantifies the heat transfer capacity of the exchanger. This value is crucial for optimizing the heat exchange system, ensuring that each stage is designed to maximize heat recovery while adhering to operational constraints and physical laws. """ return m.exchanger_area[stg, hot, cold] * ( - m.U[hot, cold] * ( - m.exchanger_hot_side_approach_T[hot, cold, stg] * - m.exchanger_cold_side_approach_T[hot, cold, stg] * - (m.exchanger_hot_side_approach_T[hot, cold, stg] + - m.exchanger_cold_side_approach_T[hot, cold, stg]) / 2 - ) ** (1 / 3)) + m.U[hot, cold] + * ( + m.exchanger_hot_side_approach_T[hot, cold, stg] + * m.exchanger_cold_side_approach_T[hot, cold, stg] + * ( + m.exchanger_hot_side_approach_T[hot, cold, stg] + + m.exchanger_cold_side_approach_T[hot, cold, stg] + ) + / 2 + ) + ** (1 / 3) + ) def _exchanger_exists(disj, hot, cold, stg): """ @@ -527,12 +614,17 @@ def _exchanger_exists(disj, hot, cold, stg): # Log mean temperature difference calculation disj.LMTD_calc = Constraint( doc="Log mean temperature difference", - expr=m.LMTD[hot, cold, stg] == ( - m.exchanger_hot_side_approach_T[hot, cold, stg] * - m.exchanger_cold_side_approach_T[hot, cold, stg] * - (m.exchanger_hot_side_approach_T[hot, cold, stg] + - m.exchanger_cold_side_approach_T[hot, cold, stg]) / 2 - ) ** (1 / 3) + expr=m.LMTD[hot, cold, stg] + == ( + m.exchanger_hot_side_approach_T[hot, cold, stg] + * m.exchanger_cold_side_approach_T[hot, cold, stg] + * ( + m.exchanger_hot_side_approach_T[hot, cold, stg] + + m.exchanger_cold_side_approach_T[hot, cold, stg] + ) + / 2 + ) + ** (1 / 3), ) m.BigM[disj.LMTD_calc] = 160 @@ -546,27 +638,37 @@ def _exchanger_exists(disj, hot, cold, stg): # Calculation of the approach temperatures if hot in m.hot_utility_streams: disj.stage_hot_approach_temperature = Constraint( - expr=m.exchanger_hot_side_approach_T[hot, cold, stg] <= - m.T_in[hot] - m.stage_exit_T[cold, stg], doc="Hot utility: hot side limit.") + expr=m.exchanger_hot_side_approach_T[hot, cold, stg] + <= m.T_in[hot] - m.stage_exit_T[cold, stg], + doc="Hot utility: hot side limit.", + ) disj.stage_cold_approach_temperature = Constraint( - expr=m.exchanger_cold_side_approach_T[hot, cold, stg] <= - m.T_out[hot] - m.stage_entry_T[cold, stg], doc="Hot utility: cold side limit.") + expr=m.exchanger_cold_side_approach_T[hot, cold, stg] + <= m.T_out[hot] - m.stage_entry_T[cold, stg], + doc="Hot utility: cold side limit.", + ) elif cold in m.cold_utility_streams: disj.stage_hot_approach_temperature = Constraint( - expr=m.exchanger_hot_side_approach_T[hot, cold, stg] <= - m.stage_entry_T[hot, stg] - m.T_out[cold], doc="Cold utility: hot side limit.") + expr=m.exchanger_hot_side_approach_T[hot, cold, stg] + <= m.stage_entry_T[hot, stg] - m.T_out[cold], + doc="Cold utility: hot side limit.", + ) disj.stage_cold_approach_temperature = Constraint( - expr=m.exchanger_cold_side_approach_T[hot, cold, stg] <= - m.stage_exit_T[hot, stg] - m.T_in[cold], doc="Cold utility: cold side limit.") + expr=m.exchanger_cold_side_approach_T[hot, cold, stg] + <= m.stage_exit_T[hot, stg] - m.T_in[cold], + doc="Cold utility: cold side limit.", + ) else: disj.stage_hot_approach_temperature = Constraint( - expr=m.exchanger_hot_side_approach_T[hot, cold, stg] <= - m.stage_entry_T[hot, stg] - - m.stage_exit_T[cold, stg], doc="Process stream: hot side limit.") + expr=m.exchanger_hot_side_approach_T[hot, cold, stg] + <= m.stage_entry_T[hot, stg] - m.stage_exit_T[cold, stg], + doc="Process stream: hot side limit.", + ) disj.stage_cold_approach_temperature = Constraint( - expr=m.exchanger_cold_side_approach_T[hot, cold, stg] <= - m.stage_exit_T[hot, stg] - - m.stage_entry_T[cold, stg], doc="Process stream: cold side limit.") + expr=m.exchanger_cold_side_approach_T[hot, cold, stg] + <= m.stage_exit_T[hot, stg] - m.stage_entry_T[cold, stg], + doc="Process stream: cold side limit.", + ) def _exchanger_absent(disj, hot, cold, stg): """ @@ -585,25 +687,37 @@ def _exchanger_absent(disj, hot, cold, stg): """ disj.indicator_var.value = False disj.no_match_exchanger_cost = Constraint( - expr=m.exchanger_area_cost[stg, hot, cold] == 0, doc="No exchanger cost.") + expr=m.exchanger_area_cost[stg, hot, cold] == 0, doc="No exchanger cost." + ) disj.no_match_exchanger_area = Constraint( - expr=m.exchanger_area[stg, hot, cold] == 0, doc="No exchanger area.") + expr=m.exchanger_area[stg, hot, cold] == 0, doc="No exchanger area." + ) disj.no_match_exchanger_fixed_cost = Constraint( - expr=m.exchanger_fixed_cost[stg, hot, cold] == 0, doc="No exchanger fixed cost.") + expr=m.exchanger_fixed_cost[stg, hot, cold] == 0, + doc="No exchanger fixed cost.", + ) disj.no_heat_exchange = Constraint( - expr=m.heat_exchanged[hot, cold, stg] == 0, doc="No heat exchange.") + expr=m.heat_exchanged[hot, cold, stg] == 0, doc="No heat exchange." + ) m.exchanger_exists = Disjunct( - m.valid_matches, m.stages, + m.valid_matches, + m.stages, doc="Disjunct for the presence of an exchanger between a " - "hot stream and a cold stream at a stage.", rule=_exchanger_exists) + "hot stream and a cold stream at a stage.", + rule=_exchanger_exists, + ) m.exchanger_absent = Disjunct( - m.valid_matches, m.stages, + m.valid_matches, + m.stages, doc="Disjunct for the absence of an exchanger between a " - "hot stream and a cold stream at a stage.", rule=_exchanger_absent) + "hot stream and a cold stream at a stage.", + rule=_exchanger_absent, + ) def _exchanger_exists_or_absent(m, hot, cold, stg): - """_summary_ + """ + Defines a disjunction to represent the decision between installing or not installing a heat exchanger between a specific hot and cold stream at a certain stage. Parameters ---------- @@ -618,16 +732,19 @@ def _exchanger_exists_or_absent(m, hot, cold, stg): Returns ------- - _type_ - _description_ + list + A list of Pyomo Disjunct objects, which includes the scenarios where the exchanger exists or is absent, allowing the model to explore different configurations for optimal energy use and cost efficiency. """ - return [m.exchanger_exists[hot, cold, stg], - m.exchanger_absent[hot, cold, stg]] + return [m.exchanger_exists[hot, cold, stg], m.exchanger_absent[hot, cold, stg]] + m.exchanger_exists_or_absent = Disjunction( - m.valid_matches, m.stages, + m.valid_matches, + m.stages, doc="Disjunction between presence or absence of an exchanger between " "a hot stream and a cold stream at a stage.", - rule=_exchanger_exists_or_absent, xor=True) + rule=_exchanger_exists_or_absent, + xor=True, + ) # Only hot utility matches in first stage and cold utility matches in last # stage for hot, cold in m.valid_matches: @@ -645,7 +762,8 @@ def _exchanger_exists_or_absent(m, hot, cold, stg): @m.Expression(m.utility_streams) def utility_cost(m, strm): - """_summary_ + """ + alculates the cost associated with the usage of a utility stream within the heat exchange model. Parameters ---------- @@ -657,19 +775,24 @@ def utility_cost(m, strm): Returns ------- Pyomo.Expression - _description_ + An expression representing the total cost of using the specified utility stream within the model, computed as the product of unit cost and usage. This helps in assessing the economic impact of utility choices in the heat exchange system. """ return m.utility_unit_cost[strm] * m.utility_usage[strm] m.total_cost = Objective( expr=sum(m.utility_cost[strm] for strm in m.utility_streams) - + sum(m.exchanger_fixed_cost[stg, hot, cold] - for stg in m.stages - for hot, cold in m.valid_matches) - + sum(m.exchanger_area_cost[stg, hot, cold] - for stg in m.stages - for hot, cold in m.valid_matches), - sense=minimize, doc="Total cost of the heat exchanger network." + + sum( + m.exchanger_fixed_cost[stg, hot, cold] + for stg in m.stages + for hot, cold in m.valid_matches + ) + + sum( + m.exchanger_area_cost[stg, hot, cold] + for stg in m.stages + for hot, cold in m.valid_matches + ), + sense=minimize, + doc="Total cost of the heat exchanger network.", ) return m From 25568b17a52e775b76de5266773873f977babd86 Mon Sep 17 00:00:00 2001 From: Albert Lee Date: Thu, 25 Apr 2024 23:35:12 -0400 Subject: [PATCH 69/99] Refactor conventional.py to improve code readability and add documentation --- gdplib/mod_hens/conventional.py | 64 +++++++++++++++++++++++---------- 1 file changed, 45 insertions(+), 19 deletions(-) diff --git a/gdplib/mod_hens/conventional.py b/gdplib/mod_hens/conventional.py index 9550d10..abf95bb 100644 --- a/gdplib/mod_hens/conventional.py +++ b/gdplib/mod_hens/conventional.py @@ -15,51 +15,77 @@ def build_conventional(cafaro_approx, num_stages): - """_summary_ + """ + Builds a conventional heat integration model based on specified parameters, delegating to the common build_model function. Parameters ---------- - cafaro_approx : _type_ - _description_ - num_stages : _type_ - _description_ + cafaro_approx : bool + Specifies whether to use the Cafaro approximation in the model. + num_stages : int + The number of stages in the heat integration model. Returns ------- - _type_ - _description_ + Pyomo.ConcreteModel + The constructed Pyomo concrete model for heat integration. """ return build_model(cafaro_approx, num_stages) def build_model(use_cafaro_approximation, num_stages): - """Build the model.""" + """_summary_ + + Parameters + ---------- + use_cafaro_approximation : bool + Flag to determine whether to use the Cafaro approximation for cost calculations. + num_stages : int + Number of stages in the heat exchange model. + + Returns + ------- + Pyomo.ConcreteModel + A fully configured heat integration model with additional conventional-specific constraints. + """ m = common.build_model(use_cafaro_approximation, num_stages) for hot, cold, stg in m.valid_matches * m.stages: disj = m.exchanger_exists[hot, cold, stg] if not use_cafaro_approximation: disj.exchanger_area_cost = Constraint( - expr=m.exchanger_area_cost[stg, hot, cold] * 1E-3 >= - m.exchanger_area_cost_factor[hot, cold] * 1E-3 * - m.exchanger_area[stg, hot, cold] ** m.area_cost_exponent) + expr=m.exchanger_area_cost[stg, hot, cold] * 1e-3 + >= m.exchanger_area_cost_factor[hot, cold] + * 1e-3 + * m.exchanger_area[stg, hot, cold] ** m.area_cost_exponent, + doc="Ensures area cost meets the standard cost scaling.", + ) else: disj.exchanger_area_cost = Constraint( - expr=m.exchanger_area_cost[stg, hot, cold] * 1E-3 >= - m.exchanger_area_cost_factor[hot, cold] * 1E-3 * m.cafaro_k - * log(m.cafaro_b * m.exchanger_area[stg, hot, cold] + 1) + expr=m.exchanger_area_cost[stg, hot, cold] * 1e-3 + >= m.exchanger_area_cost_factor[hot, cold] + * 1e-3 + * m.cafaro_k + * log( + m.cafaro_b * m.exchanger_area[stg, hot, cold] + 1, + doc="Applies Cafaro's logarithmic cost scaling to area cost.", + ) ) m.BigM[disj.exchanger_area_cost] = 100 disj.exchanger_fixed_cost = Constraint( - expr=m.exchanger_fixed_cost[stg, hot, cold] == - m.exchanger_fixed_unit_cost[hot, cold]) + expr=m.exchanger_fixed_cost[stg, hot, cold] + == m.exchanger_fixed_unit_cost[hot, cold], + doc="Sets fixed cost for the exchanger based on unit costs.", + ) # Area requirement disj.exchanger_required_area = Constraint( - expr=m.exchanger_area[stg, hot, cold] * ( - m.U[hot, cold] * m.LMTD[hot, cold, stg]) >= - m.heat_exchanged[hot, cold, stg]) + expr=m.exchanger_area[stg, hot, cold] + * (m.U[hot, cold] * m.LMTD[hot, cold, stg]) + >= m.heat_exchanged[hot, cold, stg], + doc="Calculates the required area based on heat exchanged and LMTD.", + ) m.BigM[disj.exchanger_required_area] = 5000 return m From 6c25c1dfc13faa073e0c1519503ac6c63a8719fe Mon Sep 17 00:00:00 2001 From: Albert Lee Date: Fri, 26 Apr 2024 01:12:26 -0400 Subject: [PATCH 70/99] Update conventional.py to build and configure a heat integration model using standard calculations or the Cafaro approximation, with specific constraints for the conventional scenario --- gdplib/mod_hens/conventional.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gdplib/mod_hens/conventional.py b/gdplib/mod_hens/conventional.py index abf95bb..3eec0fb 100644 --- a/gdplib/mod_hens/conventional.py +++ b/gdplib/mod_hens/conventional.py @@ -34,7 +34,8 @@ def build_conventional(cafaro_approx, num_stages): def build_model(use_cafaro_approximation, num_stages): - """_summary_ + """ + Builds and configures a heat integration model using either standard calculations or the Cafaro approximation for specific costs and heat exchange calculations, supplemented by constraints specific to the conventional scenario. Parameters ---------- From e809a01d2f6b5499ec42101b61b022cfe325a2a2 Mon Sep 17 00:00:00 2001 From: Albert Lee Date: Fri, 26 Apr 2024 01:13:22 -0400 Subject: [PATCH 71/99] Update conventional.py to build and configure a heat integration model using standard calculations or the Cafaro approximation, with specific constraints for the conventional scenario --- .../modular_discrete_single_module.py | 259 +++++++++++++++--- 1 file changed, 221 insertions(+), 38 deletions(-) diff --git a/gdplib/mod_hens/modular_discrete_single_module.py b/gdplib/mod_hens/modular_discrete_single_module.py index be621be..c2ab435 100644 --- a/gdplib/mod_hens/modular_discrete_single_module.py +++ b/gdplib/mod_hens/modular_discrete_single_module.py @@ -21,11 +21,40 @@ def build_single_module(cafaro_approx, num_stages): + """ + Builds a heat integration model tailored to handle single module types, with the option to utilize Cafaro's approximation for cost and efficiency calculations. + + Parameters + ---------- + cafaro_approx : bool + Specifies whether to use the Cafaro approximation in the model. + num_stages : int + The number of stages in the heat integration model. + + Returns + ------- + Pyomo.ConcreteModel + A Pyomo model configured with constraints and parameters specific to the requirements of using single module types in a discretized format. + """ return build_model(cafaro_approx, num_stages) def build_model(use_cafaro_approximation, num_stages): - """Build the model.""" + """ + Extends a base heat integration model by incorporating a module configuration approach. It allows only single exchanger module types, optimizing the model for specific operational constraints and simplifying the nonlinear terms through discretization. + + Parameters + ---------- + cafaro_approx : bool + Specifies whether to use the Cafaro approximation in the model. + num_stages : int + The number of stages in the heat integration model. + + Returns + ------- + Pyomo.ConcreteModel + An enhanced heat integration model that supports module configurations with discretized area considerations to simplify calculations and improve optimization performance. + """ m = common.build_model(use_cafaro_approximation, num_stages) # list of tuples (num_modules, module_size) @@ -35,37 +64,101 @@ def build_model(use_cafaro_approximation, num_stages): configurations_list += configs # Map of config indx: (# modules, module size) - m.configurations_map = { - (k + 1): v for k, v in enumerate(configurations_list)} + m.configurations_map = {(k + 1): v for k, v in enumerate(configurations_list)} m.module_index_set = RangeSet(len(configurations_list)) m.module_config_active = Var( - m.valid_matches, m.stages, m.module_index_set, + m.valid_matches, + m.stages, + m.module_index_set, doc="Binary for if which module configuration is active for a match.", - domain=Binary, initialize=0) + domain=Binary, + initialize=0, + ) @m.Param(m.module_index_set, doc="Area of each configuration") def module_area(m, indx): + """ + Calculates the total area of a module configuration based on the number of modules and the size of each module. + + Parameters + ---------- + m : Pyomo.ConcreteModel + A Pyomo concrete model representing the heat exchange model modified to support discretization of area simplifying the nonlinear expressions, specialized to the case of allowing only a single exchanger module type (size). + indx : int + Index of the module configuration in the model. + + Returns + ------- + Pyomo.Parameter + The total area of the configuration corresponding to the given index. + """ num_modules, size = m.configurations_map[indx] return num_modules * size - @m.Param(m.valid_matches, m.module_index_set, - doc="Area cost for each modular configuration.") + @m.Param( + m.valid_matches, + m.module_index_set, + doc="Area cost for each modular configuration.", + ) def modular_size_cost(m, hot, cold, indx): + """ + Determines the cost associated with a specific modular configuration, taking into account the number of modules and their individual sizes. + + Parameters + ---------- + m : Pyomo.ConcreteModel + A Pyomo concrete model representing the heat exchange model modified for the case of allowing only a single exchanger module type (size). + hot : str + The index for the hot stream involved in the heat exchanger. + cold : str + The index for the cold stream involved in the heat exchanger. + indx : int + Index of the module configuration in the model. + + Returns + ------- + Pyomo.Parameter + Cost associated with the specified modular configuration. + """ num_modules, size = m.configurations_map[indx] return num_modules * m.module_area_cost[hot, cold, size] - @m.Param(m.valid_matches, m.module_index_set, - doc="Fixed cost for each modular exchanger size.") + @m.Param( + m.valid_matches, + m.module_index_set, + doc="Fixed cost for each modular exchanger size.", + ) def modular_fixed_cost(m, hot, cold, indx): + """ + Computes the fixed cost for a modular exchanger configuration, factoring in the number of modules and the set fixed cost per unit. + + Parameters + ---------- + m : Pyomo.ConcreteModel + A Pyomo concrete model representing the heat exchange model modified for the case of allowing only a single exchanger module type (size). + cold : str + The index for the cold stream involved in the heat exchanger. + indx : int + Index of the module configuration in the model. + + Returns + ------- + Pyomo.Parameter + Fixed cost for the given modular exchanger configuration. + """ num_modules, size = m.configurations_map[indx] return num_modules * m.module_fixed_unit_cost m.LMTD_discretize = Var( - m.hot_streams, m.cold_streams, m.stages, m.module_index_set, + m.hot_streams, + m.cold_streams, + m.stages, + m.module_index_set, doc="Discretized log mean temperature difference", - bounds=(0, 500), initialize=0 + bounds=(0, 500), + initialize=0, ) for hot, cold, stg in m.valid_matches * m.stages: @@ -73,72 +166,162 @@ def modular_fixed_cost(m, hot, cold, indx): disj.choose_one_config = Constraint( expr=sum( m.module_config_active[hot, cold, stg, indx] - for indx in m.module_index_set) == 1 + for indx in m.module_index_set + ) + == 1, + doc="Enforce a single active configuration per exchanger per stage.", ) disj.exchanger_area_cost = Constraint( - expr=m.exchanger_area_cost[stg, hot, cold] * 1E-3 == - sum(m.modular_size_cost[hot, cold, indx] * 1E-3 * - m.module_config_active[hot, cold, stg, indx] - for indx in m.module_index_set) + expr=m.exchanger_area_cost[stg, hot, cold] * 1e-3 + == sum( + m.modular_size_cost[hot, cold, indx] + * 1e-3 + * m.module_config_active[hot, cold, stg, indx] + for indx in m.module_index_set + ), + doc="Compute total area cost from active configurations.", ) disj.exchanger_fixed_cost = Constraint( - expr=m.exchanger_fixed_cost[stg, hot, cold] == - sum(m.modular_fixed_cost[hot, cold, indx] * 1E-3 * - m.module_config_active[hot, cold, stg, indx] - for indx in m.module_index_set)) + expr=m.exchanger_fixed_cost[stg, hot, cold] + == sum( + m.modular_fixed_cost[hot, cold, indx] + * 1e-3 + * m.module_config_active[hot, cold, stg, indx] + for indx in m.module_index_set + ), + doc="Sum fixed costs of active configurations for total investment.", + ) disj.discretize_area = Constraint( - expr=m.exchanger_area[stg, hot, cold] == sum( - m.module_area[indx] * - m.module_config_active[hot, cold, stg, indx] - for indx in m.module_index_set) + expr=m.exchanger_area[stg, hot, cold] + == sum( + m.module_area[indx] * m.module_config_active[hot, cold, stg, indx] + for indx in m.module_index_set + ), + doc="Match exchanger area with sum of active configuration areas.", ) disj.discretized_LMTD = Constraint( - expr=m.LMTD[hot, cold, stg] == sum( - m.LMTD_discretize[hot, cold, stg, indx] - for indx in m.module_index_set - ) + expr=m.LMTD[hot, cold, stg] + == sum( + m.LMTD_discretize[hot, cold, stg, indx] for indx in m.module_index_set + ), + doc="Aggregate LMTD from active configurations for thermal modeling.", ) @disj.Constraint(m.module_index_set) def discretized_LMTD_LB(disj, indx): + """ + Sets the lower bound on the discretized Log Mean Temperature Difference (LMTD) for each module configuration. + + Parameters + ---------- + disj : Pyomo.Disjunct + The disjunct object representing a specific module configuration. + indx : int + Index of the module configuration in the model. + + Returns + ------- + Pyomo.Constraint + A constraint ensuring that the discretized LMTD respects the specified lower bound for active configurations. + """ return ( - m.LMTD[hot, cold, stg].lb - * m.module_config_active[hot, cold, stg, indx] + m.LMTD[hot, cold, stg].lb * m.module_config_active[hot, cold, stg, indx] ) <= m.LMTD_discretize[hot, cold, stg, indx] @disj.Constraint(m.module_index_set) def discretized_LMTD_UB(disj, indx): + """ + Sets the upper bound on the discretized Log Mean Temperature Difference (LMTD) for each module configuration. + + Parameters + ---------- + disj : Pyomo.Disjunct + The disjunct object representing a specific module configuration. + indx : int + Index of the module configuration in the model. + + Returns + ------- + Pyomo.Constraint + A constraint ensuring that the discretized LMTD does not exceed the specified upper bound for active configurations. + """ return m.LMTD_discretize[hot, cold, stg, indx] <= ( - m.LMTD[hot, cold, stg].ub - * m.module_config_active[hot, cold, stg, indx] + m.LMTD[hot, cold, stg].ub * m.module_config_active[hot, cold, stg, indx] ) disj.exchanger_required_area = Constraint( - expr=m.U[hot, cold] * sum( + expr=m.U[hot, cold] + * sum( m.module_area[indx] * m.LMTD_discretize[hot, cold, stg, indx] - for indx in m.module_index_set) >= - m.heat_exchanged[hot, cold, stg]) + for indx in m.module_index_set + ) + >= m.heat_exchanged[hot, cold, stg], + doc="Ensures sufficient heat transfer capacity for required heat exchange.", + ) @m.Disjunct(m.module_sizes) def module_type(disj, size): - """Disjunct for selection of one module type.""" + """ + Disjunct for selecting a specific module size in the heat exchange model. This disjunct applies constraints to enforce that only the selected module size is active within any given configuration across all stages and matches. + + Parameters + ---------- + disj : Pyomo.Disjunct + The disjunct object associated with a specific module size. + size : int + The specific size of the module being considered in this disjunct. + + Returns + ------- + Pyomo.Disjunct + A Pyomo Disjunct object that contains constraints to limit the module configuration to a single size throughout the model. + """ + @disj.Constraint(m.valid_matches, m.stages, m.module_index_set) def no_other_module_types(_, hot, cold, stg, indx): + """ + Ensures only modules of the selected size are active, deactivating other sizes. + + Parameters + ---------- + _ : Pyomo.ConcreteModel + The Pyomo model instance, not used directly in the function. + hot : str + The index for the hot stream involved in the heat exchanger. + cold : str + The index for the cold stream involved in the heat exchanger. + stg : int + The index for the stage involved in the heat exchanger. + indx : int + Index of the module configuration in the model. + + Returns + ------- + Pyomo.Constraint + A constraint expression that ensures only modules of the specified size are active, effectively disabling other module sizes for the current configuration. + """ # num_modules, size = configurations_map[indx] if m.configurations_map[indx][1] != size: return m.module_config_active[hot, cold, stg, indx] == 0 else: return Constraint.NoConstraint + # disj.no_other_module_types = Constraint( # expr=sum( # m.module_config_active[hot, cold, stg, indx] # for indx in m.module_index_set - # if m.configurations_map[indx][1] != size) == 0 + # if m.configurations_map[indx][1] != size + # ) + # == 0, + # doc="Deactivates non-selected module sizes.", # ) + m.select_one_module_type = Disjunction( - expr=[m.module_type[area] for area in m.module_sizes]) + expr=[m.module_type[area] for area in m.module_sizes], + doc="Selects exactly one module size for use across all configurations.", + ) return m From 6ddf35a5b7df7d140f3ba74daaff6fb16cee31a5 Mon Sep 17 00:00:00 2001 From: Albert Lee Date: Fri, 26 Apr 2024 01:18:03 -0400 Subject: [PATCH 72/99] Refactor function parameter name in modular_discrete_single_module.py for clarity --- gdplib/mod_hens/modular_discrete_single_module.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gdplib/mod_hens/modular_discrete_single_module.py b/gdplib/mod_hens/modular_discrete_single_module.py index c2ab435..f3f863a 100644 --- a/gdplib/mod_hens/modular_discrete_single_module.py +++ b/gdplib/mod_hens/modular_discrete_single_module.py @@ -45,7 +45,7 @@ def build_model(use_cafaro_approximation, num_stages): Parameters ---------- - cafaro_approx : bool + use_cafaro_approximation : bool Specifies whether to use the Cafaro approximation in the model. num_stages : int The number of stages in the heat integration model. From 6ba6212d6be5c84ade19553bcbc9af900a513c1d Mon Sep 17 00:00:00 2001 From: Albert Lee Date: Sat, 27 Apr 2024 15:56:17 -0400 Subject: [PATCH 73/99] Refactor function parameter name in modular_discrete_single_module.py for clarity --- gdplib/mod_hens/modular_discrete.py | 329 +++++++++++++++++++++++----- 1 file changed, 270 insertions(+), 59 deletions(-) diff --git a/gdplib/mod_hens/modular_discrete.py b/gdplib/mod_hens/modular_discrete.py index 66fdb46..66205b1 100644 --- a/gdplib/mod_hens/modular_discrete.py +++ b/gdplib/mod_hens/modular_discrete.py @@ -13,26 +13,47 @@ """ from __future__ import division -from pyomo.environ import (Binary, Constraint, log, Set, Var) +from pyomo.environ import Binary, Constraint, log, Set, Var from pyomo.gdp import Disjunct, Disjunction from . import common def build_require_modular(cafaro_approx, num_stages): + """ + Builds a heat integration model requiring all exchangers to use modular configurations. + + Parameters + ---------- + cafaro_approx : bool + Specifies whether to use the Cafaro approximation in the model. + num_stages : int + The number of stages in the heat integration model. + + Returns + ------- + Pyomo.ConcreteModel + A Pyomo model configured to use only modular heat exchanger configurations. + """ m = build_model(cafaro_approx, num_stages) # Require modular + # Enforce modular configuration for all valid matches and stages for hot, cold, stg in m.valid_matches * m.stages: disj = m.exchanger_exists[hot, cold, stg] disj.modular.indicator_var.fix(True) disj.conventional.deactivate() + # Optimize modular configurations based on cost for hot, cold in m.valid_matches: lowest_price = float('inf') + # Determine the least costly configuration for each size for size in sorted(m.possible_sizes, reverse=True): - current_size_cost = (m.modular_size_cost[hot, cold, size] + - m.modular_fixed_cost[hot, cold, size]) + current_size_cost = ( + m.modular_size_cost[hot, cold, size] + + m.modular_fixed_cost[hot, cold, size] + ) if current_size_cost > lowest_price: + # Deactivate configurations that are not the least costly for stg in m.stages: m.module_size_active[hot, cold, stg, size].fix(0) else: @@ -42,13 +63,31 @@ def build_require_modular(cafaro_approx, num_stages): def build_modular_option(cafaro_approx, num_stages): + """ + Constructs a heat integration model with the option for using modular configurations based on cost optimization. + + Parameters + ---------- + cafaro_approx : bool + Specifies whether to use the Cafaro approximation in the model. + num_stages : int + The number of stages in the heat integration model. + + Returns + ------- + Pyomo.ConcreteModel + A Pyomo model that considers modular exchanger options based on cost efficiencies. + """ m = build_model(cafaro_approx, num_stages) + # Optimize for the least cost configuration across all stages and matche for hot, cold in m.valid_matches: lowest_price = float('inf') for size in sorted(m.possible_sizes, reverse=True): - current_size_cost = (m.modular_size_cost[hot, cold, size] + - m.modular_fixed_cost[hot, cold, size]) + current_size_cost = ( + m.modular_size_cost[hot, cold, size] + + m.modular_fixed_cost[hot, cold, size] + ) if current_size_cost > lowest_price: for stg in m.stages: m.module_size_active[hot, cold, stg, size].fix(0) @@ -59,14 +98,35 @@ def build_modular_option(cafaro_approx, num_stages): def build_model(use_cafaro_approximation, num_stages): - """Build the model.""" + """ + Initializes a base Pyomo model for heat integration using the common building blocks, with additional configuration for modular sizing. + + Parameters + ---------- + use_cafaro_approximation : bool + Specifies whether to use the Cafaro approximation in the model. + num_stages : int + The number of stages in the heat integration model. + + Returns + ------- + Pyomo.ConcreteModel + A Pyomo model configured for heat integration with modular exchanger options. + """ m = common.build_model(use_cafaro_approximation, num_stages) - m.possible_sizes = Set(initialize=[10 * (i + 1) for i in range(50)]) + m.possible_sizes = Set( + initialize=[10 * (i + 1) for i in range(50)], + doc="Set of possible module sizes, ranging from 10 to 500 in increments of 10.", + ) m.module_size_active = Var( - m.valid_matches, m.stages, m.possible_sizes, + m.valid_matches, + m.stages, + m.possible_sizes, doc="Total area of modular exchangers for each match.", - domain=Binary, initialize=0) + domain=Binary, + initialize=0, + ) num_modules_required = {} for size in m.possible_sizes: @@ -79,30 +139,106 @@ def build_model(use_cafaro_approximation, num_stages): num_modules_required[size, area] = remaining_size // area remaining_size = remaining_size % area - @m.Param(m.valid_matches, m.possible_sizes, m.module_sizes, - doc="Number of exchangers of each area required to " - "yield a certain total size.") + @m.Param( + m.valid_matches, + m.possible_sizes, + m.module_sizes, + doc="Number of exchangers of each area required to " + "yield a certain total size.", + ) def modular_num_exchangers(m, hot, cold, size, area): + """ + Returns the number of exchangers required for a given total module size and area. + + Parameters + ---------- + m : Pyomo.ConcreteModel + A Pyomo model configured for heat integration with modular exchanger options. + hot : str + The index for the hot stream involved in the heat exchanger. + cold : str + The index for the cold stream involved in the heat exchanger. + size : int + Module size under consideration. + area : float + The modular area size of the heat exchanger. + + Returns + ------- + Pyomo.Parameter + Number of modules of the specified area required to achieve the total size. + """ return num_modules_required[size, area] - @m.Param(m.valid_matches, m.possible_sizes, - doc="Area cost for each modular exchanger size.") + @m.Param( + m.valid_matches, + m.possible_sizes, + doc="Area cost for each modular exchanger size.", + ) def modular_size_cost(m, hot, cold, size): - return sum(m.modular_num_exchangers[hot, cold, size, area] * - m.module_area_cost[hot, cold, area] - for area in m.module_sizes) + """ + Returns the total area cost for a specified module size by summing costs of all required module areas. + + Parameters + ---------- + m : Pyomo.ConcreteModel + A Pyomo model configured for heat integration with modular exchanger options. + hot : str + The index for the hot stream involved in the heat exchanger. + cold : str + The index for the cold stream involved in the heat exchanger. + size : int + Module size under consideration. + + Returns + ------- + Pyomo.Parameter + Total area cost for the specified module size. + """ + return sum( + m.modular_num_exchangers[hot, cold, size, area] + * m.module_area_cost[hot, cold, area] + for area in m.module_sizes + ) - @m.Param(m.valid_matches, m.possible_sizes, - doc="Fixed cost for each modular exchanger size.") + @m.Param( + m.valid_matches, + m.possible_sizes, + doc="Fixed cost for each modular exchanger size.", + ) def modular_fixed_cost(m, hot, cold, size): - return sum(m.modular_num_exchangers[hot, cold, size, area] * - m.module_fixed_unit_cost - for area in m.module_sizes) + """ + Returns the total fixed cost associated with a specific modular exchanger size. + + Parameters + ---------- + m : Pyomo.ConcreteModel + A Pyomo model configured for heat integration with modular exchanger options. + hot : str + The index for the hot stream involved in the heat exchanger. + cold : str + The index for the cold stream involved in the heat exchanger. + size : int + Module size under consideration. + + Returns + ------- + Pyomo.Parameter + Total fixed cost for the specified module size. + """ + return sum( + m.modular_num_exchangers[hot, cold, size, area] * m.module_fixed_unit_cost + for area in m.module_sizes + ) m.LMTD_discretize = Var( - m.hot_streams, m.cold_streams, m.stages, m.possible_sizes, + m.hot_streams, + m.cold_streams, + m.stages, + m.possible_sizes, doc="Discretized log mean temperature difference", - bounds=(0, 500), initialize=0 + bounds=(0, 500), + initialize=0, ) for hot, cold, stg in m.valid_matches * m.stages: @@ -110,84 +246,159 @@ def modular_fixed_cost(m, hot, cold, size): disj.conventional = Disjunct() if not use_cafaro_approximation: disj.conventional.exchanger_area_cost = Constraint( - expr=m.exchanger_area_cost[stg, hot, cold] * 1E-3 >= - m.exchanger_area_cost_factor[hot, cold] * 1E-3 * - m.exchanger_area[stg, hot, cold] ** m.area_cost_exponent) + expr=m.exchanger_area_cost[stg, hot, cold] * 1e-3 + >= m.exchanger_area_cost_factor[hot, cold] + * 1e-3 + * m.exchanger_area[stg, hot, cold] ** m.area_cost_exponent, + doc="Ensures area cost meets the standard cost scaling.", + ) else: disj.conventional.exchanger_area_cost = Constraint( - expr=m.exchanger_area_cost[stg, hot, cold] * 1E-3 >= - m.exchanger_area_cost_factor[hot, cold] * 1E-3 * m.cafaro_k - * log(m.cafaro_b * m.exchanger_area[stg, hot, cold] + 1) + expr=m.exchanger_area_cost[stg, hot, cold] * 1e-3 + >= m.exchanger_area_cost_factor[hot, cold] + * 1e-3 + * m.cafaro_k + * log(m.cafaro_b * m.exchanger_area[stg, hot, cold] + 1), + doc="Ensures area cost meets the Cafaro approximation.", ) m.BigM[disj.conventional.exchanger_area_cost] = 100 disj.conventional.exchanger_fixed_cost = Constraint( - expr=m.exchanger_fixed_cost[stg, hot, cold] == - m.exchanger_fixed_unit_cost[hot, cold]) + expr=m.exchanger_fixed_cost[stg, hot, cold] + == m.exchanger_fixed_unit_cost[hot, cold], + doc="Sets the fixed cost for conventional exchangers.", + ) @disj.conventional.Constraint(m.possible_sizes) def no_modules(_, size): + """ + Ensures that no modules are active in the conventional configuration. + + Parameters + ---------- + _ : Pyomo.ConcreteModel + The Pyomo model instance, not used directly in the function. + size : int + Module size under consideration. + + Returns + ------- + Pyomo.Constraint + A constraint that forces the module size active variables to zero, ensuring no modular units are mistakenly considered in conventional configurations. + """ return m.module_size_active[hot, cold, stg, size] == 0 # Area requirement disj.conventional.exchanger_required_area = Constraint( - expr=m.exchanger_area[stg, hot, cold] * - m.U[hot, cold] * m.LMTD[hot, cold, stg] >= - m.heat_exchanged[hot, cold, stg]) + expr=m.exchanger_area[stg, hot, cold] + * m.U[hot, cold] + * m.LMTD[hot, cold, stg] + >= m.heat_exchanged[hot, cold, stg], + doc="Calculates the required area based on heat exchanged and LMTD.", + ) m.BigM[disj.conventional.exchanger_required_area] = 5000 disj.modular = Disjunct() disj.modular.choose_one_config = Constraint( expr=sum( - m.module_size_active[hot, cold, stg, size] - for size in m.possible_sizes) == 1 + m.module_size_active[hot, cold, stg, size] for size in m.possible_sizes + ) + == 1, + doc="Only one module size can be active.", ) disj.modular.exchanger_area_cost = Constraint( - expr=m.exchanger_area_cost[stg, hot, cold] * 1E-3 == - sum(m.modular_size_cost[hot, cold, size] * 1E-3 * - m.module_size_active[hot, cold, stg, size] - for size in m.possible_sizes) + expr=m.exchanger_area_cost[stg, hot, cold] * 1e-3 + == sum( + m.modular_size_cost[hot, cold, size] + * 1e-3 + * m.module_size_active[hot, cold, stg, size] + for size in m.possible_sizes + ), + doc="Area cost for modular exchangers.", ) disj.modular.exchanger_fixed_cost = Constraint( - expr=m.exchanger_fixed_cost[stg, hot, cold] == - sum(m.modular_fixed_cost[hot, cold, size] * 1E-3 * - m.module_size_active[hot, cold, stg, size] - for size in m.possible_sizes)) + expr=m.exchanger_fixed_cost[stg, hot, cold] + == sum( + m.modular_fixed_cost[hot, cold, size] + * 1e-3 + * m.module_size_active[hot, cold, stg, size] + for size in m.possible_sizes + ), + doc="Fixed cost for modular exchangers.", + ) disj.modular.discretize_area = Constraint( - expr=m.exchanger_area[stg, hot, cold] == sum( + expr=m.exchanger_area[stg, hot, cold] + == sum( area * m.module_size_active[hot, cold, stg, area] - for area in m.possible_sizes) + for area in m.possible_sizes + ), + doc="Total area of modular exchangers for each match.", ) disj.modular.discretized_LMTD = Constraint( - expr=m.LMTD[hot, cold, stg] == sum( - m.LMTD_discretize[hot, cold, stg, size] - for size in m.possible_sizes - ) + expr=m.LMTD[hot, cold, stg] + == sum( + m.LMTD_discretize[hot, cold, stg, size] for size in m.possible_sizes + ), + doc="Discretized LMTD for each match.", ) @disj.modular.Constraint(m.possible_sizes) def discretized_LMTD_LB(disj, size): + """ + Sets the lower bound on the discretized Log Mean Temperature Difference (LMTD) for each possible size. + + Parameters + ---------- + disj : Pyomo.Disjunct + The disjunct object representing a specific module size. + size : int + Module size under consideration. + + Returns + ------- + Pyomo.Constraint + A constraint that sets the lower limit for the discretized LMTD based on the module size active in the configuration. + """ return ( - m.LMTD[hot, cold, stg].lb - * m.module_size_active[hot, cold, stg, size] + m.LMTD[hot, cold, stg].lb * m.module_size_active[hot, cold, stg, size] ) <= m.LMTD_discretize[hot, cold, stg, size] @disj.modular.Constraint(m.possible_sizes) def discretized_LMTD_UB(disj, size): + """ + Sets the upper bound on the discretized Log Mean Temperature Difference (LMTD) for each possible size. + + Parameters + ---------- + disj : Pyomo.Disjunct + The disjunct object representing a specific module size. + size : int + Module size under consideration. + + Returns + ------- + Pyomo.Constraint + A constraint that sets the upper limit for the discretized LMTD based on the module size active in the configuration. + """ return m.LMTD_discretize[hot, cold, stg, size] <= ( - m.LMTD[hot, cold, stg].ub - * m.module_size_active[hot, cold, stg, size] + m.LMTD[hot, cold, stg].ub * m.module_size_active[hot, cold, stg, size] ) disj.modular.exchanger_required_area = Constraint( - expr=m.U[hot, cold] * sum( + expr=m.U[hot, cold] + * sum( area * m.LMTD_discretize[hot, cold, stg, area] - for area in m.possible_sizes) >= - m.heat_exchanged[hot, cold, stg]) + for area in m.possible_sizes + ) + >= m.heat_exchanged[hot, cold, stg], + doc="Calculates the required area based on heat exchanged and LMTD.", + ) disj.modular_or_not = Disjunction( - expr=[disj.modular, disj.conventional]) + expr=[disj.modular, disj.conventional], + doc="Disjunction between modular and conventional configurations.", + ) return m From 8e49bb4f763c6be2e52cc7ad9c8081e67bf63e81 Mon Sep 17 00:00:00 2001 From: Albert Lee Date: Sat, 27 Apr 2024 16:43:39 -0400 Subject: [PATCH 74/99] Refactor function parameter name in modular_discrete_single_module.py for clarity --- gdplib/mod_hens/modular_integer.py | 183 +++++++++++++++++++++++------ 1 file changed, 150 insertions(+), 33 deletions(-) diff --git a/gdplib/mod_hens/modular_integer.py b/gdplib/mod_hens/modular_integer.py index 14dcf91..6034037 100644 --- a/gdplib/mod_hens/modular_integer.py +++ b/gdplib/mod_hens/modular_integer.py @@ -10,13 +10,28 @@ """ from __future__ import division -from pyomo.environ import (Constraint, Integers, log, Var) +from pyomo.environ import Constraint, Integers, log, Var from pyomo.gdp import Disjunct, Disjunction from gdplib.mod_hens import common def build_single_module(cafaro_approx, num_stages): + """ + Constructs a Pyomo model configured to exclusively use modular heat exchangers, forcing selection of a single module type per stage and stream match. This configuration utilizes the Cafaro approximation if specified. + + Parameters + ---------- + cafaro_approx : bool + Specifies whether to use the Cafaro approximation in the model. + num_stages : int + The number of stages in the heat integration model. + + Returns + ------- + Pyomo.ConcreteModel + A Pyomo ConcreteModel optimized with constraints for modular heat exchanger configurations, fixed to a single module type per valid match. + """ m = build_model(cafaro_approx, num_stages) # Require modular for hot, cold, stg in m.valid_matches * m.stages: @@ -27,20 +42,52 @@ def build_single_module(cafaro_approx, num_stages): # Must choose only one type of module @m.Disjunct(m.module_sizes) def module_type(disj, size): - """Disjunct for selection of one module type.""" + """ + Disjunct for selection of one module type. + + Parameters + ---------- + disj : Pyomo.Disjunct + _description_ + size : int + Module size under consideration. + """ disj.no_other_module_types = Constraint( - expr=sum(m.num_modules[hot, cold, stage, area] - for hot, cold in m.valid_matches - for stage in m.stages - for area in m.module_sizes - if area != size) == 0) + expr=sum( + m.num_modules[hot, cold, stage, area] + for hot, cold in m.valid_matches + for stage in m.stages + for area in m.module_sizes + if area != size + ) + == 0, + doc="Ensures no modules of other sizes are active when this size is selected.", + ) + m.select_one_module_type = Disjunction( - expr=[m.module_type[area] for area in m.module_sizes]) + expr=[m.module_type[area] for area in m.module_sizes], + doc="Select one module type", + ) return m def build_require_modular(cafaro_approx, num_stages): + """ + Builds a Pyomo model that requires the use of modular configurations for all heat exchangers within the model. This setup deactivates any conventional exchanger configurations. + + Parameters + ---------- + cafaro_approx : bool + Specifies whether to use the Cafaro approximation in the model. + num_stages : int + The number of stages in the heat integration model. + + Returns + ------- + Pyomo.ConcreteModel + A Pyomo model configured to require modular heat exchangers throughout the network. + """ m = build_model(cafaro_approx, num_stages) # Require modular for hot, cold, stg in m.valid_matches * m.stages: @@ -50,17 +97,51 @@ def build_require_modular(cafaro_approx, num_stages): def build_modular_option(cafaro_approx, num_stages): + """ + Builds a Pyomo model that can optionally use modular heat exchangers based on configuration decisions within the model. This function initializes a model using the Cafaro approximation as specified. + + Parameters + ---------- + cafaro_approx : bool + Specifies whether to use the Cafaro approximation in the model. + num_stages : int + The number of stages in the heat integration model. + + Returns + ------- + Pyomo.ConcreteModel + Returns a Pyomo model with the flexibility to choose modular heat exchanger configurations based on optimization results. + """ return build_model(cafaro_approx, num_stages) def build_model(use_cafaro_approximation, num_stages): - """Build the model.""" + """ + Base function for constructing a heat exchange network model with optional use of Cafaro approximation and integration of modular heat exchangers represented by integer variables. + + Parameters + ---------- + use_cafaro_approximation : bool + Specifies whether to use the Cafaro approximation in the model. + num_stages : int + The number of stages in the heat integration model. + + Returns + ------- + Pyomo.ConcreteModel + The initialized Pyomo model including both conventional and modular heat exchanger options. + """ m = common.build_model(use_cafaro_approximation, num_stages) m.num_modules = Var( - m.valid_matches, m.stages, m.module_sizes, + m.valid_matches, + m.stages, + m.module_sizes, doc="The number of modules of each size at each exchanger.", - domain=Integers, bounds=(0, 100), initialize=0) + domain=Integers, + bounds=(0, 100), + initialize=0, + ) # improve quality of bounds for size in m.module_sizes: for var in m.num_modules[:, :, :, size]: @@ -72,48 +153,84 @@ def build_model(use_cafaro_approximation, num_stages): disj.conventional = Disjunct() if not use_cafaro_approximation: disj.conventional.exchanger_area_cost = Constraint( - expr=m.exchanger_area_cost[stg, hot, cold] * 1E-3 >= - m.exchanger_area_cost_factor[hot, cold] * 1E-3 * - m.exchanger_area[stg, hot, cold] ** m.area_cost_exponent) + expr=m.exchanger_area_cost[stg, hot, cold] * 1e-3 + >= m.exchanger_area_cost_factor[hot, cold] + * 1e-3 + * m.exchanger_area[stg, hot, cold] ** m.area_cost_exponent, + doc="Ensures area cost meets the standard cost scaling.", + ) else: disj.conventional.exchanger_area_cost = Constraint( - expr=m.exchanger_area_cost[stg, hot, cold] * 1E-3 >= - m.exchanger_area_cost_factor[hot, cold] * 1E-3 * m.cafaro_k - * log(m.cafaro_b * m.exchanger_area[stg, hot, cold] + 1) + expr=m.exchanger_area_cost[stg, hot, cold] * 1e-3 + >= m.exchanger_area_cost_factor[hot, cold] + * 1e-3 + * m.cafaro_k + * log(m.cafaro_b * m.exchanger_area[stg, hot, cold] + 1), + doc="Applies Cafaro's logarithmic cost scaling to area cost.", ) m.BigM[disj.conventional.exchanger_area_cost] = 100 disj.conventional.exchanger_fixed_cost = Constraint( - expr=m.exchanger_fixed_cost[stg, hot, cold] == - m.exchanger_fixed_unit_cost[hot, cold]) + expr=m.exchanger_fixed_cost[stg, hot, cold] + == m.exchanger_fixed_unit_cost[hot, cold], + doc="Sets fixed cost for the exchanger based on unit costs.", + ) @disj.conventional.Constraint(m.module_sizes) def no_modules(_, area): + """ + Ensures that no modules are active in the conventional configuration. + + Parameters + ---------- + _ : Pyomo.ConcreteModel + The Pyomo model instance, not used directly in the function + area : float + The modular area size of the heat exchanger + + Returns + ------- + Pyomo.Constraint + A constraint that forces the module size active variables to zero, ensuring no modular units are mistakenly considered in conventional configurations. + """ return m.num_modules[hot, cold, stg, area] == 0 disj.modular = Disjunct() disj.modular.exchanger_area_cost = Constraint( - expr=m.exchanger_area_cost[stg, hot, cold] * 1E-3 == - sum(m.module_area_cost[hot, cold, area] + expr=m.exchanger_area_cost[stg, hot, cold] * 1e-3 + == sum( + m.module_area_cost[hot, cold, area] * m.num_modules[hot, cold, stg, area] - for area in m.module_sizes) - * 1E-3) + for area in m.module_sizes + ) + * 1e-3, + doc="Area cost for modular exchanger", + ) disj.modular.exchanger_fixed_cost = Constraint( - expr=m.exchanger_fixed_cost[stg, hot, cold] == - m.module_fixed_unit_cost * sum(m.num_modules[hot, cold, stg, area] - for area in m.module_sizes)) + expr=m.exchanger_fixed_cost[stg, hot, cold] + == m.module_fixed_unit_cost + * sum(m.num_modules[hot, cold, stg, area] for area in m.module_sizes), + doc="Fixed cost for modular exchanger", + ) disj.modular.exchanger_area = Constraint( - expr=m.exchanger_area[stg, hot, cold] == - sum(area * m.num_modules[hot, cold, stg, area] - for area in m.module_sizes)) + expr=m.exchanger_area[stg, hot, cold] + == sum( + area * m.num_modules[hot, cold, stg, area] for area in m.module_sizes + ), + doc="Area for modular exchanger", + ) disj.modular_or_not = Disjunction( - expr=[disj.modular, disj.conventional]) + expr=[disj.modular, disj.conventional], + doc="Module or conventional exchanger", + ) # Area requirement disj.exchanger_required_area = Constraint( - expr=m.exchanger_area[stg, hot, cold] * ( - m.U[hot, cold] * m.LMTD[hot, cold, stg]) >= - m.heat_exchanged[hot, cold, stg]) + expr=m.exchanger_area[stg, hot, cold] + * (m.U[hot, cold] * m.LMTD[hot, cold, stg]) + >= m.heat_exchanged[hot, cold, stg], + doc="Area requirement for exchanger", + ) m.BigM[disj.exchanger_required_area] = 5000 return m From 788934be9c2756d0a61d46761817cb56b89a2c4b Mon Sep 17 00:00:00 2001 From: Albert Lee Date: Sat, 27 Apr 2024 17:37:24 -0400 Subject: [PATCH 75/99] Refactor alphanumeric sorting functions in util.py --- gdplib/stranded_gas/util.py | 39 +++++++++++++++++++++++++++++++++++-- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/gdplib/stranded_gas/util.py b/gdplib/stranded_gas/util.py index b2822a7..e61a60b 100644 --- a/gdplib/stranded_gas/util.py +++ b/gdplib/stranded_gas/util.py @@ -3,6 +3,19 @@ # Alphanumeric sort, taken from https://nedbatchelder.com/blog/200712/human_sorting.html def tryint(s): + """ + Attempts to convert a string to an integer. If conversion fails, returns the original string. + + Parameters + ---------- + s : str + The string to convert to an integer. + + Returns + ------- + int or str + An integer if `s` can be converted, otherwise the original string. + """ try: return int(s) except ValueError: @@ -10,13 +23,35 @@ def tryint(s): def alphanum_key(s): - """ Turn a string into a list of string and number chunks. + """ + Turn a string into a list of string and number chunks. "z23a" -> ["z", 23, "a"] + + Parameters + ---------- + s : str + The string to split into chunks of strings and integers. + + Returns + ------- + list + A list of strings and integers extracted from the input string. """ return [tryint(c) for c in re.split('([0-9]+)', s)] def alphanum_sorted(l): - """ Sort the given list in the way that humans expect. + """ + Sort the given list in the way that humans expect. + + Parameters + ---------- + l : list + The list of strings to sort. + + Returns + ------- + list + The list sorted in human-like alphanumeric order. """ return sorted(l, key=alphanum_key) From 3a5a05f290a91b81c74e3297b34fd6d48fd16af7 Mon Sep 17 00:00:00 2001 From: Albert Lee Date: Sat, 27 Apr 2024 17:47:48 -0400 Subject: [PATCH 76/99] References added on the model.py --- gdplib/stranded_gas/model.py | 631 +++++++++++++++++++++++++++++++++++ 1 file changed, 631 insertions(+) diff --git a/gdplib/stranded_gas/model.py b/gdplib/stranded_gas/model.py index e563078..a43e583 100644 --- a/gdplib/stranded_gas/model.py +++ b/gdplib/stranded_gas/model.py @@ -14,6 +14,17 @@ def build_model(): + """_summary_ + + Returns + ------- + _type_ + _description_ + + References + ---------- + [1] Chen, Q., & Grossmann, I. E. (2019). Economies of numbers for a modular stranded gas processing network: Modeling and optimization. In Computer Aided Chemical Engineering (Vol. 47, pp. 257-262). Elsevier. DOI: 10.1016/B978-0-444-64241-7.50100-3 + """ m = ConcreteModel() m.BigM = Suffix(direction=Suffix.LOCAL) @@ -29,6 +40,20 @@ def build_model(): @m.Param(m.time) def discount_factor(m, t): + """_summary_ + + Parameters + ---------- + m : _type_ + _description_ + t : _type_ + _description_ + + Returns + ------- + _type_ + _description_ + """ 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) @@ -37,18 +62,74 @@ def discount_factor(m, t): @m.Param(m.module_types) def module_base_cost(m, mtype): + """_summary_ + + Parameters + ---------- + m : _type_ + _description_ + mtype : _type_ + _description_ + + Returns + ------- + _type_ + _description_ + """ return float(module_sheet[mtype]['Capital Cost [MM$]']) @m.Param(m.module_types, doc="Natural gas consumption per module of this type [MMSCF/d]") def unit_gas_consumption(m, mtype): + """_summary_ + + Parameters + ---------- + m : _type_ + _description_ + mtype : _type_ + _description_ + + Returns + ------- + _type_ + _description_ + """ return float(module_sheet[mtype]['Nat Gas [MMSCF/d]']) @m.Param(m.module_types, doc="Gasoline production per module of this type [kBD]") def gasoline_production(m, mtype): + """_summary_ + + Parameters + ---------- + m : _type_ + _description_ + mtype : _type_ + _description_ + + Returns + ------- + _type_ + _description_ + """ 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]") def module_conversion(m, mtype): + """_summary_ + + Parameters + ---------- + m : _type_ + _description_ + mtype : _type_ + _description_ + + Returns + ------- + _type_ + _description_ + """ return float(module_sheet[mtype]['Conversion [kB/MMSCF]']) site_sheet = xlsx_data['sites'].set_index('Potential site') @@ -60,10 +141,38 @@ def module_conversion(m, mtype): @m.Param(m.potential_sites) def site_x(m, site): + """_summary_ + + Parameters + ---------- + m : _type_ + _description_ + site : _type_ + _description_ + + Returns + ------- + _type_ + _description_ + """ return float(site_sheet['x'][site]) @m.Param(m.potential_sites) def site_y(m, site): + """_summary_ + + Parameters + ---------- + m : _type_ + _description_ + site : _type_ + _description_ + + Returns + ------- + _type_ + _description_ + """ return float(site_sheet['y'][site]) well_sheet = xlsx_data['wells'].set_index('Well') @@ -71,10 +180,38 @@ def site_y(m, site): @m.Param(m.well_clusters) def well_x(m, well): + """_summary_ + + Parameters + ---------- + m : _type_ + _description_ + well : _type_ + _description_ + + Returns + ------- + _type_ + _description_ + """ return float(well_sheet['x'][well]) @m.Param(m.well_clusters) def well_y(m, well): + """_summary_ + + Parameters + ---------- + m : _type_ + _description_ + well : _type_ + _description_ + + Returns + ------- + _type_ + _description_ + """ return float(well_sheet['y'][well]) sched_sheet = xlsx_data['well-schedule'] @@ -89,6 +226,22 @@ def well_y(m, well): @m.Param(m.well_clusters, m.time, doc="Supply of gas from well cluster [MMSCF/day]") def gas_supply(m, well, t): + """_summary_ + + Parameters + ---------- + m : _type_ + _description_ + well : _type_ + _description_ + t : _type_ + _description_ + + Returns + ------- + _type_ + _description_ + """ return sum(well_profiles[well][t * 3:t * 3 + 2]) / 3 mkt_sheet = xlsx_data['markets'].set_index('Market') @@ -96,10 +249,38 @@ def gas_supply(m, well, t): @m.Param(m.markets) def mkt_x(m, mkt): + """_summary_ + + Parameters + ---------- + m : _type_ + _description_ + mkt : _type_ + _description_ + + Returns + ------- + _type_ + _description_ + """ return float(mkt_sheet['x'][mkt]) @m.Param(m.markets) def mkt_y(m, mkt): + """_summary_ + + Parameters + ---------- + m : _type_ + _description_ + mkt : _type_ + _description_ + + Returns + ------- + _type_ + _description_ + """ return float(mkt_sheet['y'][mkt]) @m.Param(m.markets, doc="Gasoline demand [kBD]") @@ -111,6 +292,22 @@ def mkt_demand(m, mkt): @m.Param(m.sources, m.destinations, doc="Distance [mi]") def distance(m, src, dest): + """_summary_ + + Parameters + ---------- + m : _type_ + _description_ + src : _type_ + _description_ + dest : _type_ + _description_ + + Returns + ------- + _type_ + _description_ + """ if src in m.well_clusters: src_x = m.well_x[src] src_y = m.well_y[src] @@ -142,22 +339,92 @@ def distance(m, src, dest): @m.Param(m.time, doc="Module transport cost per mile [M$/100 miles]") def module_transport_distance_cost(m, t): + """_summary_ + + Parameters + ---------- + m : _type_ + _description_ + t : _type_ + _description_ + + Returns + ------- + _type_ + _description_ + """ return 50 * m.discount_factor[t] @m.Param(m.time, doc="Module transport cost per unit [MM$/module]") def module_transport_unit_cost(m, t): + """_summary_ + + Parameters + ---------- + m : _type_ + _description_ + t : _type_ + _description_ + + Returns + ------- + _type_ + _description_ + """ return 3 * m.discount_factor[t] @m.Param(m.time, doc="Stranded gas price [$/MSCF]") def nat_gas_price(m, t): + """_summary_ + + Parameters + ---------- + m : _type_ + _description_ + t : _type_ + _description_ + + Returns + ------- + _type_ + _description_ + """ return 5 * m.discount_factor[t] @m.Param(m.time, doc="Gasoline price [$/gal]") def gasoline_price(m, t): + """_summary_ + + Parameters + ---------- + m : _type_ + _description_ + t : _type_ + _description_ + + Returns + ------- + _type_ + _description_ + """ return 2.5 * m.discount_factor[t] @m.Param(m.time, doc="Gasoline transport cost [$/gal/100 miles]") def gasoline_tranport_cost(m, t): + """_summary_ + + Parameters + ---------- + m : _type_ + _description_ + t : _type_ + _description_ + + Returns + ------- + _type_ + _description_ + """ return 0.045 * m.discount_factor[t] m.gal_per_bbl = Param(initialize=42, doc="Gallons per barrel") @@ -170,6 +437,15 @@ def gasoline_tranport_cost(m, t): @m.Disjunct(m.module_types) def mtype_exists(disj, mtype): + """_summary_ + + Parameters + ---------- + disj : _type_ + _description_ + mtype : _type_ + _description_ + """ disj.learning_factor_calc = Constraint( expr=m.learning_factor[mtype] == (1 - m.learning_rate) ** ( log(sum(m.modules_purchased[mtype, :, :])) / log(2))) @@ -179,15 +455,54 @@ def mtype_exists(disj, mtype): @m.Disjunct(m.module_types) def mtype_absent(disj, mtype): + """_summary_ + + Parameters + ---------- + disj : _type_ + _description_ + mtype : _type_ + _description_ + """ disj.constant_learning_factor = Constraint( expr=m.learning_factor[mtype] == 1) @m.Disjunction(m.module_types) def mtype_existence(m, mtype): + """_summary_ + + Parameters + ---------- + m : _type_ + _description_ + mtype : _type_ + _description_ + + Returns + ------- + _type_ + _description_ + """ return [m.mtype_exists[mtype], m.mtype_absent[mtype]] @m.Expression(m.module_types, m.time, doc="Module unit cost [MM$/module]") def module_unit_cost(m, mtype, t): + """_summary_ + + Parameters + ---------- + m : _type_ + _description_ + mtype : _type_ + _description_ + t : _type_ + _description_ + + Returns + ------- + _type_ + _description_ + """ return m.module_base_cost[mtype] * m.learning_factor[mtype] * m.discount_factor[t] m.production = Var( @@ -210,37 +525,153 @@ def module_unit_cost(m, mtype, t): @m.Constraint(m.potential_sites, m.module_types, m.time) def consumption_capacity(m, site, mtype, t): + """_summary_ + + Parameters + ---------- + m : _type_ + _description_ + site : _type_ + _description_ + mtype : _type_ + _description_ + t : _type_ + _description_ + + Returns + ------- + _type_ + _description_ + """ return m.gas_consumption[site, mtype, t] <= ( m.num_modules[mtype, site, t] * m.unit_gas_consumption[mtype]) @m.Constraint(m.potential_sites, m.time) def production_limit(m, site, t): + """_summary_ + + Parameters + ---------- + m : _type_ + _description_ + site : _type_ + _description_ + t : _type_ + _description_ + + Returns + ------- + _type_ + _description_ + """ return m.production[site, t] <= sum( m.gas_consumption[site, mtype, t] * m.module_conversion[mtype] for mtype in m.module_types) @m.Expression(m.potential_sites, m.time) def capacity(m, site, t): + """_summary_ + + Parameters + ---------- + m : _type_ + _description_ + site : _type_ + _description_ + t : _type_ + _description_ + + Returns + ------- + _type_ + _description_ + """ return sum( 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): + """_summary_ + + Parameters + ---------- + m : _type_ + _description_ + site : _type_ + _description_ + t : _type_ + _description_ + + Returns + ------- + _type_ + _description_ + """ return sum(m.gas_consumption[site, :, t]) == sum(m.gas_flows[:, site, t]) @m.Constraint(m.well_clusters, m.time) def gas_supply_limit(m, well, t): + """_summary_ + + Parameters + ---------- + m : _type_ + _description_ + well : _type_ + _description_ + t : _type_ + _description_ + + Returns + ------- + _type_ + _description_ + """ 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): + """_summary_ + + Parameters + ---------- + m : _type_ + _description_ + site : _type_ + _description_ + t : _type_ + _description_ + + Returns + ------- + _type_ + _description_ + """ 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): + """_summary_ + + Parameters + ---------- + m : _type_ + _description_ + site : _type_ + _description_ + mtype : _type_ + _description_ + t : _type_ + _description_ + + Returns + ------- + _type_ + _description_ + """ if t >= m.module_setup_time: modules_added = m.modules_purchased[ mtype, site, t - m.module_setup_time] @@ -265,10 +696,28 @@ def module_balance(m, site, mtype, t): @m.Disjunct(m.potential_sites) def site_active(disj, site): + """_summary_ + + Parameters + ---------- + disj : _type_ + _description_ + site : _type_ + _description_ + """ pass @m.Disjunct(m.potential_sites) def site_inactive(disj, site): + """_summary_ + + Parameters + ---------- + disj : _type_ + _description_ + site : _type_ + _description_ + """ disj.no_production = Constraint( expr=sum(m.production[site, :]) == 0) disj.no_gas_consumption = Constraint( @@ -293,24 +742,90 @@ def site_inactive(disj, site): @m.Disjunction(m.potential_sites) def site_active_or_not(m, site): + """_summary_ + + Parameters + ---------- + m : _type_ + _description_ + site : _type_ + _description_ + + Returns + ------- + _type_ + _description_ + """ return [m.site_active[site], m.site_inactive[site]] @m.Disjunct(m.well_clusters, m.potential_sites) def pipeline_exists(disj, well, site): + """_summary_ + + Parameters + ---------- + disj : _type_ + _description_ + well : _type_ + _description_ + site : _type_ + _description_ + """ pass @m.Disjunct(m.well_clusters, m.potential_sites) def pipeline_absent(disj, well, site): + """_summary_ + + Parameters + ---------- + disj : _type_ + _description_ + well : _type_ + _description_ + site : _type_ + _description_ + """ disj.no_natural_gas_flow = Constraint( expr=sum(m.gas_flows[well, site, t] for t in m.time) == 0) @m.Disjunction(m.well_clusters, m.potential_sites) def pipeline_existence(m, well, site): + """_summary_ + + Parameters + ---------- + m : _type_ + _description_ + well : _type_ + _description_ + site : _type_ + _description_ + + Returns + ------- + _type_ + _description_ + """ return [m.pipeline_exists[well, site], m.pipeline_absent[well, site]] # Objective Function Construnction @m.Expression(m.potential_sites, doc="MM$") def product_revenue(m, site): + """_summary_ + + Parameters + ---------- + m : _type_ + _description_ + site : _type_ + _description_ + + Returns + ------- + _type_ + _description_ + """ return sum( m.product_flows[site, mkt, t] # kBD * 1000 # bbl/kB @@ -322,6 +837,20 @@ def product_revenue(m, site): @m.Expression(m.potential_sites, doc="MM$") def raw_material_cost(m, site): + """_summary_ + + Parameters + ---------- + m : _type_ + _description_ + site : _type_ + _description_ + + Returns + ------- + _type_ + _description_ + """ return sum( m.gas_consumption[site, mtype, t] * m.days_per_period / 1E6 # $ to MM$ @@ -333,6 +862,22 @@ def raw_material_cost(m, site): m.potential_sites, m.markets, doc="Aggregate cost to transport gasoline from a site to market [MM$]") def product_transport_cost(m, site, mkt): + """_summary_ + + Parameters + ---------- + m : _type_ + _description_ + site : _type_ + _description_ + mkt : _type_ + _description_ + + Returns + ------- + _type_ + _description_ + """ return sum( m.product_flows[site, mkt, t] * m.gal_per_bbl * 1000 # bbl/kB @@ -342,12 +887,44 @@ def product_transport_cost(m, site, mkt): @m.Expression(m.well_clusters, m.potential_sites, doc="MM$") def pipeline_construction_cost(m, well, site): + """_summary_ + + Parameters + ---------- + m : _type_ + _description_ + well : _type_ + _description_ + site : _type_ + _description_ + + Returns + ------- + _type_ + _description_ + """ 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$") def module_relocation_cost(m, from_site, to_site): + """_summary_ + + Parameters + ---------- + m : _type_ + _description_ + from_site : _type_ + _description_ + to_site : _type_ + _description_ + + Returns + ------- + _type_ + _description_ + """ return sum( m.modules_transferred[mtype, from_site, to_site, t] * m.distance[from_site, to_site] / 100 @@ -360,6 +937,20 @@ def module_relocation_cost(m, from_site, to_site): @m.Expression(m.potential_sites, doc="MM$") def module_purchase_cost(m, site): + """_summary_ + + Parameters + ---------- + m : _type_ + _description_ + site : _type_ + _description_ + + Returns + ------- + _type_ + _description_ + """ return sum( m.module_unit_cost[mtype, t] * m.modules_purchased[mtype, site, t] for mtype in m.module_types @@ -367,6 +958,18 @@ def module_purchase_cost(m, site): @m.Expression(doc="MM$") def profit(m): + """_summary_ + + Parameters + ---------- + m : _type_ + _description_ + + Returns + ------- + _type_ + _description_ + """ return ( summation(m.product_revenue) - summation(m.raw_material_cost) @@ -381,10 +984,38 @@ def profit(m): # Tightening constraints @m.Constraint(doc="Limit total module purchases over project span.") def restrict_module_purchases(m): + """_summary_ + + Parameters + ---------- + m : _type_ + _description_ + + Returns + ------- + _type_ + _description_ + """ return sum(m.modules_purchased[...]) <= 5 @m.Constraint(m.site_pairs, doc="Limit transfers between any two sites") def restrict_module_transfers(m, from_site, to_site): + """_summary_ + + Parameters + ---------- + m : _type_ + _description_ + from_site : _type_ + _description_ + to_site : _type_ + _description_ + + Returns + ------- + _type_ + _description_ + """ return sum(m.modules_transferred[:, from_site, to_site, :]) <= 5 return m From 03a1686734906b6e5439d36a48a8b936571832a8 Mon Sep 17 00:00:00 2001 From: Albert Lee Date: Sat, 27 Apr 2024 19:29:34 -0400 Subject: [PATCH 77/99] Document the parameters of the stranded gas models. --- gdplib/stranded_gas/model.py | 555 +++++++++++++++++++---------------- 1 file changed, 296 insertions(+), 259 deletions(-) diff --git a/gdplib/stranded_gas/model.py b/gdplib/stranded_gas/model.py index a43e583..6a47329 100644 --- a/gdplib/stranded_gas/model.py +++ b/gdplib/stranded_gas/model.py @@ -14,12 +14,13 @@ def build_model(): - """_summary_ + """ + Constructs a Pyomo ConcreteModel for optimizing a modular stranded gas processing network. The model is designed to convert stranded gas into gasoline using a modular and intensified GTL process. It incorporates the economic dynamics of module investments, gas processing, and product transportation. Returns ------- - _type_ - _description_ + Pyomo.ConcreteModel + A Pyomo ConcreteModel that represents the multi-period optimization problem of a GTL (Gas To Liquid) network. The model includes variables for the number of modules, their type and placement, as well as for production and transportation of gasoline. It aims to maximize the net present value (NPV) of the processing network. References ---------- @@ -40,100 +41,105 @@ def build_model(): @m.Param(m.time) def discount_factor(m, t): - """_summary_ + """ + Calculates the discount factor for a given time period in the model. Parameters ---------- - m : _type_ - _description_ - t : _type_ - _description_ + m : Pyomo.ConcreteModel + A Pyomo ConcreteModel that represents the multi-period optimization problem of a GTL network. + t : int + Set of time periods quarterly period within the 15 year project life. Returns ------- - _type_ - _description_ + Pyomo.Parameter + A float representing the discount factor for the given time period. """ 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) module_sheet = xlsx_data['modules'].set_index('Type') - m.module_types = Set(initialize=module_sheet.columns.tolist(),) + m.module_types = Set(initialize=module_sheet.columns.tolist(), doc="Module types") @m.Param(m.module_types) def module_base_cost(m, mtype): - """_summary_ + """ + Calculates the base cost of a module of a given type. Parameters ---------- - m : _type_ - _description_ - mtype : _type_ - _description_ + m : Pyomo.ConcreteModel + A Pyomo ConcreteModel that represents the multi-period optimization problem of a GTL network. + mtype : str + Index of the module type (A500, R500, S500, U500, A1000, R1000, S1000, U1000, A2000, R2000, S2000, U2000, A5000, R5000). Returns ------- - _type_ - _description_ + Pyomo.Parameter + A float representing the base cost of a module of the given type. """ return float(module_sheet[mtype]['Capital Cost [MM$]']) @m.Param(m.module_types, doc="Natural gas consumption per module of this type [MMSCF/d]") def unit_gas_consumption(m, mtype): - """_summary_ + """ + Calculates the natural gas consumption per module of a given type. Parameters ---------- - m : _type_ - _description_ - mtype : _type_ - _description_ + m : Pyomo.ConcreteModel + A Pyomo ConcreteModel that represents the multi-period optimization problem of a GTL network. + mtype : str + Index of the module type. Returns ------- - _type_ - _description_ + Pyomo.Parameter + A float representing the natural gas consumption per module of the given type. """ return float(module_sheet[mtype]['Nat Gas [MMSCF/d]']) @m.Param(m.module_types, doc="Gasoline production per module of this type [kBD]") def gasoline_production(m, mtype): - """_summary_ + """ + Calculates the gasoline production per module of a given type. Parameters ---------- - m : _type_ - _description_ - mtype : _type_ - _description_ + m : Pyomo.ConcreteModel + A Pyomo ConcreteModel that represents the multi-period optimization problem of a GTL network. + mtype : str + Index of the module type. Returns ------- - _type_ - _description_ + Pyomo.Parameter + A float representing the gasoline production per module of the given type. """ 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]") def module_conversion(m, mtype): - """_summary_ + """ + Calculates the overall conversion of natural gas into gasoline per module of a given type. Parameters ---------- - m : _type_ - _description_ - mtype : _type_ - _description_ + m : Pyomo.ConcreteModel + A Pyomo ConcreteModel that represents the multi-period optimization problem of a GTL network. + mtype : str + Index of the module type. Returns ------- - _type_ - _description_ + Pyomo.Parameter + A float representing the overall conversion of natural gas into gasoline per module of the given type. """ return float(module_sheet[mtype]['Conversion [kB/MMSCF]']) site_sheet = xlsx_data['sites'].set_index('Potential site') - m.potential_sites = Set(initialize=site_sheet.index.tolist()) + m.potential_sites = Set(initialize=site_sheet.index.tolist(), doc="Potential sites") m.site_pairs = Set( doc="Pairs of potential sites", initialize=m.potential_sites * m.potential_sites, @@ -141,76 +147,80 @@ def module_conversion(m, mtype): @m.Param(m.potential_sites) def site_x(m, site): - """_summary_ + """ + Calculates the x-coordinate of a potential site. Parameters ---------- - m : _type_ - _description_ - site : _type_ - _description_ + m : Pyomo.ConcreteModel + A Pyomo ConcreteModel that represents the multi-period optimization problem of a GTL network. + site : str + The index for the potential site. Returns ------- - _type_ - _description_ + Pyomo.Parameter + An integer representing the x-coordinate of the potential site. """ return float(site_sheet['x'][site]) @m.Param(m.potential_sites) def site_y(m, site): - """_summary_ + """ + Calculates the y-coordinate of a potential site. Parameters ---------- - m : _type_ - _description_ - site : _type_ - _description_ + m : Pyomo.ConcreteModel + A Pyomo ConcreteModel that represents the multi-period optimization problem of a GTL network. + site : str + The index for the potential site. Returns ------- - _type_ - _description_ + Pyomo.Parameter + An integer representing the y-coordinate of the potential site. """ return float(site_sheet['y'][site]) well_sheet = xlsx_data['wells'].set_index('Well') - m.well_clusters = Set(initialize=well_sheet.index.tolist()) + m.well_clusters = Set(initialize=well_sheet.index.tolist(), doc="Well clusters") @m.Param(m.well_clusters) def well_x(m, well): - """_summary_ + """ + Calculates the x-coordinate of a well cluster. Parameters ---------- - m : _type_ - _description_ - well : _type_ - _description_ + m : Pyomo.ConcreteModel + A Pyomo ConcreteModel that represents the multi-period optimization problem of a GTL network. + well : str + The index for the well cluster. It starts from w1 and goes up to w12. Returns ------- - _type_ - _description_ + Pyomo.Parameter + An integer representing the x-coordinate of the well cluster. """ return float(well_sheet['x'][well]) @m.Param(m.well_clusters) def well_y(m, well): - """_summary_ + """ + Calculates the y-coordinate of a well cluster. Parameters ---------- - m : _type_ - _description_ - well : _type_ - _description_ + m : Pyomo.ConcreteModel + A Pyomo ConcreteModel that represents the multi-period optimization problem of a GTL network. + well : str + The index for the well cluster. Returns ------- - _type_ - _description_ + Pyomo.Parameter + An integer representing the y-coordinate of the well cluster. """ return float(well_sheet['y'][well]) @@ -226,87 +236,109 @@ def well_y(m, well): @m.Param(m.well_clusters, m.time, doc="Supply of gas from well cluster [MMSCF/day]") def gas_supply(m, well, t): - """_summary_ + """ + Calculates the supply of gas from a well cluster in a given time period. Parameters ---------- - m : _type_ - _description_ - well : _type_ - _description_ - t : _type_ - _description_ + m : Pyomo.ConcreteModel + A Pyomo ConcreteModel that represents the multi-period optimization problem of a GTL network. + well : str + The index for the well cluster. + t : int + Set of time periods quarterly period within the 15 year project life. Returns ------- - _type_ - _description_ + 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 mkt_sheet = xlsx_data['markets'].set_index('Market') - m.markets = Set(initialize=mkt_sheet.index.tolist()) + m.markets = Set(initialize=mkt_sheet.index.tolist(), doc="Markets") @m.Param(m.markets) def mkt_x(m, mkt): - """_summary_ + """ + Calculates the x-coordinate of a market. Parameters ---------- - m : _type_ - _description_ - mkt : _type_ - _description_ + m : Pyomo.ConcreteModel + A Pyomo ConcreteModel that represents the multi-period optimization problem of a GTL network. + mkt : str + The index for the market. (m1, m2, m3) Returns ------- - _type_ - _description_ + Pyomo.Parameter + An integer representing the x-coordinate of the market. """ return float(mkt_sheet['x'][mkt]) @m.Param(m.markets) def mkt_y(m, mkt): - """_summary_ + """ + Calculates the y-coordinate of a market. Parameters ---------- - m : _type_ - _description_ - mkt : _type_ - _description_ + m : Pyomo.ConcreteModel + A Pyomo ConcreteModel that represents the multi-period optimization problem of a GTL network. + mkt : str + The index for the market. Returns ------- - _type_ - _description_ + Pyomo.Parameter + An integer representing the y-coordinate of the market. """ return float(mkt_sheet['y'][mkt]) @m.Param(m.markets, doc="Gasoline demand [kBD]") def mkt_demand(m, mkt): + """ + Calculates the demand for gasoline in a market in a given time period. + + Parameters + ---------- + m : Pyomo.ConcreteModel + A Pyomo ConcreteModel that represents the multi-period optimization problem of a GTL network. + mkt : str + The index for the market. + + Returns + ------- + Pyomo.Parameter + A float representing the demand for gasoline in the market in the given time period. + """ return float(mkt_sheet['demand [kBD]'][mkt]) - m.sources = Set(initialize=m.well_clusters | m.potential_sites) - m.destinations = Set(initialize=m.potential_sites | m.markets) + m.sources = Set(initialize=m.well_clusters | m.potential_sites, doc="Sources") + m.destinations = Set(initialize=m.potential_sites | m.markets, doc="Destinations") @m.Param(m.sources, m.destinations, doc="Distance [mi]") def distance(m, src, dest): - """_summary_ + """ + 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 ---------- - m : _type_ - _description_ - src : _type_ - _description_ - dest : _type_ - _description_ + m : Pyomo.ConcreteModel + A Pyomo ConcreteModel that represents the multi-period optimization problem of a GTL network. + src : str + The identifier for the source, which could be a well cluster or a potential site. + dest : str + The identifier for the destination, which could be a potential site or a market. Returns ------- - _type_ - _description_ + Pyomo.Parameter + A parameter representing the Euclidean distance between the source and destination. """ if src in m.well_clusters: src_x = m.well_x[src] @@ -339,91 +371,96 @@ def distance(m, src, dest): @m.Param(m.time, doc="Module transport cost per mile [M$/100 miles]") def module_transport_distance_cost(m, t): - """_summary_ + """ + Calculates the module transport cost per mile in a given time period. Parameters ---------- - m : _type_ - _description_ - t : _type_ - _description_ + m : Pyomo.ConcreteModel + A Pyomo ConcreteModel that represents the multi-period optimization problem of a GTL network. + t : int + Set of time periods quarterly period within the 15 year project life. Returns ------- - _type_ - _description_ + Pyomo.Parameter + A float representing the module transport cost per mile in the given time period. """ return 50 * m.discount_factor[t] @m.Param(m.time, doc="Module transport cost per unit [MM$/module]") def module_transport_unit_cost(m, t): - """_summary_ + """ + Calculates the module transport cost per unit in a given time period. Parameters ---------- - m : _type_ - _description_ - t : _type_ - _description_ + m : Pyomo.ConcreteModel + A Pyomo ConcreteModel that represents the multi-period optimization problem of a GTL network. + t : int + Set of time periods quarterly period within the 15 year project life. Returns ------- - _type_ - _description_ + Pyomo.Parameter + A float representing the module transport cost per unit in the given time period. """ return 3 * m.discount_factor[t] @m.Param(m.time, doc="Stranded gas price [$/MSCF]") def nat_gas_price(m, t): - """_summary_ + """ + Calculates the price of stranded gas in a given time period. Parameters ---------- - m : _type_ - _description_ - t : _type_ - _description_ + m : Pyomo.ConcreteModel + A Pyomo ConcreteModel that represents the multi-period optimization problem of a GTL network. + t : int + Set of time periods quarterly period within the 15 year project life. Returns ------- - _type_ - _description_ + Pyomo.Parameter + A float representing the price of stranded gas in the given time period. """ return 5 * m.discount_factor[t] @m.Param(m.time, doc="Gasoline price [$/gal]") def gasoline_price(m, t): - """_summary_ + """ + Calculates the price of gasoline in a given time period. Parameters ---------- - m : _type_ - _description_ - t : _type_ - _description_ + m : Pyomo.ConcreteModel + A Pyomo ConcreteModel that represents the multi-period optimization problem of a GTL network. + t : int + Set of time periods quarterly period within the 15 year project life. Returns ------- - _type_ - _description_ + Pyomo.Parameter + A float representing the price of gasoline in the given time period. """ return 2.5 * m.discount_factor[t] @m.Param(m.time, doc="Gasoline transport cost [$/gal/100 miles]") def gasoline_tranport_cost(m, t): - """_summary_ + """ + Calculates the gasoline transport cost in a given time period. Parameters ---------- - m : _type_ - _description_ - t : _type_ - _description_ + m : Pyomo.ConcreteModel + A Pyomo ConcreteModel that represents the multi-period optimization problem of a GTL network. + t : int + Set of time periods quarterly period within the 15 year project life. Returns ------- - _type_ - _description_ + Pyomo.Parameter + A float representing the gasoline transport cost in the given time period. """ return 0.045 * m.discount_factor[t] @@ -441,10 +478,10 @@ def mtype_exists(disj, mtype): Parameters ---------- - disj : _type_ - _description_ - mtype : _type_ + disj : Pyomo.Disjunct _description_ + mtype : str + Index of the module type. """ disj.learning_factor_calc = Constraint( expr=m.learning_factor[mtype] == (1 - m.learning_rate) ** ( @@ -459,10 +496,10 @@ def mtype_absent(disj, mtype): Parameters ---------- - disj : _type_ - _description_ - mtype : _type_ + disj : Pyomo.Disjunct _description_ + mtype : str + Index of the module type. """ disj.constant_learning_factor = Constraint( expr=m.learning_factor[mtype] == 1) @@ -473,10 +510,10 @@ def mtype_existence(m, mtype): Parameters ---------- - m : _type_ - _description_ - mtype : _type_ - _description_ + m : Pyomo.ConcreteModel + A Pyomo ConcreteModel that represents the multi-period optimization problem of a GTL network. + mtype : str + Index of the module type. Returns ------- @@ -491,12 +528,12 @@ def module_unit_cost(m, mtype, t): Parameters ---------- - m : _type_ - _description_ - mtype : _type_ - _description_ - t : _type_ - _description_ + m : Pyomo.ConcreteModel + A Pyomo ConcreteModel that represents the multi-period optimization problem of a GTL network. + mtype : str + Index of the module type. + t : int + Set of time periods quarterly period within the 15 year project life. Returns ------- @@ -529,14 +566,14 @@ def consumption_capacity(m, site, mtype, t): Parameters ---------- - m : _type_ - _description_ - site : _type_ - _description_ - mtype : _type_ - _description_ - t : _type_ - _description_ + m : Pyomo.ConcreteModel + A Pyomo ConcreteModel that represents the multi-period optimization problem of a GTL network. + site : str + The index for the potential site. + mtype : str + Index of the module type. + t : int + Set of time periods quarterly period within the 15 year project life. Returns ------- @@ -552,12 +589,12 @@ def production_limit(m, site, t): Parameters ---------- - m : _type_ - _description_ - site : _type_ - _description_ - t : _type_ - _description_ + m : Pyomo.ConcreteModel + A Pyomo ConcreteModel that represents the multi-period optimization problem of a GTL network. + site : str + The index for the potential site. + t : int + Set of time periods quarterly period within the 15 year project life. Returns ------- @@ -574,12 +611,12 @@ def capacity(m, site, t): Parameters ---------- - m : _type_ - _description_ - site : _type_ - _description_ - t : _type_ - _description_ + m : Pyomo.ConcreteModel + A Pyomo ConcreteModel that represents the multi-period optimization problem of a GTL network. + site : str + The index for the potential site. + t : int + Set of time periods quarterly period within the 15 year project life. Returns ------- @@ -596,12 +633,12 @@ def gas_supply_meets_consumption(m, site, t): Parameters ---------- - m : _type_ - _description_ - site : _type_ - _description_ - t : _type_ - _description_ + m : Pyomo.ConcreteModel + A Pyomo ConcreteModel that represents the multi-period optimization problem of a GTL network. + site : str + The index for the potential site. + t : int + Set of time periods quarterly period within the 15 year project life. Returns ------- @@ -616,12 +653,12 @@ def gas_supply_limit(m, well, t): Parameters ---------- - m : _type_ - _description_ - well : _type_ - _description_ - t : _type_ - _description_ + m : Pyomo.ConcreteModel + A Pyomo ConcreteModel that represents the multi-period optimization problem of a GTL network. + well : str + The index for the well cluster. + t : int + Set of time periods quarterly period within the 15 year project life. Returns ------- @@ -637,12 +674,12 @@ def gasoline_production_requirement(m, site, t): Parameters ---------- - m : _type_ - _description_ - site : _type_ - _description_ - t : _type_ - _description_ + m : Pyomo.ConcreteModel + A Pyomo ConcreteModel that represents the multi-period optimization problem of a GTL network. + site : str + The index for the potential site. + t : int + Set of time periods quarterly period within the 15 year project life. Returns ------- @@ -658,14 +695,14 @@ def module_balance(m, site, mtype, t): Parameters ---------- - m : _type_ - _description_ - site : _type_ - _description_ - mtype : _type_ - _description_ - t : _type_ - _description_ + m : Pyomo.ConcreteModel + A Pyomo ConcreteModel that represents the multi-period optimization problem of a GTL network. + site : str + The index for the potential site. + mtype : str + Index of the module type. + t : int + Set of time periods quarterly period within the 15 year project life. Returns ------- @@ -700,10 +737,10 @@ def site_active(disj, site): Parameters ---------- - disj : _type_ - _description_ - site : _type_ + disj : Pyomo.Disjunct _description_ + site : str + The index for the potential site. """ pass @@ -713,10 +750,10 @@ def site_inactive(disj, site): Parameters ---------- - disj : _type_ - _description_ - site : _type_ + disj : Pyomo.Disjunct _description_ + site : str + The index for the potential site. """ disj.no_production = Constraint( expr=sum(m.production[site, :]) == 0) @@ -746,10 +783,10 @@ def site_active_or_not(m, site): Parameters ---------- - m : _type_ - _description_ - site : _type_ - _description_ + m : Pyomo.ConcreteModel + A Pyomo ConcreteModel that represents the multi-period optimization problem of a GTL network. + site : str + The index for the potential site. Returns ------- @@ -764,12 +801,12 @@ def pipeline_exists(disj, well, site): Parameters ---------- - disj : _type_ - _description_ - well : _type_ - _description_ - site : _type_ + disj : Pyomo.Disjunct _description_ + well : str + The index for the well cluster. + site : str + The index for the potential site. """ pass @@ -779,12 +816,12 @@ def pipeline_absent(disj, well, site): Parameters ---------- - disj : _type_ - _description_ - well : _type_ - _description_ - site : _type_ + disj : Pyomo.Disjunct _description_ + well : str + The index for the well cluster. + site : str + 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) @@ -795,12 +832,12 @@ def pipeline_existence(m, well, site): Parameters ---------- - m : _type_ - _description_ - well : _type_ - _description_ - site : _type_ - _description_ + m : Pyomo.ConcreteModel + A Pyomo ConcreteModel that represents the multi-period optimization problem of a GTL network. + well : str + The index for the well cluster. + site : str + The index for the potential site. Returns ------- @@ -816,10 +853,10 @@ def product_revenue(m, site): Parameters ---------- - m : _type_ - _description_ - site : _type_ - _description_ + m : Pyomo.ConcreteModel + A Pyomo ConcreteModel that represents the multi-period optimization problem of a GTL network. + site : str + The index for the potential site. Returns ------- @@ -841,10 +878,10 @@ def raw_material_cost(m, site): Parameters ---------- - m : _type_ - _description_ - site : _type_ - _description_ + m : Pyomo.ConcreteModel + A Pyomo ConcreteModel that represents the multi-period optimization problem of a GTL network. + site : str + The index for the potential site. Returns ------- @@ -866,12 +903,12 @@ def product_transport_cost(m, site, mkt): Parameters ---------- - m : _type_ - _description_ - site : _type_ - _description_ - mkt : _type_ - _description_ + m : Pyomo.ConcreteModel + A Pyomo ConcreteModel that represents the multi-period optimization problem of a GTL network. + site : str + The index for the potential site. + mkt : str + The index for the market. Returns ------- @@ -891,12 +928,12 @@ def pipeline_construction_cost(m, well, site): Parameters ---------- - m : _type_ - _description_ - well : _type_ - _description_ - site : _type_ - _description_ + m : Pyomo.ConcreteModel + A Pyomo ConcreteModel that represents the multi-period optimization problem of a GTL network. + well : str + The index for the well cluster. + site : str + The index for the potential site. Returns ------- @@ -913,8 +950,8 @@ def module_relocation_cost(m, from_site, to_site): Parameters ---------- - m : _type_ - _description_ + m : Pyomo.ConcreteModel + A Pyomo ConcreteModel that represents the multi-period optimization problem of a GTL network. from_site : _type_ _description_ to_site : _type_ @@ -941,10 +978,10 @@ def module_purchase_cost(m, site): Parameters ---------- - m : _type_ - _description_ - site : _type_ - _description_ + m : Pyomo.ConcreteModel + A Pyomo ConcreteModel that represents the multi-period optimization problem of a GTL network. + site : str + The index for the potential site. Returns ------- @@ -962,8 +999,8 @@ def profit(m): Parameters ---------- - m : _type_ - _description_ + m : Pyomo.ConcreteModel + A Pyomo ConcreteModel that represents the multi-period optimization problem of a GTL network. Returns ------- @@ -988,8 +1025,8 @@ def restrict_module_purchases(m): Parameters ---------- - m : _type_ - _description_ + m : Pyomo.ConcreteModel + A Pyomo ConcreteModel that represents the multi-period optimization problem of a GTL network. Returns ------- @@ -1004,8 +1041,8 @@ def restrict_module_transfers(m, from_site, to_site): Parameters ---------- - m : _type_ - _description_ + m : Pyomo.ConcreteModel + A Pyomo ConcreteModel that represents the multi-period optimization problem of a GTL network. from_site : _type_ _description_ to_site : _type_ From 1240a91963bcf2ecbf68031501610c1483f16e91 Mon Sep 17 00:00:00 2001 From: Albert Lee Date: Sat, 27 Apr 2024 19:42:13 -0400 Subject: [PATCH 78/99] Documentation on the disjunctions for exist of module type. --- gdplib/stranded_gas/model.py | 43 ++++++++++++++++++++++-------------- 1 file changed, 27 insertions(+), 16 deletions(-) diff --git a/gdplib/stranded_gas/model.py b/gdplib/stranded_gas/model.py index 6a47329..b9a7acc 100644 --- a/gdplib/stranded_gas/model.py +++ b/gdplib/stranded_gas/model.py @@ -474,39 +474,49 @@ def gasoline_tranport_cost(m, t): @m.Disjunct(m.module_types) def mtype_exists(disj, mtype): - """_summary_ + """ + Represents the scenario where a specific module type exists within the GTL network. Parameters ---------- disj : Pyomo.Disjunct - _description_ + A Pyomo Disjunct that represents the existence of a module type. mtype : str Index of the module type. + + Constraints + ------------ + learning_factor_calc : Pyomo Constraint + Captures the learning curve effect by adjusting the learning factor based on the total quantity of modules purchased. + require_module_purchases : Pyomo Constraint + 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))) + 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) + expr=sum(m.modules_purchased[mtype, :, :]) >= 1, doc="At least one module purchase") @m.Disjunct(m.module_types) def mtype_absent(disj, mtype): - """_summary_ + """ + Represents the scenario where a specific module type does not exist within the GTL network. Parameters ---------- disj : Pyomo.Disjunct - _description_ + A Pyomo Disjunct that represents the absence of a module type. mtype : str Index of the module type. """ disj.constant_learning_factor = Constraint( - expr=m.learning_factor[mtype] == 1) + expr=m.learning_factor[mtype] == 1, doc="Constant learning factor") @m.Disjunction(m.module_types) def mtype_existence(m, mtype): - """_summary_ + """ + A disjunction that determines whether a module type exists or is absent within the GTL network. Parameters ---------- @@ -517,14 +527,15 @@ def mtype_existence(m, mtype): Returns ------- - _type_ - _description_ + list of Pyomo.Disjunct + A list containing two disjuncts, one for the scenario where the module type exists and one for where it is absent. """ return [m.mtype_exists[mtype], m.mtype_absent[mtype]] @m.Expression(m.module_types, m.time, doc="Module unit cost [MM$/module]") def module_unit_cost(m, mtype, t): - """_summary_ + """ + Computes the unit cost of a module type at a specific time period, considering the base cost, the learning factor due to economies of numbers, and the time-based discount factor. Parameters ---------- @@ -537,8 +548,8 @@ def module_unit_cost(m, mtype, t): Returns ------- - _type_ - _description_ + 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] @@ -577,7 +588,7 @@ def consumption_capacity(m, site, mtype, t): Returns ------- - _type_ + Pyomo.Constraint _description_ """ return m.gas_consumption[site, mtype, t] <= ( @@ -738,7 +749,7 @@ def site_active(disj, site): Parameters ---------- disj : Pyomo.Disjunct - _description_ + A Pyomo Disjunct that represents the active state of a potential site. site : str The index for the potential site. """ @@ -751,7 +762,7 @@ def site_inactive(disj, site): Parameters ---------- disj : Pyomo.Disjunct - _description_ + A Pyomo Disjunct that represents the inactive state of a potential site. site : str The index for the potential site. """ From 702ece7fa45c7456081fe5dc92663c0300df7faf Mon Sep 17 00:00:00 2001 From: Albert Lee Date: Sat, 27 Apr 2024 22:11:49 -0400 Subject: [PATCH 79/99] Update documentation on disjunctions for exist of module type --- gdplib/stranded_gas/model.py | 160 ++++++++++++++++++++--------------- 1 file changed, 91 insertions(+), 69 deletions(-) diff --git a/gdplib/stranded_gas/model.py b/gdplib/stranded_gas/model.py index b9a7acc..b03a181 100644 --- a/gdplib/stranded_gas/model.py +++ b/gdplib/stranded_gas/model.py @@ -573,7 +573,8 @@ def module_unit_cost(m, mtype, t): @m.Constraint(m.potential_sites, m.module_types, m.time) def consumption_capacity(m, site, mtype, t): - """_summary_ + """ + Ensures that the natural gas consumption at any site for any module type does not exceed the production capacity of the modules present. Parameters ---------- @@ -589,14 +590,15 @@ def consumption_capacity(m, site, mtype, t): Returns ------- Pyomo.Constraint - _description_ + 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.Constraint(m.potential_sites, m.time) def production_limit(m, site, t): - """_summary_ + """ + Limits the production of gasoline at each site to the maximum possible based on the gas consumed and the conversion efficiency of the modules. Parameters ---------- @@ -609,8 +611,8 @@ def production_limit(m, site, t): Returns ------- - _type_ - _description_ + Pyomo.Constraint + A constraint that ensures the production of gasoline at each site does not exceed the sum of the product of gas consumption and conversion rates for all module types at that site. """ return m.production[site, t] <= sum( m.gas_consumption[site, mtype, t] * m.module_conversion[mtype] @@ -618,7 +620,8 @@ def production_limit(m, site, t): @m.Expression(m.potential_sites, m.time) def capacity(m, site, t): - """_summary_ + """ + Calculates the total potential gasoline production capacity at each site for a given time period, based on the number of active modules, their gas consumption, and conversion efficiency. Parameters ---------- @@ -631,8 +634,8 @@ def capacity(m, site, t): Returns ------- - _type_ - _description_ + Pyomo.Expression + 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] @@ -640,7 +643,8 @@ def capacity(m, site, t): @m.Constraint(m.potential_sites, m.time) def gas_supply_meets_consumption(m, site, t): - """_summary_ + """ + Ensures that the total gas consumed at a site is exactly matched by the gas supplied to it, reflecting a balance between supply and demand at any given time period. Parameters ---------- @@ -653,14 +657,15 @@ def gas_supply_meets_consumption(m, site, t): Returns ------- - _type_ - _description_ + Pyomo.Constraint + A constraint that balances the gas supply with the gas consumption at each site, ensuring that the total gas flow to the site equals the total consumption. """ return sum(m.gas_consumption[site, :, t]) == sum(m.gas_flows[:, site, t]) @m.Constraint(m.well_clusters, m.time) def gas_supply_limit(m, well, t): - """_summary_ + """ + Ensures that the total gas supplied from a well cluster does not exceed the available gas supply for that cluster during any given time period. Parameters ---------- @@ -673,15 +678,16 @@ def gas_supply_limit(m, well, t): Returns ------- - _type_ - _description_ + 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] @m.Constraint(m.potential_sites, m.time) def gasoline_production_requirement(m, site, t): - """_summary_ + """ + Ensures that the total amount of gasoline shipped from a site matches the production at that site for each time period. Parameters ---------- @@ -694,15 +700,16 @@ def gasoline_production_requirement(m, site, t): Returns ------- - _type_ - _description_ + 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] @m.Constraint(m.potential_sites, m.module_types, m.time) def module_balance(m, site, mtype, t): - """_summary_ + """ + Balances the number of modules at a site across time periods by accounting for modules added, transferred, and previously existing. This ensures a consistent and accurate count of modules that reflects all transactions and changes over time. Parameters ---------- @@ -717,8 +724,8 @@ def module_balance(m, site, mtype, t): Returns ------- - _type_ - _description_ + Pyomo.Constraint + 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[ @@ -744,7 +751,8 @@ def module_balance(m, site, mtype, t): @m.Disjunct(m.potential_sites) def site_active(disj, site): - """_summary_ + """ + Represents the active state of a potential site within the GTL network. Parameters ---------- @@ -757,7 +765,8 @@ def site_active(disj, site): @m.Disjunct(m.potential_sites) def site_inactive(disj, site): - """_summary_ + """ + Represents the inactive state of a potential site within the GTL network. Parameters ---------- @@ -782,15 +791,16 @@ def site_inactive(disj, site): 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) + 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) + 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): - """_summary_ + """ + A disjunction that determines whether a potential site is active or inactive within the GTL network. Parameters ---------- @@ -801,14 +811,15 @@ def site_active_or_not(m, site): Returns ------- - _type_ - _description_ + list of Pyomo.Disjunct + A list containing two disjuncts, one for the scenario where the site is active and one for where it is inactive. """ return [m.site_active[site], m.site_inactive[site]] @m.Disjunct(m.well_clusters, m.potential_sites) def pipeline_exists(disj, well, site): - """_summary_ + """ + Represents the scenario where a pipeline exists between a well cluster and a potential site. Parameters ---------- @@ -823,7 +834,8 @@ def pipeline_exists(disj, well, site): @m.Disjunct(m.well_clusters, m.potential_sites) def pipeline_absent(disj, well, site): - """_summary_ + """ + Represents the scenario where a pipeline does not exist between a well cluster and a potential site. Parameters ---------- @@ -835,11 +847,12 @@ 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) + 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): - """_summary_ + """ + A disjunction that determines whether a pipeline exists or is absent between a well cluster and a potential site. Parameters ---------- @@ -852,15 +865,16 @@ def pipeline_existence(m, well, site): Returns ------- - _type_ - _description_ + list of Pyomo.Disjunct + A list containing two disjuncts, one for the scenario where a pipeline exists and one for where it is absent. """ return [m.pipeline_exists[well, site], m.pipeline_absent[well, site]] # Objective Function Construnction @m.Expression(m.potential_sites, doc="MM$") def product_revenue(m, site): - """_summary_ + """ + Calculates the total revenue generated from the sale of gasoline produced at each site. This expression multiplies the volume of gasoline sold by the price per gallon, adjusted to millions of dollars for the entire production period. Parameters ---------- @@ -871,8 +885,8 @@ def product_revenue(m, site): Returns ------- - _type_ - _description_ + Pyomo.Expression + An expression representing the total revenue in million dollars from selling the gasoline produced at the site. """ return sum( m.product_flows[site, mkt, t] # kBD @@ -885,7 +899,8 @@ def product_revenue(m, site): @m.Expression(m.potential_sites, doc="MM$") def raw_material_cost(m, site): - """_summary_ + """ + Calculates the total cost of natural gas consumed as a raw material at each site, converted to millions of dollars. Parameters ---------- @@ -896,8 +911,8 @@ def raw_material_cost(m, site): Returns ------- - _type_ - _description_ + Pyomo.Expression + 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 @@ -910,7 +925,8 @@ def raw_material_cost(m, site): m.potential_sites, m.markets, doc="Aggregate cost to transport gasoline from a site to market [MM$]") def product_transport_cost(m, site, mkt): - """_summary_ + """ + Computes the cost of transporting gasoline from each production site to different markets, expressed in million dollars. Parameters ---------- @@ -923,8 +939,8 @@ def product_transport_cost(m, site, mkt): Returns ------- - _type_ - _description_ + Pyomo.Expression + 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 @@ -935,7 +951,8 @@ def product_transport_cost(m, site, mkt): @m.Expression(m.well_clusters, m.potential_sites, doc="MM$") def pipeline_construction_cost(m, well, site): - """_summary_ + """ + Calculates the cost of constructing pipelines from well clusters to potential sites, with costs dependent on the existence of a pipeline, distance, and unit cost per mile. Parameters ---------- @@ -948,8 +965,8 @@ def pipeline_construction_cost(m, well, site): Returns ------- - _type_ - _description_ + 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) @@ -957,21 +974,22 @@ def pipeline_construction_cost(m, well, site): # Module transport cost @m.Expression(m.site_pairs, doc="MM$") def module_relocation_cost(m, from_site, to_site): - """_summary_ + """ + Calculates the cost of relocating modules from one site to another, considering the distance, transport cost per mile, and unit cost per module. This cost includes the transportation costs based on distance and per-unit transport costs, accounting for all modules transferred between specified sites over the entire project duration. Parameters ---------- m : Pyomo.ConcreteModel A Pyomo ConcreteModel that represents the multi-period optimization problem of a GTL network. - from_site : _type_ - _description_ - to_site : _type_ - _description_ + from_site : str + Index for the originating site of the module transfer. + to_site : str + Index for the destination site of the module transfer. Returns ------- - _type_ - _description_ + Pyomo.Expression + An expression calculating the total relocation cost for modules moved between the two sites, factoring in the distance and both per-mile and per-unit costs, scaled to million dollars. """ return sum( m.modules_transferred[mtype, from_site, to_site, t] @@ -985,7 +1003,8 @@ def module_relocation_cost(m, from_site, to_site): @m.Expression(m.potential_sites, doc="MM$") def module_purchase_cost(m, site): - """_summary_ + """ + Computes the total cost of purchasing new modules for a specific site, considering the unit costs of modules, which may vary over time due to discounts and other factors. This expression aggregates the costs for all modules purchased across the project's timeframe. Parameters ---------- @@ -996,8 +1015,8 @@ def module_purchase_cost(m, site): Returns ------- - _type_ - _description_ + Pyomo.Expression + An expression representing the total cost of module purchases at the specified site, converted to million dollars. """ return sum( m.module_unit_cost[mtype, t] * m.modules_purchased[mtype, site, t] @@ -1006,7 +1025,8 @@ def module_purchase_cost(m, site): @m.Expression(doc="MM$") def profit(m): - """_summary_ + """ + Calculates the overall profit for the GTL network by subtracting all relevant costs from the total revenue. This is used as the objective function to be maximized (or minimize the negative profit). Parameters ---------- @@ -1015,8 +1035,8 @@ def profit(m): Returns ------- - _type_ - _description_ + Pyomo.Expression + The net profit expression, computed as the difference between total revenue and all accumulated costs across the network. """ return ( summation(m.product_revenue) @@ -1027,12 +1047,13 @@ def profit(m): - summation(m.module_purchase_cost) ) - m.neg_profit = Objective(expr=-m.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.") def restrict_module_purchases(m): - """_summary_ + """ + Enforces a limit on the total number of module purchases across all sites and module types throughout the entire project span. This constraint is crucial for controlling capital expenditure and ensuring that the module acquisition does not exceed a specified threshold, helping to maintain budget constraints. Parameters ---------- @@ -1041,28 +1062,29 @@ def restrict_module_purchases(m): Returns ------- - _type_ - _description_ + Pyomo.Constraint + A global constraint that limits the aggregate number of modules purchased across all sites to 5, ensuring that the total investment in module purchases remains within predefined limits. """ return sum(m.modules_purchased[...]) <= 5 @m.Constraint(m.site_pairs, doc="Limit transfers between any two sites") def restrict_module_transfers(m, from_site, to_site): - """_summary_ + """ + Imposes a limit on the number of modules that can be transferred between any two sites during the entire project timeline. This constraint helps manage logistics and ensures that module reallocation does not become overly frequent or excessive, which could lead to operational inefficiencies and increased costs. Parameters ---------- m : Pyomo.ConcreteModel A Pyomo ConcreteModel that represents the multi-period optimization problem of a GTL network. - from_site : _type_ - _description_ - to_site : _type_ - _description_ + from_site : str + Index for the origin site from which modules are being transferred. + to_site : str + Index for the destination site to which modules are being transferred. Returns ------- - _type_ - _description_ + Pyomo.Constraint + A constraint limiting the total number of modules transferred from one site to another to 5, providing a control mechanism on the frequency and volume of inter-site module movements. """ return sum(m.modules_transferred[:, from_site, to_site, :]) <= 5 From df40046be4387c46e0ab5009a114634106e546e4 Mon Sep 17 00:00:00 2001 From: Albert Lee Date: Mon, 6 May 2024 14:58:15 -0400 Subject: [PATCH 80/99] Removed the unnecesary references. --- gdplib/pyomo_examples/med_term_purchasing.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/gdplib/pyomo_examples/med_term_purchasing.py b/gdplib/pyomo_examples/med_term_purchasing.py index 47af318..9377735 100644 --- a/gdplib/pyomo_examples/med_term_purchasing.py +++ b/gdplib/pyomo_examples/med_term_purchasing.py @@ -43,9 +43,8 @@ def build_model(): References ---------- - [1] Vecchietti, A., & Grossmann, I. (2004). Computational experience with logmip solving linear and nonlinear disjunctive programming problems. Proc. of FOCAPD, 587–590. - [2] Vecchietti, A., Lee, S., & Grossmann, I. E. (2003). Modeling of discrete/continuous optimization problems: characterization and formulation of disjunctions and their relaxations. Computers & Chemical Engineering, 27(3), 433–448. DOI: 10.1016/S0098-1354(02)00220-X - [3] Park, M., Park, S., Mele, F. D., & Grossmann, I. E. (2006). Modeling of purchase and sales contracts in supply chain optimization. Industrial and Engineering Chemistry Research, 45(14), 5013–5026. DOI: 10.1021/ie0513144 + [1] Vecchietti, A., & Grossmann, I. (2004). Computational experience with logmip solving linear and nonlinear disjunctive programming problems. Proc. of FOCAPD, 587-590. + [2] Park, M., Park, S., Mele, F. D., & Grossmann, I. E. (2006). Modeling of purchase and sales contracts in supply chain optimization. Industrial and Engineering Chemistry Research, 45(14), 5013-5026. DOI: 10.1021/ie0513144 """ model = AbstractModel() From bffffdbd57ae11394b0a8f689ad8f24ec1e5cb5a Mon Sep 17 00:00:00 2001 From: ZedongPeng Date: Mon, 6 May 2024 17:01:01 -0400 Subject: [PATCH 81/99] split models in pyomo_examples --- gdplib/batch_processing/__init__.py | 3 +++ .../batch_processing.dat | 0 .../batch_processing.py | 0 gdplib/disease_model/__init__.py | 3 +++ .../disease_model.py | 0 gdplib/jobshop/__init__.py | 3 +++ gdplib/{pyomo_examples => jobshop}/jobshop-small.dat | 0 gdplib/{pyomo_examples => jobshop}/jobshop.py | 0 gdplib/med_term_purchasing/__init__.py | 3 +++ .../med_term_purchasing.dat | 0 .../med_term_purchasing.py | 0 gdplib/pyomo_examples/README.md | 4 ---- gdplib/pyomo_examples/__init__.py | 11 ----------- 13 files changed, 12 insertions(+), 15 deletions(-) create mode 100644 gdplib/batch_processing/__init__.py rename gdplib/{pyomo_examples => batch_processing}/batch_processing.dat (100%) rename gdplib/{pyomo_examples => batch_processing}/batch_processing.py (100%) create mode 100644 gdplib/disease_model/__init__.py rename gdplib/{pyomo_examples => disease_model}/disease_model.py (100%) create mode 100644 gdplib/jobshop/__init__.py rename gdplib/{pyomo_examples => jobshop}/jobshop-small.dat (100%) rename gdplib/{pyomo_examples => jobshop}/jobshop.py (100%) create mode 100644 gdplib/med_term_purchasing/__init__.py rename gdplib/{pyomo_examples => med_term_purchasing}/med_term_purchasing.dat (100%) rename gdplib/{pyomo_examples => med_term_purchasing}/med_term_purchasing.py (100%) delete mode 100644 gdplib/pyomo_examples/README.md delete mode 100644 gdplib/pyomo_examples/__init__.py diff --git a/gdplib/batch_processing/__init__.py b/gdplib/batch_processing/__init__.py new file mode 100644 index 0000000..58f319a --- /dev/null +++ b/gdplib/batch_processing/__init__.py @@ -0,0 +1,3 @@ +from .batch_processing import build_model + +__all__ = ['build_model'] diff --git a/gdplib/pyomo_examples/batch_processing.dat b/gdplib/batch_processing/batch_processing.dat similarity index 100% rename from gdplib/pyomo_examples/batch_processing.dat rename to gdplib/batch_processing/batch_processing.dat diff --git a/gdplib/pyomo_examples/batch_processing.py b/gdplib/batch_processing/batch_processing.py similarity index 100% rename from gdplib/pyomo_examples/batch_processing.py rename to gdplib/batch_processing/batch_processing.py diff --git a/gdplib/disease_model/__init__.py b/gdplib/disease_model/__init__.py new file mode 100644 index 0000000..7058d12 --- /dev/null +++ b/gdplib/disease_model/__init__.py @@ -0,0 +1,3 @@ +from .disease_model import build_model + +__all__ = ['build_model'] diff --git a/gdplib/pyomo_examples/disease_model.py b/gdplib/disease_model/disease_model.py similarity index 100% rename from gdplib/pyomo_examples/disease_model.py rename to gdplib/disease_model/disease_model.py diff --git a/gdplib/jobshop/__init__.py b/gdplib/jobshop/__init__.py new file mode 100644 index 0000000..6361ccd --- /dev/null +++ b/gdplib/jobshop/__init__.py @@ -0,0 +1,3 @@ +from .jobshop import build_model + +__all__ = ['build_model'] diff --git a/gdplib/pyomo_examples/jobshop-small.dat b/gdplib/jobshop/jobshop-small.dat similarity index 100% rename from gdplib/pyomo_examples/jobshop-small.dat rename to gdplib/jobshop/jobshop-small.dat diff --git a/gdplib/pyomo_examples/jobshop.py b/gdplib/jobshop/jobshop.py similarity index 100% rename from gdplib/pyomo_examples/jobshop.py rename to gdplib/jobshop/jobshop.py diff --git a/gdplib/med_term_purchasing/__init__.py b/gdplib/med_term_purchasing/__init__.py new file mode 100644 index 0000000..fb2e2da --- /dev/null +++ b/gdplib/med_term_purchasing/__init__.py @@ -0,0 +1,3 @@ +from .med_term_purchasing import build_model + +__all__ = ['build_model'] diff --git a/gdplib/pyomo_examples/med_term_purchasing.dat b/gdplib/med_term_purchasing/med_term_purchasing.dat similarity index 100% rename from gdplib/pyomo_examples/med_term_purchasing.dat rename to gdplib/med_term_purchasing/med_term_purchasing.dat diff --git a/gdplib/pyomo_examples/med_term_purchasing.py b/gdplib/med_term_purchasing/med_term_purchasing.py similarity index 100% rename from gdplib/pyomo_examples/med_term_purchasing.py rename to gdplib/med_term_purchasing/med_term_purchasing.py diff --git a/gdplib/pyomo_examples/README.md b/gdplib/pyomo_examples/README.md deleted file mode 100644 index a00148f..0000000 --- a/gdplib/pyomo_examples/README.md +++ /dev/null @@ -1,4 +0,0 @@ -# Models taken from Pyomo.GDP examples directory - -These models are among the oldest in the library. -They were primarily developed by Emma Savannah Johnson (esjohn@sandia.gov), and are simply reproduced here in importable form. diff --git a/gdplib/pyomo_examples/__init__.py b/gdplib/pyomo_examples/__init__.py deleted file mode 100644 index be668e9..0000000 --- a/gdplib/pyomo_examples/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -from .batch_processing import build_model as build_batch_processing_model -from .disease_model import build_model as build_disease_model -from .jobshop import build_small_concrete as build_jobshop_model -from .med_term_purchasing import build_concrete as build_med_term_purchasing_model - -__all__ = [ - 'build_batch_processing_model', - 'build_disease_model', - 'build_jobshop_model', - 'build_med_term_purchasing_model' -] From 353d69c7e9fb16898b134bbb549d67ca70a81ebb Mon Sep 17 00:00:00 2001 From: ZedongPeng Date: Mon, 6 May 2024 17:22:42 -0400 Subject: [PATCH 82/99] unify the function to build_model --- gdplib/jobshop/jobshop.py | 21 ++----------------- .../med_term_purchasing.py | 18 ++-------------- 2 files changed, 4 insertions(+), 35 deletions(-) diff --git a/gdplib/jobshop/jobshop.py b/gdplib/jobshop/jobshop.py index 25ef303..790488d 100644 --- a/gdplib/jobshop/jobshop.py +++ b/gdplib/jobshop/jobshop.py @@ -228,29 +228,12 @@ def _disj(model, I, K, J): model.makespan = Objective( expr=model.ms, doc='Objective Function: Minimize the makespan' ) + model.create_instance(join(this_file_dir(), 'jobshop-small.dat')) return model -def build_small_concrete(): - """ - Build a small jobshop scheduling model for testing purposes. - The AbstractModel is instantiated with the data from 'jobshop-small.dat' turning it into a ConcreteModel. - - Parameters - ---------- - None - However the data file jobshop-small.dat must be in the same directory as this file. - - Returns - ------- - ConcreteModel : Pyomo.ConcreteModel - A concrete instance of the jobshop scheduling model populated with data from 'jobshop-small.dat', ready for optimization. - """ - return build_model().create_instance(join(this_file_dir(), 'jobshop-small.dat')) - - if __name__ == "__main__": - m = build_small_concrete() + m = build_model() TransformationFactory('gdp.bigm').apply_to(m) SolverFactory('gams').solve( m, solver='baron', tee=True, add_options=['option optcr=1e-6;'] diff --git a/gdplib/med_term_purchasing/med_term_purchasing.py b/gdplib/med_term_purchasing/med_term_purchasing.py index 9377735..02a0961 100644 --- a/gdplib/med_term_purchasing/med_term_purchasing.py +++ b/gdplib/med_term_purchasing/med_term_purchasing.py @@ -1960,26 +1960,12 @@ def FD_contract(model, j, t): rule=FD_contract, doc='Fixed duration contract scenarios', ) - + model.create_instance(join(this_file_dir(), 'med_term_purchasing.dat')) return model -def build_concrete(): - """ - Build a concrete model applying the data of the medium-term purchasing contracts problem on build_model(). - - Returns - ------- - Pyomo.ConcreteModel - A concrete model for the medium-term purchasing contracts problem. - """ - return build_model().create_instance( - join(this_file_dir(), 'med_term_purchasing.dat') - ) - - if __name__ == "__main__": - m = build_concrete() + m = build_model() TransformationFactory('gdp.bigm').apply_to(m) SolverFactory('gams').solve( m, solver='baron', tee=True, add_options=['option optcr=1e-6;'] From e715e1e270bf07513c594b38fdce3b581b4539cd Mon Sep 17 00:00:00 2001 From: ZedongPeng Date: Mon, 6 May 2024 17:25:24 -0400 Subject: [PATCH 83/99] update __init__.py --- gdplib/__init__.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/gdplib/__init__.py b/gdplib/__init__.py index 1812f52..a3cb501 100644 --- a/gdplib/__init__.py +++ b/gdplib/__init__.py @@ -2,9 +2,13 @@ import gdplib.modprodnet import gdplib.biofuel import gdplib.logical # Requires logical expression system -import gdplib.pyomo_examples import gdplib.stranded_gas # Requires logical expression system import gdplib.gdp_col import gdplib.hda import gdplib.kaibel import gdplib.methanol +import gdplib.batch_processing +import gdplib.jobshop +import gdplib.disease_model +import gdplib.med_term_purchasing +import gdplib.syngas From d3fcdf7d0da77ca9e94b1d5456cc368a23f369de Mon Sep 17 00:00:00 2001 From: ZedongPeng Date: Mon, 6 May 2024 20:03:49 -0400 Subject: [PATCH 84/99] fix abstract model generation bug --- gdplib/jobshop/jobshop.py | 2 +- gdplib/med_term_purchasing/med_term_purchasing.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gdplib/jobshop/jobshop.py b/gdplib/jobshop/jobshop.py index 790488d..1e58752 100644 --- a/gdplib/jobshop/jobshop.py +++ b/gdplib/jobshop/jobshop.py @@ -228,7 +228,7 @@ def _disj(model, I, K, J): model.makespan = Objective( expr=model.ms, doc='Objective Function: Minimize the makespan' ) - model.create_instance(join(this_file_dir(), 'jobshop-small.dat')) + model = model.create_instance(join(this_file_dir(), 'jobshop-small.dat')) return model diff --git a/gdplib/med_term_purchasing/med_term_purchasing.py b/gdplib/med_term_purchasing/med_term_purchasing.py index 02a0961..6d12e2d 100644 --- a/gdplib/med_term_purchasing/med_term_purchasing.py +++ b/gdplib/med_term_purchasing/med_term_purchasing.py @@ -1960,7 +1960,7 @@ def FD_contract(model, j, t): rule=FD_contract, doc='Fixed duration contract scenarios', ) - model.create_instance(join(this_file_dir(), 'med_term_purchasing.dat')) + model = model.create_instance(join(this_file_dir(), 'med_term_purchasing.dat')) return model From 1c2c28b5a747f3dfaef99c83e623d92eb3745cd8 Mon Sep 17 00:00:00 2001 From: ZedongPeng Date: Mon, 6 May 2024 21:09:40 -0400 Subject: [PATCH 85/99] first working version of benchmark.py --- benchmark.py | 116 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 benchmark.py diff --git a/benchmark.py b/benchmark.py new file mode 100644 index 0000000..5820997 --- /dev/null +++ b/benchmark.py @@ -0,0 +1,116 @@ +from pyomo.environ import * +import os +import json +import time +import sys +from datetime import datetime +from gdplib.jobshop.jobshop import build_model +from importlib import import_module +from pyomo.util.model_size import build_model_size_report +import pandas as pd + +subsolver = "scip" + + +def benchmark(model, strategy, timelimit, result_dir): + """Benchmark the model using the given strategy and subsolver. + + Parameters + ---------- + model : PyomoModel + the model to be solved + strategy : string + the strategy used to solve the model + timelimit : int + the time limit for the solver + result_dir : string + the directory to store the benchmark results + """ + model = model.clone() + stdout = sys.stdout + if strategy in ["gdp.bigm", "gdp.hull"]: + transformation_start_time = time.time() + TransformationFactory(strategy).apply_to(model) + transformation_end_time = time.time() + with open( + result_dir + "/" + strategy + "_" + subsolver + ".log", "w" + ) as sys.stdout: + results = SolverFactory("scip").solve(model, tee=True, timelimit=timelimit) + results.solver.transformation_time = ( + transformation_end_time - transformation_start_time + ) + print(results) + elif strategy in [ + "gdpopt.enumerate", + "gdpopt.loa", + "gdpopt.gloa", + "gdpopt.lbb", + "gdpopt.ric", + ]: + with open( + result_dir + "/" + strategy + "_" + subsolver + ".log", "w" + ) as sys.stdout: + results = SolverFactory(strategy).solve( + model, + tee=True, + nlp_solver="scip", + mip_solver="scip", + minlp_solver="scip", + local_minlp_solver="scip", + time_limit=timelimit, + ) + print(results) + + sys.stdout = stdout + with open(result_dir + "/" + strategy + "_" + subsolver + ".json", "w") as f: + json.dump(results.json_repn(), f) + + +if __name__ == "__main__": + instance_list = [ + # "batch_processing", + # "biofuel", + # "disease_model", + # "gdp_col", + # "hda", + "jobshop", + # "kaibel", + # "logical", + # "med_term_purchasing", + # "methanol", + # "mod_hens", + # "modprodnet", + # "stranded_gas", + # "syngas", + ] + strategy_list = [ + "gdp.bigm", + "gdp.hull", + "gdpopt.enumerate", + "gdpopt.loa", + "gdpopt.gloa", + "gdpopt.ric", + ] + current_time = datetime.now().strftime("%Y-%m-%d_%H-%M-%S") + timelimit = 600 + + for instance in instance_list: + print("Benchmarking instance: " + instance) + result_dir = "gdplib/" + instance + "/benchmark_result/" + current_time + os.makedirs(result_dir, exist_ok=True) + + model = import_module("gdplib." + instance).build_model() + report = build_model_size_report(model) + report_df = pd.DataFrame(report.overall, index=[0]).T + report_df.index.name = "Component" + report_df.columns = ["Number"] + # Generate the model size report (Markdown) + report_df.to_markdown("gdplib/" + instance + "/" + "model_size_report.md") + + # Generate the model size report (JSON) + # TODO: check if we need the json file. + with open("gdplib/" + instance + "/" + "model_size_report.json", "w") as f: + json.dump(report, f) + + for strategy in strategy_list: + benchmark(model, strategy, timelimit, result_dir) From 2e1e596ea1290c9024f38f98314dd4e13d35047f Mon Sep 17 00:00:00 2001 From: ZedongPeng Date: Mon, 6 May 2024 21:10:08 -0400 Subject: [PATCH 86/99] result example of jobshop --- .../2024-05-06_21-01-21/gdp.bigm_scip.json | 1 + .../2024-05-06_21-01-21/gdp.hull_scip.json | 1 + .../2024-05-06_21-01-21/gdpopt.enumerate_scip.json | 1 + .../2024-05-06_21-01-21/gdpopt.gloa_scip.json | 1 + .../2024-05-06_21-01-21/gdpopt.loa_scip.json | 1 + .../2024-05-06_21-01-21/gdpopt.ric_scip.json | 1 + gdplib/jobshop/model_size_report.json | 1 + gdplib/jobshop/model_size_report.md | 10 ++++++++++ 8 files changed, 17 insertions(+) create mode 100644 gdplib/jobshop/benchmark_result/2024-05-06_21-01-21/gdp.bigm_scip.json create mode 100644 gdplib/jobshop/benchmark_result/2024-05-06_21-01-21/gdp.hull_scip.json create mode 100644 gdplib/jobshop/benchmark_result/2024-05-06_21-01-21/gdpopt.enumerate_scip.json create mode 100644 gdplib/jobshop/benchmark_result/2024-05-06_21-01-21/gdpopt.gloa_scip.json create mode 100644 gdplib/jobshop/benchmark_result/2024-05-06_21-01-21/gdpopt.loa_scip.json create mode 100644 gdplib/jobshop/benchmark_result/2024-05-06_21-01-21/gdpopt.ric_scip.json create mode 100644 gdplib/jobshop/model_size_report.json create mode 100644 gdplib/jobshop/model_size_report.md diff --git a/gdplib/jobshop/benchmark_result/2024-05-06_21-01-21/gdp.bigm_scip.json b/gdplib/jobshop/benchmark_result/2024-05-06_21-01-21/gdp.bigm_scip.json new file mode 100644 index 0000000..04f3905 --- /dev/null +++ b/gdplib/jobshop/benchmark_result/2024-05-06_21-01-21/gdp.bigm_scip.json @@ -0,0 +1 @@ +{"Problem": [{"Lower bound": -Infinity, "Upper bound": 11.0, "Number of objectives": 1, "Number of constraints": 0, "Number of variables": 10, "Sense": "unknown"}], "Solver": [{"Status": "ok", "Message": "optimal solution found", "Termination condition": "optimal", "Id": 0, "Error rc": 0, "Time": 0.04, "Gap": 0.0, "Primal bound": 11.0, "Dual bound": 11.0, "Transformation time": 0.003754854202270508}], "Solution": [{"number of solutions": 0, "number of solutions displayed": 0}]} \ No newline at end of file diff --git a/gdplib/jobshop/benchmark_result/2024-05-06_21-01-21/gdp.hull_scip.json b/gdplib/jobshop/benchmark_result/2024-05-06_21-01-21/gdp.hull_scip.json new file mode 100644 index 0000000..c57115b --- /dev/null +++ b/gdplib/jobshop/benchmark_result/2024-05-06_21-01-21/gdp.hull_scip.json @@ -0,0 +1 @@ +{"Problem": [{"Lower bound": -Infinity, "Upper bound": 11.0, "Number of objectives": 1, "Number of constraints": 0, "Number of variables": 22, "Sense": "unknown"}], "Solver": [{"Status": "ok", "Message": "optimal solution found", "Termination condition": "optimal", "Id": 0, "Error rc": 0, "Time": 0.01, "Gap": 0.0, "Primal bound": 11.0, "Dual bound": 11.0, "Transformation time": 0.008002042770385742}], "Solution": [{"number of solutions": 0, "number of solutions displayed": 0}]} \ No newline at end of file diff --git a/gdplib/jobshop/benchmark_result/2024-05-06_21-01-21/gdpopt.enumerate_scip.json b/gdplib/jobshop/benchmark_result/2024-05-06_21-01-21/gdpopt.enumerate_scip.json new file mode 100644 index 0000000..b09a6f2 --- /dev/null +++ b/gdplib/jobshop/benchmark_result/2024-05-06_21-01-21/gdpopt.enumerate_scip.json @@ -0,0 +1 @@ +{"Problem": [{"Name": "unknown", "Lower bound": 11.0, "Upper bound": 11.0, "Number of objectives": 1, "Number of constraints": 9, "Number of variables": 10, "Number of binary variables": 6, "Number of integer variables": 0, "Number of continuous variables": 4, "Number of nonzeros": null, "Sense": 1, "Number of disjunctions": 3}], "Solver": [{"Name": "GDPopt (22, 5, 13) - enumerate", "Status": "ok", "User time": 0.3502802930015605, "Wallclock time": 0.3502802930015605, "Termination condition": "optimal", "Iterations": 8, "Timing": {"main_timer_start_time": 123407.445576859, "nlp": 0.34058500100218225, "total": 0.3502802930015605}}]} \ No newline at end of file diff --git a/gdplib/jobshop/benchmark_result/2024-05-06_21-01-21/gdpopt.gloa_scip.json b/gdplib/jobshop/benchmark_result/2024-05-06_21-01-21/gdpopt.gloa_scip.json new file mode 100644 index 0000000..7464bb2 --- /dev/null +++ b/gdplib/jobshop/benchmark_result/2024-05-06_21-01-21/gdpopt.gloa_scip.json @@ -0,0 +1 @@ +{"Problem": [{"Name": "unknown", "Lower bound": 11.0, "Upper bound": 11.0, "Number of objectives": 1, "Number of constraints": 9, "Number of variables": 10, "Number of binary variables": 6, "Number of integer variables": 0, "Number of continuous variables": 4, "Number of nonzeros": null, "Sense": 1, "Number of disjunctions": 3}], "Solver": [{"Name": "GDPopt (22, 5, 13) - GLOA", "Status": "ok", "User time": 0.1213786309963325, "Wallclock time": 0.1213786309963325, "Termination condition": "optimal", "Iterations": 1, "Timing": {"main_timer_start_time": 123407.944400965, "mip": 0.05432544200448319, "nlp": 0.05131585600611288, "integer cut generation": 0.000397921001422219, "total": 0.1213786309963325}}]} \ No newline at end of file diff --git a/gdplib/jobshop/benchmark_result/2024-05-06_21-01-21/gdpopt.loa_scip.json b/gdplib/jobshop/benchmark_result/2024-05-06_21-01-21/gdpopt.loa_scip.json new file mode 100644 index 0000000..06a1841 --- /dev/null +++ b/gdplib/jobshop/benchmark_result/2024-05-06_21-01-21/gdpopt.loa_scip.json @@ -0,0 +1 @@ +{"Problem": [{"Name": "unknown", "Lower bound": 11.0, "Upper bound": 11.0, "Number of objectives": 1, "Number of constraints": 9, "Number of variables": 10, "Number of binary variables": 6, "Number of integer variables": 0, "Number of continuous variables": 4, "Number of nonzeros": null, "Sense": 1, "Number of disjunctions": 3}], "Solver": [{"Name": "GDPopt (22, 5, 13) - LOA", "Status": "ok", "User time": 0.1308980710018659, "Wallclock time": 0.1308980710018659, "Termination condition": "optimal", "Iterations": 1, "Timing": {"main_timer_start_time": 123407.805129371, "mip": 0.06084998599544633, "nlp": 0.054532527006813325, "integer cut generation": 0.0005358900089049712, "total": 0.1308980710018659}}]} \ No newline at end of file diff --git a/gdplib/jobshop/benchmark_result/2024-05-06_21-01-21/gdpopt.ric_scip.json b/gdplib/jobshop/benchmark_result/2024-05-06_21-01-21/gdpopt.ric_scip.json new file mode 100644 index 0000000..fa7bad2 --- /dev/null +++ b/gdplib/jobshop/benchmark_result/2024-05-06_21-01-21/gdpopt.ric_scip.json @@ -0,0 +1 @@ +{"Problem": [{"Name": "unknown", "Lower bound": 11.0, "Upper bound": 11.0, "Number of objectives": 1, "Number of constraints": 9, "Number of variables": 10, "Number of binary variables": 6, "Number of integer variables": 0, "Number of continuous variables": 4, "Number of nonzeros": null, "Sense": 1, "Number of disjunctions": 3}], "Solver": [{"Name": "GDPopt (22, 5, 13) - RIC", "Status": "ok", "User time": 0.13380873500136659, "Wallclock time": 0.13380873500136659, "Termination condition": "optimal", "Iterations": 1, "Timing": {"main_timer_start_time": 123408.074596677, "mip": 0.05659815100079868, "nlp": 0.06265952499234118, "integer cut generation": 0.0004167870065430179, "total": 0.13380873500136659}}]} \ No newline at end of file diff --git a/gdplib/jobshop/model_size_report.json b/gdplib/jobshop/model_size_report.json new file mode 100644 index 0000000..138d2a1 --- /dev/null +++ b/gdplib/jobshop/model_size_report.json @@ -0,0 +1 @@ +{"activated": {"variables": 10, "binary_variables": 6, "integer_variables": 0, "continuous_variables": 4, "disjunctions": 3, "disjuncts": 6, "constraints": 9, "nonlinear_constraints": 0}, "overall": {"variables": 10, "binary_variables": 6, "integer_variables": 0, "continuous_variables": 4, "disjunctions": 3, "disjuncts": 6, "constraints": 9, "nonlinear_constraints": 0}, "warning": {"unassociated_disjuncts": 0}} \ No newline at end of file diff --git a/gdplib/jobshop/model_size_report.md b/gdplib/jobshop/model_size_report.md new file mode 100644 index 0000000..3d4d37e --- /dev/null +++ b/gdplib/jobshop/model_size_report.md @@ -0,0 +1,10 @@ +| Component | Number | +|:----------------------|---------:| +| variables | 10 | +| binary_variables | 6 | +| integer_variables | 0 | +| continuous_variables | 4 | +| disjunctions | 3 | +| disjuncts | 6 | +| constraints | 9 | +| nonlinear_constraints | 0 | \ No newline at end of file From 7fafd9a91390583ebf841f021e6fa01325674152 Mon Sep 17 00:00:00 2001 From: ZedongPeng Date: Tue, 7 May 2024 14:07:23 -0400 Subject: [PATCH 87/99] add name for each model --- gdplib/batch_processing/batch_processing.py | 273 ++- gdplib/disease_model/disease_model.py | 1581 ++++++++++++++++- gdplib/jobshop/jobshop.py | 2 +- .../med_term_purchasing.py | 2 +- 4 files changed, 1769 insertions(+), 89 deletions(-) diff --git a/gdplib/batch_processing/batch_processing.py b/gdplib/batch_processing/batch_processing.py index 55cae74..f11610c 100644 --- a/gdplib/batch_processing/batch_processing.py +++ b/gdplib/batch_processing/batch_processing.py @@ -20,16 +20,16 @@ # tanks. The problem is convexified and has a nonlinear objective and global constraints # NOTE: When I refer to 'gams' in the comments, that is Batch101006_BM.gms for now. It's confusing -# because the _opt file is different (It has hard-coded bigM parameters so that each constraint +# because the _opt file is different (It has hard-coded bigM parameters so that each constraint # has the "optimal" bigM). def build_model(): """ - Constructs and initializes a Pyomo model for the batch processing problem. + Constructs and initializes a Pyomo model for the batch processing problem. The model is designed to minimize the total cost associated with the design and operation of a plant consisting of multiple - parallel processing units with intermediate storage tanks. + parallel processing units with intermediate storage tanks. It involves determining the optimal number and sizes of processing units, batch sizes for different products at various stages, and sizes and placements of storage tanks to ensure operational efficiency while meeting production requirements within a specified time horizon. @@ -40,7 +40,7 @@ def build_model(): Returns ------- Pyomo.ConcreteModel - An instance of the Pyomo ConcreteModel class representing the batch processing optimization model, + An instance of the Pyomo ConcreteModel class representing the batch processing optimization model, ready to be solved with an appropriate solver. References @@ -48,13 +48,12 @@ def build_model(): [1] Ravemark, E. Optimization models for design and operation of chemical batch processes. Ph.D. Thesis, ETH Zurich, 1995. https://doi.org/10.3929/ethz-a-001591449 [2] Vecchietti, A., & Grossmann, I. E. (1999). LOGMIP: a disjunctive 0–1 non-linear optimizer for process system models. Computers & chemical engineering, 23(4-5), 555-565. https://doi.org/10.1016/S0098-1354(97)87539-4 """ - model = AbstractModel() + model = AbstractModel("Batch Processing Optimization Problem") # TODO: it looks like they set a bigM for each j. Which I need to look up how to do... model.BigM = Suffix(direction=Suffix.LOCAL) model.BigM[None] = 1000 - # Constants from GAMS StorageTankSizeFactor = 10 StorageTankSizeFactorByProd = 3 @@ -77,7 +76,7 @@ def build_model(): # TODO: this seems like an over-complicated way to accomplish this task... def filter_out_last(model, j): """ - Filters out the last stage from the set of stages to avoid considering it in certain constraints + Filters out the last stage from the set of stages to avoid considering it in certain constraints or disjunctions where the next stage would be required but doesn't exist. Parameters @@ -94,8 +93,8 @@ def filter_out_last(model, j): Returns True if the stage is not the last one in the set, False otherwise. """ return j != model.STAGES.last() - model.STAGESExceptLast = Set(initialize=model.STAGES, filter=filter_out_last) + model.STAGESExceptLast = Set(initialize=model.STAGES, filter=filter_out_last) # TODO: these aren't in the formulation?? # model.STORAGE_TANKS = Set() @@ -109,13 +108,21 @@ def filter_out_last(model, j): model.Beta2 = Param(doc='Exponent Parameter of the intermediate storage tanks') model.ProductionAmount = Param(model.PRODUCTS, doc='Production Amount') - model.ProductSizeFactor = Param(model.PRODUCTS, model.STAGES, doc='Product Size Factor') + model.ProductSizeFactor = Param( + model.PRODUCTS, model.STAGES, doc='Product Size Factor' + ) model.ProcessingTime = Param(model.PRODUCTS, model.STAGES, doc='Processing Time') # These are hard-coded in the GAMS file, hence the defaults - model.StorageTankSizeFactor = Param(model.STAGES, default=StorageTankSizeFactor, doc='Storage Tank Size Factor') - model.StorageTankSizeFactorByProd = Param(model.PRODUCTS, model.STAGES, - default=StorageTankSizeFactorByProd, doc='Storage Tank Size Factor by Product') + model.StorageTankSizeFactor = Param( + model.STAGES, default=StorageTankSizeFactor, doc='Storage Tank Size Factor' + ) + model.StorageTankSizeFactorByProd = Param( + model.PRODUCTS, + model.STAGES, + default=StorageTankSizeFactorByProd, + doc='Storage Tank Size Factor by Product', + ) # TODO: bonmin wasn't happy and I think it might have something to do with this? # or maybe issues with convexity or a lack thereof... I don't know yet. @@ -123,7 +130,7 @@ def filter_out_last(model, j): # from 1, right? def get_log_coeffs(model, k): """ - Calculates the logarithmic coefficients used in the model, typically for transforming linear + Calculates the logarithmic coefficients used in the model, typically for transforming linear relationships into logarithmic form for optimization purposes. Parameters @@ -140,16 +147,29 @@ def get_log_coeffs(model, k): """ return log(model.PARALLELUNITS.ord(k)) - model.LogCoeffs = Param(model.PARALLELUNITS, initialize=get_log_coeffs, doc='Logarithmic Coefficients') + model.LogCoeffs = Param( + model.PARALLELUNITS, initialize=get_log_coeffs, doc='Logarithmic Coefficients' + ) # bounds - model.volumeLB = Param(model.STAGES, default=VolumeLB, doc='Lower Bound of Volume of the Units') - model.volumeUB = Param(model.STAGES, default=VolumeUB, doc='Upper Bound of Volume of the Units') - model.storageTankSizeLB = Param(model.STAGES, default=StorageTankSizeLB, doc='Lower Bound of Storage Tank Size') - model.storageTankSizeUB = Param(model.STAGES, default=StorageTankSizeUB, doc='Upper Bound of Storage Tank Size') - model.unitsInPhaseUB = Param(model.STAGES, default=UnitsInPhaseUB, doc='Upper Bound of Units in Phase') - model.unitsOutOfPhaseUB = Param(model.STAGES, default=UnitsOutOfPhaseUB, doc='Upper Bound of Units Out of Phase') - + model.volumeLB = Param( + model.STAGES, default=VolumeLB, doc='Lower Bound of Volume of the Units' + ) + model.volumeUB = Param( + model.STAGES, default=VolumeUB, doc='Upper Bound of Volume of the Units' + ) + model.storageTankSizeLB = Param( + model.STAGES, default=StorageTankSizeLB, doc='Lower Bound of Storage Tank Size' + ) + model.storageTankSizeUB = Param( + model.STAGES, default=StorageTankSizeUB, doc='Upper Bound of Storage Tank Size' + ) + model.unitsInPhaseUB = Param( + model.STAGES, default=UnitsInPhaseUB, doc='Upper Bound of Units in Phase' + ) + model.unitsOutOfPhaseUB = Param( + model.STAGES, default=UnitsOutOfPhaseUB, doc='Upper Bound of Units Out of Phase' + ) # Variables @@ -192,9 +212,16 @@ def get_volume_bounds(model, j): A tuple containing the lower and upper bounds for the volume of processing units at stage j.. """ return (model.volumeLB[j], model.volumeUB[j]) - model.volume_log = Var(model.STAGES, bounds=get_volume_bounds, doc='Logarithmic Volume of the Units') - model.batchSize_log = Var(model.PRODUCTS, model.STAGES, doc='Logarithmic Batch Size of the Products') - model.cycleTime_log = Var(model.PRODUCTS, doc='Logarithmic Cycle Time of the Products') + + model.volume_log = Var( + model.STAGES, bounds=get_volume_bounds, doc='Logarithmic Volume of the Units' + ) + model.batchSize_log = Var( + model.PRODUCTS, model.STAGES, doc='Logarithmic Batch Size of the Products' + ) + model.cycleTime_log = Var( + model.PRODUCTS, doc='Logarithmic Cycle Time of the Products' + ) def get_unitsOutOfPhase_bounds(model, j): """ @@ -213,7 +240,12 @@ def get_unitsOutOfPhase_bounds(model, j): A tuple containing the lower and upper bounds for the logarithmic representation of the number of units out of phase at stage j. """ return (0, model.unitsOutOfPhaseUB[j]) - model.unitsOutOfPhase_log = Var(model.STAGES, bounds=get_unitsOutOfPhase_bounds, doc='Logarithmic Units Out of Phase') + + model.unitsOutOfPhase_log = Var( + model.STAGES, + bounds=get_unitsOutOfPhase_bounds, + doc='Logarithmic Units Out of Phase', + ) def get_unitsInPhase_bounds(model, j): """ @@ -232,7 +264,10 @@ def get_unitsInPhase_bounds(model, j): A tuple containing the minimum and maximum bounds for the logarithmic number of units in phase at stage j, ensuring model constraints are met. """ return (0, model.unitsInPhaseUB[j]) - model.unitsInPhase_log = Var(model.STAGES, bounds=get_unitsInPhase_bounds, doc='Logarithmic Units In Phase') + + model.unitsInPhase_log = Var( + model.STAGES, bounds=get_unitsInPhase_bounds, doc='Logarithmic Units In Phase' + ) def get_storageTankSize_bounds(model, j): """ @@ -251,12 +286,21 @@ def get_storageTankSize_bounds(model, j): A tuple containing the lower and upper bounds for the storage tank size at the specified stage. """ return (model.storageTankSizeLB[j], model.storageTankSizeUB[j]) + # TODO: these bounds make it infeasible... - model.storageTankSize_log = Var(model.STAGES, bounds=get_storageTankSize_bounds, doc='Logarithmic Storage Tank Size') + model.storageTankSize_log = Var( + model.STAGES, + bounds=get_storageTankSize_bounds, + doc='Logarithmic Storage Tank Size', + ) # binary variables for deciding number of parallel units in and out of phase - model.outOfPhase = Var(model.STAGES, model.PARALLELUNITS, within=Binary, doc='Out of Phase Units') - model.inPhase = Var(model.STAGES, model.PARALLELUNITS, within=Binary, doc='In Phase Units') + model.outOfPhase = Var( + model.STAGES, model.PARALLELUNITS, within=Binary, doc='Out of Phase Units' + ) + model.inPhase = Var( + model.STAGES, model.PARALLELUNITS, within=Binary, doc='In Phase Units' + ) # Objective @@ -276,18 +320,29 @@ def get_cost_rule(model): Notes ----- - The cost is a function of the volume of processing units and the size of storage tanks, each scaled by respective cost + The cost is a function of the volume of processing units and the size of storage tanks, each scaled by respective cost parameters and exponentiated to reflect non-linear cost relationships. """ - return model.Alpha1 * sum(exp(model.unitsInPhase_log[j] + model.unitsOutOfPhase_log[j] + \ - model.Beta1 * model.volume_log[j]) for j in model.STAGES) +\ - model.Alpha2 * sum(exp(model.Beta2 * model.storageTankSize_log[j]) for j in model.STAGESExceptLast) - model.min_cost = Objective(rule=get_cost_rule, doc='Minimize the Total Cost of the Plant Design') + return model.Alpha1 * sum( + exp( + model.unitsInPhase_log[j] + + model.unitsOutOfPhase_log[j] + + model.Beta1 * model.volume_log[j] + ) + for j in model.STAGES + ) + model.Alpha2 * sum( + exp(model.Beta2 * model.storageTankSize_log[j]) + for j in model.STAGESExceptLast + ) + + model.min_cost = Objective( + rule=get_cost_rule, doc='Minimize the Total Cost of the Plant Design' + ) # Constraints def processing_capacity_rule(model, j, i): """ - Ensures that the volume of each processing unit at stage j is sufficient to accommodate the batch size of product i, + Ensures that the volume of each processing unit at stage j is sufficient to accommodate the batch size of product i, taking into account the size factor of the product and the number of units in phase at that stage. Parameters @@ -305,9 +360,19 @@ def processing_capacity_rule(model, j, i): Pyomo.Expression A Pyomo expression that defines the processing capacity constraint for product `i` at stage `j`. """ - return model.volume_log[j] >= log(model.ProductSizeFactor[i, j]) + model.batchSize_log[i, j] - \ - model.unitsInPhase_log[j] - model.processing_capacity = Constraint(model.STAGES, model.PRODUCTS, rule=processing_capacity_rule, doc='Processing Capacity') + return ( + model.volume_log[j] + >= log(model.ProductSizeFactor[i, j]) + + model.batchSize_log[i, j] + - model.unitsInPhase_log[j] + ) + + model.processing_capacity = Constraint( + model.STAGES, + model.PRODUCTS, + rule=processing_capacity_rule, + doc='Processing Capacity', + ) def processing_time_rule(model, j, i): """ @@ -329,9 +394,16 @@ def processing_time_rule(model, j, i): A Pyomo expression defining the constraint that the cycle time for processing product `i` at stage `j` must not exceed the maximum allowed, considering the batch size and the units out of phase at this stage. """ - return model.cycleTime_log[i] >= log(model.ProcessingTime[i, j]) - model.batchSize_log[i, j] - \ - model.unitsOutOfPhase_log[j] - model.processing_time = Constraint(model.STAGES, model.PRODUCTS, rule=processing_time_rule, doc='Processing Time') + return ( + model.cycleTime_log[i] + >= log(model.ProcessingTime[i, j]) + - model.batchSize_log[i, j] + - model.unitsOutOfPhase_log[j] + ) + + model.processing_time = Constraint( + model.STAGES, model.PRODUCTS, rule=processing_time_rule, doc='Processing Time' + ) def finish_in_time_rule(model): """ @@ -347,16 +419,18 @@ def finish_in_time_rule(model): Pyomo.Expression A Pyomo constraint expression ensuring the total production time does not exceed the time horizon for the plant. """ - return model.HorizonTime >= sum(model.ProductionAmount[i]*exp(model.cycleTime_log[i]) \ - for i in model.PRODUCTS) - model.finish_in_time = Constraint(rule=finish_in_time_rule, doc='Finish in Time') + return model.HorizonTime >= sum( + model.ProductionAmount[i] * exp(model.cycleTime_log[i]) + for i in model.PRODUCTS + ) + model.finish_in_time = Constraint(rule=finish_in_time_rule, doc='Finish in Time') # Disjunctions def storage_tank_selection_disjunct_rule(disjunct, selectStorageTank, j): """ - Defines the conditions under which a storage tank will be included or excluded between stages j and j+1. + Defines the conditions under which a storage tank will be included or excluded between stages j and j+1. This rule is applied to a disjunct, which is part of a disjunction representing this binary decision. Parameters @@ -371,12 +445,13 @@ def storage_tank_selection_disjunct_rule(disjunct, selectStorageTank, j): Returns ------- None - This function defines constraints within the disjunct based on the decision to include (selectStorageTank=1) or exclude (selectStorageTank=0) a storage tank. + This function defines constraints within the disjunct based on the decision to include (selectStorageTank=1) or exclude (selectStorageTank=0) a storage tank. Constraints ensure the storage tank's volume can accommodate the batch sizes at stage j and j+1 if included, or ensure batch size continuity if excluded. """ model = disjunct.model() + def volume_stage_j_rule(disjunct, i): - """ + """ Ensures the storage tank size between stages j and j+1 is sufficient to accommodate the batch size of product i at stage j, considering the storage tank size factor. Parameters @@ -392,9 +467,11 @@ def volume_stage_j_rule(disjunct, i): Pyomo.Constraint A constraint ensuring the storage tank size is sufficient for the batch size at stage j. """ - return model.storageTankSize_log[j] >= log(model.StorageTankSizeFactor[j]) + \ - model.batchSize_log[i, j] - + return ( + model.storageTankSize_log[j] + >= log(model.StorageTankSizeFactor[j]) + model.batchSize_log[i, j] + ) + def volume_stage_jPlus1_rule(disjunct, i): """ Ensures the storage tank size between stages j and j+1 is sufficient to accommodate the batch size of product i at stage j+1, considering the storage tank size factor. @@ -412,9 +489,11 @@ def volume_stage_jPlus1_rule(disjunct, i): Pyomo.Constraint A constraint ensuring the storage tank size is sufficient for the batch size at stage j+1. """ - return model.storageTankSize_log[j] >= log(model.StorageTankSizeFactor[j]) + \ - model.batchSize_log[i, j+1] - + return ( + model.storageTankSize_log[j] + >= log(model.StorageTankSizeFactor[j]) + model.batchSize_log[i, j + 1] + ) + def batch_size_rule(disjunct, i): """ Ensures the difference in batch sizes between stages j and j+1 for product i is within the acceptable limits defined by the storage tank size factor by product. @@ -432,10 +511,12 @@ def batch_size_rule(disjunct, i): Pyomo.Constraint A constraint enforcing acceptable batch size differences between stages j and j+1. """ - return inequality(-log(model.StorageTankSizeFactorByProd[i,j]), - model.batchSize_log[i,j] - model.batchSize_log[i, j+1], - log(model.StorageTankSizeFactorByProd[i,j])) - + return inequality( + -log(model.StorageTankSizeFactorByProd[i, j]), + model.batchSize_log[i, j] - model.batchSize_log[i, j + 1], + log(model.StorageTankSizeFactorByProd[i, j]), + ) + def no_batch_rule(disjunct, i): """ Enforces batch size continuity between stages j and j+1 for product i, applicable when no storage tank is selected. @@ -453,20 +534,28 @@ def no_batch_rule(disjunct, i): Pyomo.Constraint A constraint ensuring batch size continuity between stages j and j+1 """ - return model.batchSize_log[i,j] - model.batchSize_log[i,j+1] == 0 + return model.batchSize_log[i, j] - model.batchSize_log[i, j + 1] == 0 if selectStorageTank: - disjunct.volume_stage_j = Constraint(model.PRODUCTS, rule=volume_stage_j_rule) - disjunct.volume_stage_jPlus1 = Constraint(model.PRODUCTS, - rule=volume_stage_jPlus1_rule) + disjunct.volume_stage_j = Constraint( + model.PRODUCTS, rule=volume_stage_j_rule + ) + disjunct.volume_stage_jPlus1 = Constraint( + model.PRODUCTS, rule=volume_stage_jPlus1_rule + ) disjunct.batch_size = Constraint(model.PRODUCTS, rule=batch_size_rule) else: # The formulation says 0, but GAMS has this constant. # 04/04: Francisco says volume should be free: # disjunct.no_volume = Constraint(expr=model.storageTankSize_log[j] == MinFlow) disjunct.no_batch = Constraint(model.PRODUCTS, rule=no_batch_rule) - model.storage_tank_selection_disjunct = Disjunct([0,1], model.STAGESExceptLast, - rule=storage_tank_selection_disjunct_rule, doc='Storage Tank Selection Disjunct') + + model.storage_tank_selection_disjunct = Disjunct( + [0, 1], + model.STAGESExceptLast, + rule=storage_tank_selection_disjunct_rule, + doc='Storage Tank Selection Disjunct', + ) def select_storage_tanks_rule(model, j): """ @@ -484,8 +573,16 @@ def select_storage_tanks_rule(model, j): list A list of disjuncts representing the choices for including or not including a storage tank between stages j and j+1. """ - return [model.storage_tank_selection_disjunct[selectTank, j] for selectTank in [0,1]] - model.select_storage_tanks = Disjunction(model.STAGESExceptLast, rule=select_storage_tanks_rule, doc='Select Storage Tanks') + return [ + model.storage_tank_selection_disjunct[selectTank, j] + for selectTank in [0, 1] + ] + + model.select_storage_tanks = Disjunction( + model.STAGESExceptLast, + rule=select_storage_tanks_rule, + doc='Select Storage Tanks', + ) # though this is a disjunction in the GAMs model, it is more efficiently formulated this way: # TODO: what on earth is k? Number of Parallel units. @@ -503,16 +600,20 @@ def units_out_of_phase_rule(model, j): Returns ------- None - Adds a constraint to the Pyomo model representing the logarithmic sum of out-of-phase units at stage j. + Adds a constraint to the Pyomo model representing the logarithmic sum of out-of-phase units at stage j. This constraint is not returned but directly added to the model. Notes ----- These are not directly related to disjunctions but more to the logical modeling of unit operations. """ - return model.unitsOutOfPhase_log[j] == sum(model.LogCoeffs[k] * model.outOfPhase[j,k] \ - for k in model.PARALLELUNITS) - model.units_out_of_phase = Constraint(model.STAGES, rule=units_out_of_phase_rule, doc='Units Out of Phase') + return model.unitsOutOfPhase_log[j] == sum( + model.LogCoeffs[k] * model.outOfPhase[j, k] for k in model.PARALLELUNITS + ) + + model.units_out_of_phase = Constraint( + model.STAGES, rule=units_out_of_phase_rule, doc='Units Out of Phase' + ) def units_in_phase_rule(model, j): """_summary_ @@ -528,16 +629,20 @@ def units_in_phase_rule(model, j): Returns ------- None - Incorporates a constraint into the Pyomo model that corresponds to the logarithmic sum of in-phase units at stage j. + Incorporates a constraint into the Pyomo model that corresponds to the logarithmic sum of in-phase units at stage j. The constraint is directly applied to the model without an explicit return value. Notes ----- These are not directly related to disjunctions but more to the logical modeling of unit operations. """ - return model.unitsInPhase_log[j] == sum(model.LogCoeffs[k] * model.inPhase[j,k] \ - for k in model.PARALLELUNITS) - model.units_in_phase = Constraint(model.STAGES, rule=units_in_phase_rule, doc='Units In Phase') + return model.unitsInPhase_log[j] == sum( + model.LogCoeffs[k] * model.inPhase[j, k] for k in model.PARALLELUNITS + ) + + model.units_in_phase = Constraint( + model.STAGES, rule=units_in_phase_rule, doc='Units In Phase' + ) def units_out_of_phase_xor_rule(model, j): """ @@ -555,8 +660,13 @@ def units_out_of_phase_xor_rule(model, j): Pyomo.Constraint A Pyomo constraint expression calculating the logarithmic representation of the number of units out of phase at stage j """ - return sum(model.outOfPhase[j,k] for k in model.PARALLELUNITS) == 1 - model.units_out_of_phase_xor = Constraint(model.STAGES, rule=units_out_of_phase_xor_rule, doc='Exclusive OR for Units Out of Phase') + return sum(model.outOfPhase[j, k] for k in model.PARALLELUNITS) == 1 + + model.units_out_of_phase_xor = Constraint( + model.STAGES, + rule=units_out_of_phase_xor_rule, + doc='Exclusive OR for Units Out of Phase', + ) def units_in_phase_xor_rule(model, j): """ @@ -574,8 +684,13 @@ def units_in_phase_xor_rule(model, j): Pyomo.Constraint A Pyomo constraint expression enforcing the XOR condition for units out of phase at stage j. """ - return sum(model.inPhase[j,k] for k in model.PARALLELUNITS) == 1 - model.units_in_phase_xor = Constraint(model.STAGES, rule=units_in_phase_xor_rule, doc='Exclusive OR for Units In Phase') + return sum(model.inPhase[j, k] for k in model.PARALLELUNITS) == 1 + + model.units_in_phase_xor = Constraint( + model.STAGES, + rule=units_in_phase_xor_rule, + doc='Exclusive OR for Units In Phase', + ) return model.create_instance(join(this_file_dir(), 'batch_processing.dat')) @@ -583,5 +698,7 @@ def units_in_phase_xor_rule(model, j): if __name__ == "__main__": m = build_model() TransformationFactory('gdp.bigm').apply_to(m) - SolverFactory('gams').solve(m, solver='baron', tee=True, add_options=['option optcr=1e-6;']) + SolverFactory('gams').solve( + m, solver='baron', tee=True, add_options=['option optcr=1e-6;'] + ) m.min_cost.display() diff --git a/gdplib/disease_model/disease_model.py b/gdplib/disease_model/disease_model.py index 47b7b81..1f60644 100644 --- a/gdplib/disease_model/disease_model.py +++ b/gdplib/disease_model/disease_model.py @@ -39,19 +39,1582 @@ def build_model(): # import data # Population Data - pop = [ 15.881351, 15.881339, 15.881320, 15.881294, 15.881261, 15.881223, 15.881180, 15.881132, 15.881079, 15.881022, 15.880961, 15.880898, 15.880832, 15.880764, 15.880695, 15.880624, 15.880553, 15.880480, 15.880409, 15.880340, 15.880270, 15.880203, 15.880138, 15.880076, 15.880016, 15.879960, 15.879907, 15.879852, 15.879799, 15.879746, 15.879693, 15.879638, 15.879585, 15.879531, 15.879477, 15.879423, 15.879370, 15.879315, 15.879262, 15.879209, 15.879155, 15.879101, 15.879048, 15.878994, 15.878940, 15.878886, 15.878833, 15.878778, 15.878725, 15.878672, 15.878618, 15.878564, 15.878510, 15.878457, 15.878402, 15.878349, 15.878295, 15.878242, 15.878187, 15.878134, 15.878081, 15.878026, 15.877973, 15.877919, 15.877864, 15.877811, 15.877758, 15.877704, 15.877650, 15.877596, 15.877543, 15.877488, 15.877435, 15.877381, 15.877326, 15.877273, 15.877220, 15.877166, 15.877111, 15.877058, 15.877005, 15.876950, 15.876896, 15.876843, 15.876789, 15.876735, 15.876681, 15.876628, 15.876573, 15.876520, 15.876466, 15.876411, 15.876358, 15.876304, 15.876251, 15.876196, 15.876143, 15.876089, 15.876034, 15.875981, 15.875927, 15.875872, 15.875819, 15.875765, 15.875712, 15.875657, 15.875604, 15.875550, 15.875495, 15.875442, 15.875388, 15.875335, 15.875280, 15.875226, 15.875173, 15.875118, 15.875064, 15.875011, 15.874956, 15.874902, 15.874849, 15.874795, 15.874740, 15.874687, 15.874633, 15.874578, 15.874525, 15.874471, 15.874416, 15.874363, 15.874309, 15.874256, 15.874201, 15.874147, 15.874094, 15.874039, 15.873985, 15.873931, 15.873878, 15.873823, 15.873769, 15.873716, 15.873661, 15.873607, 15.873554, 15.873499, 15.873445, 15.873391, 15.873338, 15.873283, 15.873229, 15.873175, 15.873121, 15.873067, 15.873013, 15.872960, 15.872905, 15.872851, 15.872797, 15.872742, 15.872689, 15.872635, 15.872580, 15.872526, 15.872473, 15.872419, 15.872364, 15.872310, 15.872256, 15.872202, 15.872148, 15.872094, 15.872039, 15.871985, 15.871932, 15.871878, 15.871823, 15.871769, 15.871715, 15.871660, 15.871607, 15.871553, 15.871499, 15.871444, 15.871390, 15.871337, 15.871282, 15.871228, 15.871174, 15.871119, 15.871065, 15.871012, 15.870958, 15.870903, 15.870849, 15.870795, 15.870740, 15.870686, 15.870633, 15.870577, 15.870524, 15.870470, 15.870416, 15.870361, 15.870307, 15.870253, 15.870198, 15.870144, 15.870091, 15.870037, 15.869982, 15.869928, 15.869874, 15.869819, 15.869765, 15.869711, 15.869656, 15.869602, 15.869548, 15.869495, 15.869439, 15.869386, 15.869332, 15.869277, 15.869223, 15.869169, 15.869114, 15.869060, 15.869006, 15.868952, 15.868897, 15.868843, 15.868789, 15.868734, 15.868679, 15.868618, 15.868556, 15.868489, 15.868421, 15.868351, 15.868280, 15.868208, 15.868134, 15.868063, 15.867991, 15.867921, 15.867852, 15.867785, 15.867721, 15.867659, 15.867601, 15.867549, 15.867499, 15.867455, 15.867416, 15.867383, 15.867357, 15.867338, 15.867327, 15.867321, 15.867327, 15.867338, 15.867359, 15.867386, 15.867419, 15.867459, 15.867505, 15.867555, 15.867610, 15.867671, 15.867734, 15.867801, 15.867869, 15.867941, 15.868012, 15.868087, 15.868161, 15.868236, 15.868310, 15.868384, 15.868457, 15.868527, 15.868595, 15.868661, 15.868722, 15.868780, 15.868837, 15.868892, 15.868948, 15.869005, 15.869061, 15.869116, 15.869173, 15.869229, 15.869284, 15.869341, 15.869397, 15.869452, 15.869509, 15.869565, 15.869620, 15.869677, 15.869733, 15.869788, 15.869845, 15.869901, 15.869956, 15.870012, 15.870069, 15.870124, 15.870180, 15.870237, 15.870292, 15.870348, 15.870405, 15.870461, 15.870516, 15.870572, 15.870629, 15.870684, 15.870740, 15.870796, 15.870851, 15.870908, 15.870964, 15.871019, 15.871076, 15.871132, 15.871187, 15.871243, 15.871300, 15.871355, 15.871411, 15.871467, 15.871522, 15.871579, 15.871635, 15.871691, 15.871746, 15.871802, 15.871859, 15.871914, 15.871970, 15.872026, 15.872081, 15.872138, 15.872194, 15.872249, 15.872305, 15.872361, 15.872416, 15.872473, 15.872529, 15.872584, 15.872640, 15.872696, 15.872751, 15.872807, 15.872864, 15.872919, 15.872975, 15.873031, 15.873087, 15.873142, 15.873198, 15.873255, 15.873310, 15.873366, 15.873422, 15.873477, 15.873533, 15.873589, 15.873644, 15.873700, 15.873757, 15.873811, 15.873868, 15.873924, 15.873979, 15.874035, 15.874091, 15.874146, 15.874202, 15.874258, 15.874313, 15.874369, 15.874425, 15.874481, 15.874536, 15.874592] - - logIstar = [7.943245, 8.269994, 8.517212, 8.814208, 9.151740, 9.478472, 9.559847, 9.664087, 9.735378, 9.852583, 9.692265, 9.498807, 9.097634, 8.388878, 7.870516, 7.012956, 6.484941, 5.825368, 5.346815, 5.548361, 5.706732, 5.712617, 5.709714, 5.696888, 5.530087, 5.826563, 6.643563, 7.004292, 7.044663, 7.190259, 7.335926, 7.516861, 7.831779, 8.188895, 8.450204, 8.801436, 8.818379, 8.787658, 8.601685, 8.258338, 7.943364, 7.425585, 7.062834, 6.658307, 6.339600, 6.526984, 6.679178, 6.988758, 7.367331, 7.746694, 8.260558, 8.676522, 9.235582, 9.607778, 9.841917, 10.081571, 10.216090, 10.350366, 10.289668, 10.248842, 10.039504, 9.846343, 9.510392, 9.190923, 8.662465, 7.743221, 7.128458, 5.967898, 5.373883, 5.097497, 4.836570, 5.203345, 5.544798, 5.443047, 5.181152, 5.508669, 6.144130, 6.413744, 6.610423, 6.748885, 6.729511, 6.789841, 6.941034, 7.093516, 7.307039, 7.541077, 7.644803, 7.769145, 7.760187, 7.708017, 7.656795, 7.664983, 7.483828, 6.887324, 6.551093, 6.457449, 6.346064, 6.486300, 6.612378, 6.778753, 6.909477, 7.360570, 8.150303, 8.549044, 8.897572, 9.239323, 9.538751, 9.876531, 10.260911, 10.613536, 10.621510, 10.661115, 10.392899, 10.065536, 9.920090, 9.933097, 9.561691, 8.807713, 8.263463, 7.252184, 6.669083, 5.877763, 5.331878, 5.356563, 5.328469, 5.631146, 6.027497, 6.250717, 6.453919, 6.718444, 7.071636, 7.348905, 7.531528, 7.798226, 8.197941, 8.578809, 8.722964, 8.901152, 8.904370, 8.889865, 8.881902, 8.958903, 8.721281, 8.211509, 7.810624, 7.164607, 6.733688, 6.268503, 5.905983, 5.900432, 5.846547, 6.245427, 6.786271, 7.088480, 7.474295, 7.650063, 7.636703, 7.830990, 8.231516, 8.584816, 8.886908, 9.225216, 9.472778, 9.765505, 9.928623, 10.153033, 10.048574, 9.892620, 9.538818, 8.896100, 8.437584, 7.819738, 7.362598, 6.505880, 5.914972, 6.264584, 6.555019, 6.589319, 6.552029, 6.809771, 7.187616, 7.513918, 8.017712, 8.224957, 8.084474, 8.079148, 8.180991, 8.274269, 8.413748, 8.559599, 8.756090, 9.017927, 9.032720, 9.047983, 8.826873, 8.366489, 8.011876, 7.500830, 7.140406, 6.812626, 6.538719, 6.552218, 6.540129, 6.659927, 6.728530, 7.179692, 7.989210, 8.399173, 8.781128, 9.122303, 9.396378, 9.698512, 9.990104, 10.276543, 10.357284, 10.465869, 10.253833, 10.018503, 9.738407, 9.484367, 9.087025, 8.526409, 8.041126, 7.147168, 6.626706, 6.209446, 5.867231, 5.697439, 5.536769, 5.421413, 5.238297, 5.470136, 5.863007, 6.183083, 6.603569, 6.906278, 7.092324, 7.326612, 7.576052, 7.823430, 7.922775, 8.041677, 8.063403, 8.073229, 8.099726, 8.168522, 8.099041, 8.011404, 7.753147, 6.945211, 6.524244, 6.557723, 6.497742, 6.256247, 5.988794, 6.268093, 6.583316, 7.106842, 8.053929, 8.508237, 8.938915, 9.311863, 9.619753, 9.931745, 10.182361, 10.420978, 10.390829, 10.389230, 10.079342, 9.741479, 9.444561, 9.237448, 8.777687, 7.976436, 7.451502, 6.742856, 6.271545, 5.782289, 5.403089, 5.341954, 5.243509, 5.522993, 5.897001, 6.047042, 6.100738, 6.361727, 6.849562, 7.112544, 7.185346, 7.309412, 7.423746, 7.532142, 7.510318, 7.480175, 7.726362, 8.061117, 8.127072, 8.206166, 8.029634, 7.592953, 7.304869, 7.005394, 6.750019, 6.461377, 6.226432, 6.287047, 6.306452, 6.783694, 7.450957, 7.861692, 8.441530, 8.739626, 8.921994, 9.168961, 9.428077, 9.711664, 10.032714, 10.349937, 10.483985, 10.647475, 10.574038, 10.522431, 10.192246, 9.756246, 9.342511, 8.872072, 8.414189, 7.606582, 7.084701, 6.149903, 5.517257, 5.839429, 6.098090, 6.268935, 6.475965, 6.560543, 6.598942, 6.693938, 6.802531, 6.934345, 7.078370, 7.267736, 7.569640, 7.872204, 8.083603, 8.331226, 8.527144, 8.773523, 8.836599, 8.894303, 8.808326, 8.641717, 8.397901, 7.849034, 7.482899, 7.050252, 6.714103, 6.900603, 7.050765, 7.322905, 7.637986, 8.024340, 8.614505, 8.933591, 9.244008, 9.427410, 9.401385, 9.457744, 9.585068, 9.699673, 9.785478, 9.884559, 9.769732, 9.655075, 9.423071, 9.210198, 8.786654, 8.061787, 7.560976, 6.855829, 6.390707, 5.904006, 5.526631, 5.712303, 5.867027, 5.768367, 5.523352, 5.909118, 6.745543, 6.859218 ] - - deltaS = [ 9916.490263 ,12014.263380 ,13019.275755 ,12296.373612 ,8870.995603 ,1797.354574 ,-6392.880771 ,-16150.825387 ,-27083.245106 ,-40130.421462 ,-50377.169958 ,-57787.717468 ,-60797.223427 ,-59274.041897 ,-55970.213230 ,-51154.650927 ,-45877.841034 ,-40278.553775 ,-34543.967175 ,-28849.633641 ,-23192.776605 ,-17531.130740 ,-11862.021829 ,-6182.456792 ,-450.481090 ,5201.184400 ,10450.773882 ,15373.018272 ,20255.699431 ,24964.431669 ,29470.745887 ,33678.079947 ,37209.808930 ,39664.432393 ,41046.735479 ,40462.982011 ,39765.070209 ,39270.815830 ,39888.077002 ,42087.276604 ,45332.012929 ,49719.128772 ,54622.190928 ,59919.718626 ,65436.341097 ,70842.911460 ,76143.747430 ,81162.358574 ,85688.102884 ,89488.917734 ,91740.108470 ,91998.787916 ,87875.986012 ,79123.877908 ,66435.611045 ,48639.250610 ,27380.282817 ,2166.538464 ,-21236.428084 ,-43490.803535 ,-60436.624080 ,-73378.401966 ,-80946.278268 ,-84831.969493 ,-84696.627286 ,-81085.365407 ,-76410.847049 ,-70874.415387 ,-65156.276464 ,-59379.086883 ,-53557.267619 ,-47784.164830 ,-42078.001172 ,-36340.061427 ,-30541.788202 ,-24805.281435 ,-19280.817165 ,-13893.690606 ,-8444.172221 ,-3098.160839 ,2270.908649 ,7594.679295 ,12780.079247 ,17801.722109 ,22543.091206 ,26897.369814 ,31051.285734 ,34933.809557 ,38842.402859 ,42875.230152 ,47024.395356 ,51161.516122 ,55657.298307 ,60958.155424 ,66545.635029 ,72202.930397 ,77934.761905 ,83588.207792 ,89160.874522 ,94606.115027 ,99935.754968 ,104701.404975 ,107581.670606 ,108768.440311 ,107905.700480 ,104062.148863 ,96620.281684 ,83588.443029 ,61415.088182 ,27124.031692 ,-7537.285321 ,-43900.451653 ,-70274.062783 ,-87573.481475 ,-101712.148408 ,-116135.719087 ,-124187.225446 ,-124725.278371 ,-122458.145590 ,-117719.918256 ,-112352.138605 ,-106546.806030 ,-100583.803012 ,-94618.253238 ,-88639.090897 ,-82725.009842 ,-76938.910669 ,-71248.957807 ,-65668.352795 ,-60272.761991 ,-55179.538428 ,-50456.021161 ,-46037.728058 ,-42183.912670 ,-39522.184006 ,-38541.255303 ,-38383.665728 ,-39423.998130 ,-40489.466130 ,-41450.406768 ,-42355.156592 ,-43837.562085 ,-43677.262972 ,-41067.896944 ,-37238.628465 ,-32230.392026 ,-26762.766062 ,-20975.163308 ,-15019.218554 ,-9053.105545 ,-3059.663132 ,2772.399618 ,8242.538397 ,13407.752291 ,18016.047539 ,22292.125752 ,26616.583347 ,30502.564253 ,33153.890890 ,34216.684448 ,33394.220786 ,29657.417791 ,23064.375405 ,12040.831532 ,-2084.921068 ,-21390.235970 ,-38176.615985 ,-51647.714482 ,-59242.564959 ,-60263.150854 ,-58599.245165 ,-54804.972560 ,-50092.112608 ,-44465.812552 ,-38533.096297 ,-32747.104307 ,-27130.082610 ,-21529.632955 ,-15894.611939 ,-10457.566933 ,-5429.042583 ,-903.757828 ,2481.947589 ,5173.789976 ,8358.768202 ,11565.584635 ,14431.147931 ,16951.619820 ,18888.807708 ,20120.884465 ,20222.141242 ,18423.168124 ,16498.668271 ,14442.624242 ,14070.038273 ,16211.370808 ,19639.815904 ,24280.360465 ,29475.380079 ,35030.793540 ,40812.325095 ,46593.082382 ,52390.906885 ,58109.310860 ,63780.896094 ,68984.456561 ,72559.442320 ,74645.487900 ,74695.219755 ,72098.143876 ,66609.929889 ,56864.971296 ,41589.295266 ,19057.032104 ,-5951.329863 ,-34608.796853 ,-56603.801584 ,-72678.838057 ,-83297.070856 ,-90127.593511 ,-92656.040614 ,-91394.995510 ,-88192.056842 ,-83148.833075 ,-77582.587173 ,-71750.440823 ,-65765.369857 ,-59716.101820 ,-53613.430067 ,-47473.832358 ,-41287.031890 ,-35139.919259 ,-29097.671507 ,-23178.836760 ,-17486.807388 ,-12046.775779 ,-6802.483422 ,-1867.556171 ,2644.380534 ,6615.829501 ,10332.557518 ,13706.737038 ,17017.991307 ,20303.136670 ,23507.386461 ,26482.194102 ,29698.585356 ,33196.305757 ,37385.914179 ,42872.996212 ,48725.617879 ,54564.488527 ,60453.841604 ,66495.146265 ,72668.620416 ,78723.644870 ,84593.136677 ,89974.936239 ,93439.798630 ,95101.207834 ,94028.126381 ,89507.925620 ,80989.846001 ,66944.274744 ,47016.422041 ,19932.783790 ,-6198.433172 ,-32320.379400 ,-49822.852084 ,-60517.553414 ,-66860.548269 ,-70849.714105 ,-71058.721556 ,-67691.947812 ,-63130.703822 ,-57687.607311 ,-51916.952488 ,-45932.054982 ,-39834.909941 ,-33714.535713 ,-27564.443333 ,-21465.186188 ,-15469.326408 ,-9522.358787 ,-3588.742161 ,2221.802073 ,7758.244339 ,13020.269708 ,18198.562827 ,23211.338588 ,28051.699645 ,32708.577247 ,37413.795242 ,42181.401920 ,46462.499633 ,49849.582315 ,53026.578940 ,55930.600705 ,59432.642178 ,64027.356857 ,69126.843653 ,74620.328837 ,80372.056070 ,86348.152766 ,92468.907239 ,98568.998246 ,104669.511588 ,110445.790143 ,115394.348973 ,119477.553152 ,121528.574511 ,121973.674087 ,121048.017786 ,118021.473181 ,112151.993711 ,102195.999157 ,85972.731130 ,61224.719621 ,31949.279603 ,-3726.022971 ,-36485.298619 ,-67336.469799 ,-87799.366129 ,-98865.713558 ,-104103.651120 ,-105068.402300 ,-103415.820781 ,-99261.356633 ,-94281.850081 ,-88568.701325 ,-82625.711921 ,-76766.776770 ,-70998.803524 ,-65303.404499 ,-59719.198305 ,-54182.230439 ,-48662.904657 ,-43206.731668 ,-37732.701095 ,-32375.478519 ,-27167.508567 ,-22197.211891 ,-17722.869502 ,-13925.135219 ,-10737.893027 ,-8455.327914 ,-7067.008358 ,-7086.991191 ,-7527.693561 ,-8378.025732 ,-8629.383998 ,-7854.586079 ,-5853.040657 ,-1973.225485 ,2699.850783 ,8006.098287 ,13651.734934 ,19139.318072 ,24476.645420 ,29463.480336 ,33899.078820 ,37364.528796 ,38380.214949 ,37326.585649 ,33428.470616 ,27441.000494 ,21761.126583 ,15368.408081 ,7224.234078 ,-2702.217396 ,-14109.682505 ,-27390.915614 ,-38569.562393 ,-47875.155339 ,-53969.121872 ,-57703.473001 ,-57993.198171 ,-54908.391840 ,-50568.410328 ,-45247.622563 ,-39563.224328 ,-33637.786521 ,-27585.345413 ,-21572.074797 ,-15597.363909 ,-9577.429076 ,-3475.770622 ,2520.378408 ,8046.881775 ,13482.345595 ] - - beta_set = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26] - + pop = [ + 15.881351, + 15.881339, + 15.881320, + 15.881294, + 15.881261, + 15.881223, + 15.881180, + 15.881132, + 15.881079, + 15.881022, + 15.880961, + 15.880898, + 15.880832, + 15.880764, + 15.880695, + 15.880624, + 15.880553, + 15.880480, + 15.880409, + 15.880340, + 15.880270, + 15.880203, + 15.880138, + 15.880076, + 15.880016, + 15.879960, + 15.879907, + 15.879852, + 15.879799, + 15.879746, + 15.879693, + 15.879638, + 15.879585, + 15.879531, + 15.879477, + 15.879423, + 15.879370, + 15.879315, + 15.879262, + 15.879209, + 15.879155, + 15.879101, + 15.879048, + 15.878994, + 15.878940, + 15.878886, + 15.878833, + 15.878778, + 15.878725, + 15.878672, + 15.878618, + 15.878564, + 15.878510, + 15.878457, + 15.878402, + 15.878349, + 15.878295, + 15.878242, + 15.878187, + 15.878134, + 15.878081, + 15.878026, + 15.877973, + 15.877919, + 15.877864, + 15.877811, + 15.877758, + 15.877704, + 15.877650, + 15.877596, + 15.877543, + 15.877488, + 15.877435, + 15.877381, + 15.877326, + 15.877273, + 15.877220, + 15.877166, + 15.877111, + 15.877058, + 15.877005, + 15.876950, + 15.876896, + 15.876843, + 15.876789, + 15.876735, + 15.876681, + 15.876628, + 15.876573, + 15.876520, + 15.876466, + 15.876411, + 15.876358, + 15.876304, + 15.876251, + 15.876196, + 15.876143, + 15.876089, + 15.876034, + 15.875981, + 15.875927, + 15.875872, + 15.875819, + 15.875765, + 15.875712, + 15.875657, + 15.875604, + 15.875550, + 15.875495, + 15.875442, + 15.875388, + 15.875335, + 15.875280, + 15.875226, + 15.875173, + 15.875118, + 15.875064, + 15.875011, + 15.874956, + 15.874902, + 15.874849, + 15.874795, + 15.874740, + 15.874687, + 15.874633, + 15.874578, + 15.874525, + 15.874471, + 15.874416, + 15.874363, + 15.874309, + 15.874256, + 15.874201, + 15.874147, + 15.874094, + 15.874039, + 15.873985, + 15.873931, + 15.873878, + 15.873823, + 15.873769, + 15.873716, + 15.873661, + 15.873607, + 15.873554, + 15.873499, + 15.873445, + 15.873391, + 15.873338, + 15.873283, + 15.873229, + 15.873175, + 15.873121, + 15.873067, + 15.873013, + 15.872960, + 15.872905, + 15.872851, + 15.872797, + 15.872742, + 15.872689, + 15.872635, + 15.872580, + 15.872526, + 15.872473, + 15.872419, + 15.872364, + 15.872310, + 15.872256, + 15.872202, + 15.872148, + 15.872094, + 15.872039, + 15.871985, + 15.871932, + 15.871878, + 15.871823, + 15.871769, + 15.871715, + 15.871660, + 15.871607, + 15.871553, + 15.871499, + 15.871444, + 15.871390, + 15.871337, + 15.871282, + 15.871228, + 15.871174, + 15.871119, + 15.871065, + 15.871012, + 15.870958, + 15.870903, + 15.870849, + 15.870795, + 15.870740, + 15.870686, + 15.870633, + 15.870577, + 15.870524, + 15.870470, + 15.870416, + 15.870361, + 15.870307, + 15.870253, + 15.870198, + 15.870144, + 15.870091, + 15.870037, + 15.869982, + 15.869928, + 15.869874, + 15.869819, + 15.869765, + 15.869711, + 15.869656, + 15.869602, + 15.869548, + 15.869495, + 15.869439, + 15.869386, + 15.869332, + 15.869277, + 15.869223, + 15.869169, + 15.869114, + 15.869060, + 15.869006, + 15.868952, + 15.868897, + 15.868843, + 15.868789, + 15.868734, + 15.868679, + 15.868618, + 15.868556, + 15.868489, + 15.868421, + 15.868351, + 15.868280, + 15.868208, + 15.868134, + 15.868063, + 15.867991, + 15.867921, + 15.867852, + 15.867785, + 15.867721, + 15.867659, + 15.867601, + 15.867549, + 15.867499, + 15.867455, + 15.867416, + 15.867383, + 15.867357, + 15.867338, + 15.867327, + 15.867321, + 15.867327, + 15.867338, + 15.867359, + 15.867386, + 15.867419, + 15.867459, + 15.867505, + 15.867555, + 15.867610, + 15.867671, + 15.867734, + 15.867801, + 15.867869, + 15.867941, + 15.868012, + 15.868087, + 15.868161, + 15.868236, + 15.868310, + 15.868384, + 15.868457, + 15.868527, + 15.868595, + 15.868661, + 15.868722, + 15.868780, + 15.868837, + 15.868892, + 15.868948, + 15.869005, + 15.869061, + 15.869116, + 15.869173, + 15.869229, + 15.869284, + 15.869341, + 15.869397, + 15.869452, + 15.869509, + 15.869565, + 15.869620, + 15.869677, + 15.869733, + 15.869788, + 15.869845, + 15.869901, + 15.869956, + 15.870012, + 15.870069, + 15.870124, + 15.870180, + 15.870237, + 15.870292, + 15.870348, + 15.870405, + 15.870461, + 15.870516, + 15.870572, + 15.870629, + 15.870684, + 15.870740, + 15.870796, + 15.870851, + 15.870908, + 15.870964, + 15.871019, + 15.871076, + 15.871132, + 15.871187, + 15.871243, + 15.871300, + 15.871355, + 15.871411, + 15.871467, + 15.871522, + 15.871579, + 15.871635, + 15.871691, + 15.871746, + 15.871802, + 15.871859, + 15.871914, + 15.871970, + 15.872026, + 15.872081, + 15.872138, + 15.872194, + 15.872249, + 15.872305, + 15.872361, + 15.872416, + 15.872473, + 15.872529, + 15.872584, + 15.872640, + 15.872696, + 15.872751, + 15.872807, + 15.872864, + 15.872919, + 15.872975, + 15.873031, + 15.873087, + 15.873142, + 15.873198, + 15.873255, + 15.873310, + 15.873366, + 15.873422, + 15.873477, + 15.873533, + 15.873589, + 15.873644, + 15.873700, + 15.873757, + 15.873811, + 15.873868, + 15.873924, + 15.873979, + 15.874035, + 15.874091, + 15.874146, + 15.874202, + 15.874258, + 15.874313, + 15.874369, + 15.874425, + 15.874481, + 15.874536, + 15.874592, + ] + + logIstar = [ + 7.943245, + 8.269994, + 8.517212, + 8.814208, + 9.151740, + 9.478472, + 9.559847, + 9.664087, + 9.735378, + 9.852583, + 9.692265, + 9.498807, + 9.097634, + 8.388878, + 7.870516, + 7.012956, + 6.484941, + 5.825368, + 5.346815, + 5.548361, + 5.706732, + 5.712617, + 5.709714, + 5.696888, + 5.530087, + 5.826563, + 6.643563, + 7.004292, + 7.044663, + 7.190259, + 7.335926, + 7.516861, + 7.831779, + 8.188895, + 8.450204, + 8.801436, + 8.818379, + 8.787658, + 8.601685, + 8.258338, + 7.943364, + 7.425585, + 7.062834, + 6.658307, + 6.339600, + 6.526984, + 6.679178, + 6.988758, + 7.367331, + 7.746694, + 8.260558, + 8.676522, + 9.235582, + 9.607778, + 9.841917, + 10.081571, + 10.216090, + 10.350366, + 10.289668, + 10.248842, + 10.039504, + 9.846343, + 9.510392, + 9.190923, + 8.662465, + 7.743221, + 7.128458, + 5.967898, + 5.373883, + 5.097497, + 4.836570, + 5.203345, + 5.544798, + 5.443047, + 5.181152, + 5.508669, + 6.144130, + 6.413744, + 6.610423, + 6.748885, + 6.729511, + 6.789841, + 6.941034, + 7.093516, + 7.307039, + 7.541077, + 7.644803, + 7.769145, + 7.760187, + 7.708017, + 7.656795, + 7.664983, + 7.483828, + 6.887324, + 6.551093, + 6.457449, + 6.346064, + 6.486300, + 6.612378, + 6.778753, + 6.909477, + 7.360570, + 8.150303, + 8.549044, + 8.897572, + 9.239323, + 9.538751, + 9.876531, + 10.260911, + 10.613536, + 10.621510, + 10.661115, + 10.392899, + 10.065536, + 9.920090, + 9.933097, + 9.561691, + 8.807713, + 8.263463, + 7.252184, + 6.669083, + 5.877763, + 5.331878, + 5.356563, + 5.328469, + 5.631146, + 6.027497, + 6.250717, + 6.453919, + 6.718444, + 7.071636, + 7.348905, + 7.531528, + 7.798226, + 8.197941, + 8.578809, + 8.722964, + 8.901152, + 8.904370, + 8.889865, + 8.881902, + 8.958903, + 8.721281, + 8.211509, + 7.810624, + 7.164607, + 6.733688, + 6.268503, + 5.905983, + 5.900432, + 5.846547, + 6.245427, + 6.786271, + 7.088480, + 7.474295, + 7.650063, + 7.636703, + 7.830990, + 8.231516, + 8.584816, + 8.886908, + 9.225216, + 9.472778, + 9.765505, + 9.928623, + 10.153033, + 10.048574, + 9.892620, + 9.538818, + 8.896100, + 8.437584, + 7.819738, + 7.362598, + 6.505880, + 5.914972, + 6.264584, + 6.555019, + 6.589319, + 6.552029, + 6.809771, + 7.187616, + 7.513918, + 8.017712, + 8.224957, + 8.084474, + 8.079148, + 8.180991, + 8.274269, + 8.413748, + 8.559599, + 8.756090, + 9.017927, + 9.032720, + 9.047983, + 8.826873, + 8.366489, + 8.011876, + 7.500830, + 7.140406, + 6.812626, + 6.538719, + 6.552218, + 6.540129, + 6.659927, + 6.728530, + 7.179692, + 7.989210, + 8.399173, + 8.781128, + 9.122303, + 9.396378, + 9.698512, + 9.990104, + 10.276543, + 10.357284, + 10.465869, + 10.253833, + 10.018503, + 9.738407, + 9.484367, + 9.087025, + 8.526409, + 8.041126, + 7.147168, + 6.626706, + 6.209446, + 5.867231, + 5.697439, + 5.536769, + 5.421413, + 5.238297, + 5.470136, + 5.863007, + 6.183083, + 6.603569, + 6.906278, + 7.092324, + 7.326612, + 7.576052, + 7.823430, + 7.922775, + 8.041677, + 8.063403, + 8.073229, + 8.099726, + 8.168522, + 8.099041, + 8.011404, + 7.753147, + 6.945211, + 6.524244, + 6.557723, + 6.497742, + 6.256247, + 5.988794, + 6.268093, + 6.583316, + 7.106842, + 8.053929, + 8.508237, + 8.938915, + 9.311863, + 9.619753, + 9.931745, + 10.182361, + 10.420978, + 10.390829, + 10.389230, + 10.079342, + 9.741479, + 9.444561, + 9.237448, + 8.777687, + 7.976436, + 7.451502, + 6.742856, + 6.271545, + 5.782289, + 5.403089, + 5.341954, + 5.243509, + 5.522993, + 5.897001, + 6.047042, + 6.100738, + 6.361727, + 6.849562, + 7.112544, + 7.185346, + 7.309412, + 7.423746, + 7.532142, + 7.510318, + 7.480175, + 7.726362, + 8.061117, + 8.127072, + 8.206166, + 8.029634, + 7.592953, + 7.304869, + 7.005394, + 6.750019, + 6.461377, + 6.226432, + 6.287047, + 6.306452, + 6.783694, + 7.450957, + 7.861692, + 8.441530, + 8.739626, + 8.921994, + 9.168961, + 9.428077, + 9.711664, + 10.032714, + 10.349937, + 10.483985, + 10.647475, + 10.574038, + 10.522431, + 10.192246, + 9.756246, + 9.342511, + 8.872072, + 8.414189, + 7.606582, + 7.084701, + 6.149903, + 5.517257, + 5.839429, + 6.098090, + 6.268935, + 6.475965, + 6.560543, + 6.598942, + 6.693938, + 6.802531, + 6.934345, + 7.078370, + 7.267736, + 7.569640, + 7.872204, + 8.083603, + 8.331226, + 8.527144, + 8.773523, + 8.836599, + 8.894303, + 8.808326, + 8.641717, + 8.397901, + 7.849034, + 7.482899, + 7.050252, + 6.714103, + 6.900603, + 7.050765, + 7.322905, + 7.637986, + 8.024340, + 8.614505, + 8.933591, + 9.244008, + 9.427410, + 9.401385, + 9.457744, + 9.585068, + 9.699673, + 9.785478, + 9.884559, + 9.769732, + 9.655075, + 9.423071, + 9.210198, + 8.786654, + 8.061787, + 7.560976, + 6.855829, + 6.390707, + 5.904006, + 5.526631, + 5.712303, + 5.867027, + 5.768367, + 5.523352, + 5.909118, + 6.745543, + 6.859218, + ] + + deltaS = [ + 9916.490263, + 12014.263380, + 13019.275755, + 12296.373612, + 8870.995603, + 1797.354574, + -6392.880771, + -16150.825387, + -27083.245106, + -40130.421462, + -50377.169958, + -57787.717468, + -60797.223427, + -59274.041897, + -55970.213230, + -51154.650927, + -45877.841034, + -40278.553775, + -34543.967175, + -28849.633641, + -23192.776605, + -17531.130740, + -11862.021829, + -6182.456792, + -450.481090, + 5201.184400, + 10450.773882, + 15373.018272, + 20255.699431, + 24964.431669, + 29470.745887, + 33678.079947, + 37209.808930, + 39664.432393, + 41046.735479, + 40462.982011, + 39765.070209, + 39270.815830, + 39888.077002, + 42087.276604, + 45332.012929, + 49719.128772, + 54622.190928, + 59919.718626, + 65436.341097, + 70842.911460, + 76143.747430, + 81162.358574, + 85688.102884, + 89488.917734, + 91740.108470, + 91998.787916, + 87875.986012, + 79123.877908, + 66435.611045, + 48639.250610, + 27380.282817, + 2166.538464, + -21236.428084, + -43490.803535, + -60436.624080, + -73378.401966, + -80946.278268, + -84831.969493, + -84696.627286, + -81085.365407, + -76410.847049, + -70874.415387, + -65156.276464, + -59379.086883, + -53557.267619, + -47784.164830, + -42078.001172, + -36340.061427, + -30541.788202, + -24805.281435, + -19280.817165, + -13893.690606, + -8444.172221, + -3098.160839, + 2270.908649, + 7594.679295, + 12780.079247, + 17801.722109, + 22543.091206, + 26897.369814, + 31051.285734, + 34933.809557, + 38842.402859, + 42875.230152, + 47024.395356, + 51161.516122, + 55657.298307, + 60958.155424, + 66545.635029, + 72202.930397, + 77934.761905, + 83588.207792, + 89160.874522, + 94606.115027, + 99935.754968, + 104701.404975, + 107581.670606, + 108768.440311, + 107905.700480, + 104062.148863, + 96620.281684, + 83588.443029, + 61415.088182, + 27124.031692, + -7537.285321, + -43900.451653, + -70274.062783, + -87573.481475, + -101712.148408, + -116135.719087, + -124187.225446, + -124725.278371, + -122458.145590, + -117719.918256, + -112352.138605, + -106546.806030, + -100583.803012, + -94618.253238, + -88639.090897, + -82725.009842, + -76938.910669, + -71248.957807, + -65668.352795, + -60272.761991, + -55179.538428, + -50456.021161, + -46037.728058, + -42183.912670, + -39522.184006, + -38541.255303, + -38383.665728, + -39423.998130, + -40489.466130, + -41450.406768, + -42355.156592, + -43837.562085, + -43677.262972, + -41067.896944, + -37238.628465, + -32230.392026, + -26762.766062, + -20975.163308, + -15019.218554, + -9053.105545, + -3059.663132, + 2772.399618, + 8242.538397, + 13407.752291, + 18016.047539, + 22292.125752, + 26616.583347, + 30502.564253, + 33153.890890, + 34216.684448, + 33394.220786, + 29657.417791, + 23064.375405, + 12040.831532, + -2084.921068, + -21390.235970, + -38176.615985, + -51647.714482, + -59242.564959, + -60263.150854, + -58599.245165, + -54804.972560, + -50092.112608, + -44465.812552, + -38533.096297, + -32747.104307, + -27130.082610, + -21529.632955, + -15894.611939, + -10457.566933, + -5429.042583, + -903.757828, + 2481.947589, + 5173.789976, + 8358.768202, + 11565.584635, + 14431.147931, + 16951.619820, + 18888.807708, + 20120.884465, + 20222.141242, + 18423.168124, + 16498.668271, + 14442.624242, + 14070.038273, + 16211.370808, + 19639.815904, + 24280.360465, + 29475.380079, + 35030.793540, + 40812.325095, + 46593.082382, + 52390.906885, + 58109.310860, + 63780.896094, + 68984.456561, + 72559.442320, + 74645.487900, + 74695.219755, + 72098.143876, + 66609.929889, + 56864.971296, + 41589.295266, + 19057.032104, + -5951.329863, + -34608.796853, + -56603.801584, + -72678.838057, + -83297.070856, + -90127.593511, + -92656.040614, + -91394.995510, + -88192.056842, + -83148.833075, + -77582.587173, + -71750.440823, + -65765.369857, + -59716.101820, + -53613.430067, + -47473.832358, + -41287.031890, + -35139.919259, + -29097.671507, + -23178.836760, + -17486.807388, + -12046.775779, + -6802.483422, + -1867.556171, + 2644.380534, + 6615.829501, + 10332.557518, + 13706.737038, + 17017.991307, + 20303.136670, + 23507.386461, + 26482.194102, + 29698.585356, + 33196.305757, + 37385.914179, + 42872.996212, + 48725.617879, + 54564.488527, + 60453.841604, + 66495.146265, + 72668.620416, + 78723.644870, + 84593.136677, + 89974.936239, + 93439.798630, + 95101.207834, + 94028.126381, + 89507.925620, + 80989.846001, + 66944.274744, + 47016.422041, + 19932.783790, + -6198.433172, + -32320.379400, + -49822.852084, + -60517.553414, + -66860.548269, + -70849.714105, + -71058.721556, + -67691.947812, + -63130.703822, + -57687.607311, + -51916.952488, + -45932.054982, + -39834.909941, + -33714.535713, + -27564.443333, + -21465.186188, + -15469.326408, + -9522.358787, + -3588.742161, + 2221.802073, + 7758.244339, + 13020.269708, + 18198.562827, + 23211.338588, + 28051.699645, + 32708.577247, + 37413.795242, + 42181.401920, + 46462.499633, + 49849.582315, + 53026.578940, + 55930.600705, + 59432.642178, + 64027.356857, + 69126.843653, + 74620.328837, + 80372.056070, + 86348.152766, + 92468.907239, + 98568.998246, + 104669.511588, + 110445.790143, + 115394.348973, + 119477.553152, + 121528.574511, + 121973.674087, + 121048.017786, + 118021.473181, + 112151.993711, + 102195.999157, + 85972.731130, + 61224.719621, + 31949.279603, + -3726.022971, + -36485.298619, + -67336.469799, + -87799.366129, + -98865.713558, + -104103.651120, + -105068.402300, + -103415.820781, + -99261.356633, + -94281.850081, + -88568.701325, + -82625.711921, + -76766.776770, + -70998.803524, + -65303.404499, + -59719.198305, + -54182.230439, + -48662.904657, + -43206.731668, + -37732.701095, + -32375.478519, + -27167.508567, + -22197.211891, + -17722.869502, + -13925.135219, + -10737.893027, + -8455.327914, + -7067.008358, + -7086.991191, + -7527.693561, + -8378.025732, + -8629.383998, + -7854.586079, + -5853.040657, + -1973.225485, + 2699.850783, + 8006.098287, + 13651.734934, + 19139.318072, + 24476.645420, + 29463.480336, + 33899.078820, + 37364.528796, + 38380.214949, + 37326.585649, + 33428.470616, + 27441.000494, + 21761.126583, + 15368.408081, + 7224.234078, + -2702.217396, + -14109.682505, + -27390.915614, + -38569.562393, + -47875.155339, + -53969.121872, + -57703.473001, + -57993.198171, + -54908.391840, + -50568.410328, + -45247.622563, + -39563.224328, + -33637.786521, + -27585.345413, + -21572.074797, + -15597.363909, + -9577.429076, + -3475.770622, + 2520.378408, + 8046.881775, + 13482.345595, + ] + + beta_set = [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + ] # from new_data_set import * # Uncomment this line to use new data set # declare model name - model = ConcreteModel() + model = ConcreteModel('SIR Disease Model') # declare constants bpy = 26 # biweeks per year diff --git a/gdplib/jobshop/jobshop.py b/gdplib/jobshop/jobshop.py index 1e58752..d4d0dd6 100644 --- a/gdplib/jobshop/jobshop.py +++ b/gdplib/jobshop/jobshop.py @@ -56,7 +56,7 @@ def build_model(): Raman & Grossmann, Modelling and computational techniques for logic based integer programming, Computers and Chemical Engineering 18, 7, p.563-578, 1994. Aldo Vecchietti, LogMIP User's Manual, http://www.logmip.ceride.gov.ar/, 2007 """ - model = AbstractModel() + model = AbstractModel('Jobshop Scheduling Model') model.JOBS = Set(ordered=True, doc='Set of jobs') model.STAGES = Set(ordered=True, doc='Set of stages') diff --git a/gdplib/med_term_purchasing/med_term_purchasing.py b/gdplib/med_term_purchasing/med_term_purchasing.py index 6d12e2d..16ad6f8 100644 --- a/gdplib/med_term_purchasing/med_term_purchasing.py +++ b/gdplib/med_term_purchasing/med_term_purchasing.py @@ -46,7 +46,7 @@ def build_model(): [1] Vecchietti, A., & Grossmann, I. (2004). Computational experience with logmip solving linear and nonlinear disjunctive programming problems. Proc. of FOCAPD, 587-590. [2] Park, M., Park, S., Mele, F. D., & Grossmann, I. E. (2006). Modeling of purchase and sales contracts in supply chain optimization. Industrial and Engineering Chemistry Research, 45(14), 5013-5026. DOI: 10.1021/ie0513144 """ - model = AbstractModel() + model = AbstractModel("Medium-term Purchasing Contracts Problem") # Constants (data that was hard-coded in GAMS model) AMOUNT_UB = 1000 From 99d74e771fac9c4096d20a60c35414011683fd64 Mon Sep 17 00:00:00 2001 From: ZedongPeng Date: Tue, 7 May 2024 17:09:37 -0400 Subject: [PATCH 88/99] add github Lint workflow --- .github/workflows/black.yml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 .github/workflows/black.yml diff --git a/.github/workflows/black.yml b/.github/workflows/black.yml new file mode 100644 index 0000000..481c5d9 --- /dev/null +++ b/.github/workflows/black.yml @@ -0,0 +1,17 @@ +name: Lint + +on: [push, pull_request] + +jobs: + lint: + name: lint/style-and-typos + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Black Formatting Check + uses: psf/black@stable + with: + args: . -S -C --check --diff + - name: Spell Check + uses: crate-ci/typos@master + From faca3164c5ce4f2f01a44944c24274f14309ba7f Mon Sep 17 00:00:00 2001 From: ZedongPeng Date: Tue, 7 May 2024 17:21:03 -0400 Subject: [PATCH 89/99] rename black.yml to lint.yml --- .github/workflows/{black.yml => lint.yml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/workflows/{black.yml => lint.yml} (100%) diff --git a/.github/workflows/black.yml b/.github/workflows/lint.yml similarity index 100% rename from .github/workflows/black.yml rename to .github/workflows/lint.yml From 731ed1a2beffe00dd46fa9630b5cc1f5c1f19dd5 Mon Sep 17 00:00:00 2001 From: ZedongPeng Date: Tue, 7 May 2024 17:30:49 -0400 Subject: [PATCH 90/99] separate benchmark and mode size report code --- benchmark.py | 11 ----------- generate_model_size_report.py | 36 +++++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 11 deletions(-) create mode 100644 generate_model_size_report.py diff --git a/benchmark.py b/benchmark.py index 5820997..381ab5b 100644 --- a/benchmark.py +++ b/benchmark.py @@ -100,17 +100,6 @@ def benchmark(model, strategy, timelimit, result_dir): os.makedirs(result_dir, exist_ok=True) model = import_module("gdplib." + instance).build_model() - report = build_model_size_report(model) - report_df = pd.DataFrame(report.overall, index=[0]).T - report_df.index.name = "Component" - report_df.columns = ["Number"] - # Generate the model size report (Markdown) - report_df.to_markdown("gdplib/" + instance + "/" + "model_size_report.md") - - # Generate the model size report (JSON) - # TODO: check if we need the json file. - with open("gdplib/" + instance + "/" + "model_size_report.json", "w") as f: - json.dump(report, f) for strategy in strategy_list: benchmark(model, strategy, timelimit, result_dir) diff --git a/generate_model_size_report.py b/generate_model_size_report.py new file mode 100644 index 0000000..96ddac0 --- /dev/null +++ b/generate_model_size_report.py @@ -0,0 +1,36 @@ +from datetime import datetime +from importlib import import_module +from pyomo.util.model_size import build_model_size_report +import pandas as pd + + +if __name__ == "__main__": + instance_list = [ + # "batch_processing", + # "biofuel", + # "disease_model", + # "gdp_col", + # "hda", + "jobshop", + # "kaibel", + # "logical", + # "med_term_purchasing", + # "methanol", + # "mod_hens", + # "modprodnet", + # "stranded_gas", + # "syngas", + ] + current_time = datetime.now().strftime("%Y-%m-%d_%H-%M-%S") + timelimit = 600 + + for instance in instance_list: + print("Generating model size report: " + instance) + + model = import_module("gdplib." + instance).build_model() + report = build_model_size_report(model) + report_df = pd.DataFrame(report.overall, index=[0]).T + report_df.index.name = "Component" + report_df.columns = ["Number"] + # Generate the model size report (Markdown) + report_df.to_markdown("gdplib/" + instance + "/" + "model_size_report.md") From 6ed81c21bff47cb2b0c488c55ad1993ec0d15088 Mon Sep 17 00:00:00 2001 From: ZedongPeng Date: Tue, 7 May 2024 17:41:14 -0400 Subject: [PATCH 91/99] update benchmark code --- benchmark.py | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/benchmark.py b/benchmark.py index 381ab5b..95983d7 100644 --- a/benchmark.py +++ b/benchmark.py @@ -1,20 +1,17 @@ -from pyomo.environ import * import os import json import time import sys from datetime import datetime -from gdplib.jobshop.jobshop import build_model from importlib import import_module -from pyomo.util.model_size import build_model_size_report -import pandas as pd - -subsolver = "scip" +from pyomo.environ import * -def benchmark(model, strategy, timelimit, result_dir): +def benchmark(model, strategy, timelimit, result_dir, subsolver="scip"): """Benchmark the model using the given strategy and subsolver. + The result files include the solver output and the JSON representation of the results. + Parameters ---------- model : PyomoModel @@ -25,6 +22,10 @@ def benchmark(model, strategy, timelimit, result_dir): the time limit for the solver result_dir : string the directory to store the benchmark results + + Returns + ------- + None """ model = model.clone() stdout = sys.stdout @@ -35,7 +36,9 @@ def benchmark(model, strategy, timelimit, result_dir): with open( result_dir + "/" + strategy + "_" + subsolver + ".log", "w" ) as sys.stdout: - results = SolverFactory("scip").solve(model, tee=True, timelimit=timelimit) + results = SolverFactory(subsolver).solve( + model, tee=True, timelimit=timelimit + ) results.solver.transformation_time = ( transformation_end_time - transformation_start_time ) @@ -53,10 +56,10 @@ def benchmark(model, strategy, timelimit, result_dir): results = SolverFactory(strategy).solve( model, tee=True, - nlp_solver="scip", - mip_solver="scip", - minlp_solver="scip", - local_minlp_solver="scip", + nlp_solver=subsolver, + mip_solver=subsolver, + minlp_solver=subsolver, + local_minlp_solver=subsolver, time_limit=timelimit, ) print(results) @@ -64,6 +67,7 @@ def benchmark(model, strategy, timelimit, result_dir): sys.stdout = stdout with open(result_dir + "/" + strategy + "_" + subsolver + ".json", "w") as f: json.dump(results.json_repn(), f) + return None if __name__ == "__main__": From 450e7ef3db779808fa0ed04a88842cc07863b58c Mon Sep 17 00:00:00 2001 From: ZedongPeng Date: Wed, 8 May 2024 09:21:26 -0400 Subject: [PATCH 92/99] update benchmark code --- benchmark.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmark.py b/benchmark.py index 95983d7..1bb9df8 100644 --- a/benchmark.py +++ b/benchmark.py @@ -100,7 +100,7 @@ def benchmark(model, strategy, timelimit, result_dir, subsolver="scip"): for instance in instance_list: print("Benchmarking instance: " + instance) - result_dir = "gdplib/" + instance + "/benchmark_result/" + current_time + result_dir = "gdplib/" + instance + "/benchmark_result/" os.makedirs(result_dir, exist_ok=True) model = import_module("gdplib." + instance).build_model() From 79ba308900cdf8befa668ef5a96cc4f7d5c87cd1 Mon Sep 17 00:00:00 2001 From: ZedongPeng Date: Wed, 8 May 2024 09:23:30 -0400 Subject: [PATCH 93/99] remove example result file --- .gitignore | 2 ++ .../2024-05-06_21-01-21/gdp.bigm_scip.json | 1 - .../2024-05-06_21-01-21/gdp.hull_scip.json | 1 - .../2024-05-06_21-01-21/gdpopt.enumerate_scip.json | 1 - .../2024-05-06_21-01-21/gdpopt.gloa_scip.json | 1 - .../2024-05-06_21-01-21/gdpopt.loa_scip.json | 1 - .../2024-05-06_21-01-21/gdpopt.ric_scip.json | 1 - gdplib/jobshop/model_size_report.json | 1 - gdplib/jobshop/model_size_report.md | 10 ---------- 9 files changed, 2 insertions(+), 17 deletions(-) delete mode 100644 gdplib/jobshop/benchmark_result/2024-05-06_21-01-21/gdp.bigm_scip.json delete mode 100644 gdplib/jobshop/benchmark_result/2024-05-06_21-01-21/gdp.hull_scip.json delete mode 100644 gdplib/jobshop/benchmark_result/2024-05-06_21-01-21/gdpopt.enumerate_scip.json delete mode 100644 gdplib/jobshop/benchmark_result/2024-05-06_21-01-21/gdpopt.gloa_scip.json delete mode 100644 gdplib/jobshop/benchmark_result/2024-05-06_21-01-21/gdpopt.loa_scip.json delete mode 100644 gdplib/jobshop/benchmark_result/2024-05-06_21-01-21/gdpopt.ric_scip.json delete mode 100644 gdplib/jobshop/model_size_report.json delete mode 100644 gdplib/jobshop/model_size_report.md diff --git a/.gitignore b/.gitignore index 9b8da19..b9c4f32 100644 --- a/.gitignore +++ b/.gitignore @@ -130,3 +130,5 @@ dmypy.json # Pycharm .idea/ + +gdplib/*/benchmark_result/ diff --git a/gdplib/jobshop/benchmark_result/2024-05-06_21-01-21/gdp.bigm_scip.json b/gdplib/jobshop/benchmark_result/2024-05-06_21-01-21/gdp.bigm_scip.json deleted file mode 100644 index 04f3905..0000000 --- a/gdplib/jobshop/benchmark_result/2024-05-06_21-01-21/gdp.bigm_scip.json +++ /dev/null @@ -1 +0,0 @@ -{"Problem": [{"Lower bound": -Infinity, "Upper bound": 11.0, "Number of objectives": 1, "Number of constraints": 0, "Number of variables": 10, "Sense": "unknown"}], "Solver": [{"Status": "ok", "Message": "optimal solution found", "Termination condition": "optimal", "Id": 0, "Error rc": 0, "Time": 0.04, "Gap": 0.0, "Primal bound": 11.0, "Dual bound": 11.0, "Transformation time": 0.003754854202270508}], "Solution": [{"number of solutions": 0, "number of solutions displayed": 0}]} \ No newline at end of file diff --git a/gdplib/jobshop/benchmark_result/2024-05-06_21-01-21/gdp.hull_scip.json b/gdplib/jobshop/benchmark_result/2024-05-06_21-01-21/gdp.hull_scip.json deleted file mode 100644 index c57115b..0000000 --- a/gdplib/jobshop/benchmark_result/2024-05-06_21-01-21/gdp.hull_scip.json +++ /dev/null @@ -1 +0,0 @@ -{"Problem": [{"Lower bound": -Infinity, "Upper bound": 11.0, "Number of objectives": 1, "Number of constraints": 0, "Number of variables": 22, "Sense": "unknown"}], "Solver": [{"Status": "ok", "Message": "optimal solution found", "Termination condition": "optimal", "Id": 0, "Error rc": 0, "Time": 0.01, "Gap": 0.0, "Primal bound": 11.0, "Dual bound": 11.0, "Transformation time": 0.008002042770385742}], "Solution": [{"number of solutions": 0, "number of solutions displayed": 0}]} \ No newline at end of file diff --git a/gdplib/jobshop/benchmark_result/2024-05-06_21-01-21/gdpopt.enumerate_scip.json b/gdplib/jobshop/benchmark_result/2024-05-06_21-01-21/gdpopt.enumerate_scip.json deleted file mode 100644 index b09a6f2..0000000 --- a/gdplib/jobshop/benchmark_result/2024-05-06_21-01-21/gdpopt.enumerate_scip.json +++ /dev/null @@ -1 +0,0 @@ -{"Problem": [{"Name": "unknown", "Lower bound": 11.0, "Upper bound": 11.0, "Number of objectives": 1, "Number of constraints": 9, "Number of variables": 10, "Number of binary variables": 6, "Number of integer variables": 0, "Number of continuous variables": 4, "Number of nonzeros": null, "Sense": 1, "Number of disjunctions": 3}], "Solver": [{"Name": "GDPopt (22, 5, 13) - enumerate", "Status": "ok", "User time": 0.3502802930015605, "Wallclock time": 0.3502802930015605, "Termination condition": "optimal", "Iterations": 8, "Timing": {"main_timer_start_time": 123407.445576859, "nlp": 0.34058500100218225, "total": 0.3502802930015605}}]} \ No newline at end of file diff --git a/gdplib/jobshop/benchmark_result/2024-05-06_21-01-21/gdpopt.gloa_scip.json b/gdplib/jobshop/benchmark_result/2024-05-06_21-01-21/gdpopt.gloa_scip.json deleted file mode 100644 index 7464bb2..0000000 --- a/gdplib/jobshop/benchmark_result/2024-05-06_21-01-21/gdpopt.gloa_scip.json +++ /dev/null @@ -1 +0,0 @@ -{"Problem": [{"Name": "unknown", "Lower bound": 11.0, "Upper bound": 11.0, "Number of objectives": 1, "Number of constraints": 9, "Number of variables": 10, "Number of binary variables": 6, "Number of integer variables": 0, "Number of continuous variables": 4, "Number of nonzeros": null, "Sense": 1, "Number of disjunctions": 3}], "Solver": [{"Name": "GDPopt (22, 5, 13) - GLOA", "Status": "ok", "User time": 0.1213786309963325, "Wallclock time": 0.1213786309963325, "Termination condition": "optimal", "Iterations": 1, "Timing": {"main_timer_start_time": 123407.944400965, "mip": 0.05432544200448319, "nlp": 0.05131585600611288, "integer cut generation": 0.000397921001422219, "total": 0.1213786309963325}}]} \ No newline at end of file diff --git a/gdplib/jobshop/benchmark_result/2024-05-06_21-01-21/gdpopt.loa_scip.json b/gdplib/jobshop/benchmark_result/2024-05-06_21-01-21/gdpopt.loa_scip.json deleted file mode 100644 index 06a1841..0000000 --- a/gdplib/jobshop/benchmark_result/2024-05-06_21-01-21/gdpopt.loa_scip.json +++ /dev/null @@ -1 +0,0 @@ -{"Problem": [{"Name": "unknown", "Lower bound": 11.0, "Upper bound": 11.0, "Number of objectives": 1, "Number of constraints": 9, "Number of variables": 10, "Number of binary variables": 6, "Number of integer variables": 0, "Number of continuous variables": 4, "Number of nonzeros": null, "Sense": 1, "Number of disjunctions": 3}], "Solver": [{"Name": "GDPopt (22, 5, 13) - LOA", "Status": "ok", "User time": 0.1308980710018659, "Wallclock time": 0.1308980710018659, "Termination condition": "optimal", "Iterations": 1, "Timing": {"main_timer_start_time": 123407.805129371, "mip": 0.06084998599544633, "nlp": 0.054532527006813325, "integer cut generation": 0.0005358900089049712, "total": 0.1308980710018659}}]} \ No newline at end of file diff --git a/gdplib/jobshop/benchmark_result/2024-05-06_21-01-21/gdpopt.ric_scip.json b/gdplib/jobshop/benchmark_result/2024-05-06_21-01-21/gdpopt.ric_scip.json deleted file mode 100644 index fa7bad2..0000000 --- a/gdplib/jobshop/benchmark_result/2024-05-06_21-01-21/gdpopt.ric_scip.json +++ /dev/null @@ -1 +0,0 @@ -{"Problem": [{"Name": "unknown", "Lower bound": 11.0, "Upper bound": 11.0, "Number of objectives": 1, "Number of constraints": 9, "Number of variables": 10, "Number of binary variables": 6, "Number of integer variables": 0, "Number of continuous variables": 4, "Number of nonzeros": null, "Sense": 1, "Number of disjunctions": 3}], "Solver": [{"Name": "GDPopt (22, 5, 13) - RIC", "Status": "ok", "User time": 0.13380873500136659, "Wallclock time": 0.13380873500136659, "Termination condition": "optimal", "Iterations": 1, "Timing": {"main_timer_start_time": 123408.074596677, "mip": 0.05659815100079868, "nlp": 0.06265952499234118, "integer cut generation": 0.0004167870065430179, "total": 0.13380873500136659}}]} \ No newline at end of file diff --git a/gdplib/jobshop/model_size_report.json b/gdplib/jobshop/model_size_report.json deleted file mode 100644 index 138d2a1..0000000 --- a/gdplib/jobshop/model_size_report.json +++ /dev/null @@ -1 +0,0 @@ -{"activated": {"variables": 10, "binary_variables": 6, "integer_variables": 0, "continuous_variables": 4, "disjunctions": 3, "disjuncts": 6, "constraints": 9, "nonlinear_constraints": 0}, "overall": {"variables": 10, "binary_variables": 6, "integer_variables": 0, "continuous_variables": 4, "disjunctions": 3, "disjuncts": 6, "constraints": 9, "nonlinear_constraints": 0}, "warning": {"unassociated_disjuncts": 0}} \ No newline at end of file diff --git a/gdplib/jobshop/model_size_report.md b/gdplib/jobshop/model_size_report.md deleted file mode 100644 index 3d4d37e..0000000 --- a/gdplib/jobshop/model_size_report.md +++ /dev/null @@ -1,10 +0,0 @@ -| Component | Number | -|:----------------------|---------:| -| variables | 10 | -| binary_variables | 6 | -| integer_variables | 0 | -| continuous_variables | 4 | -| disjunctions | 3 | -| disjuncts | 6 | -| constraints | 9 | -| nonlinear_constraints | 0 | \ No newline at end of file From c275ae8998ec79ac08e886cd0be16e7be7be87e4 Mon Sep 17 00:00:00 2001 From: Albert Lee Date: Wed, 8 May 2024 23:07:08 -0400 Subject: [PATCH 94/99] Managed Comments and added the title of the code inside ConcreteModel() --- gdplib/biofuel/model.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/gdplib/biofuel/model.py b/gdplib/biofuel/model.py index 05eeb16..348ff52 100644 --- a/gdplib/biofuel/model.py +++ b/gdplib/biofuel/model.py @@ -1,4 +1,11 @@ -from __future__ import division +""" +model.py +This model describes a cost minimization for a multi-period biofuel processing network. + +References: + [1] Lara, C. L., Trespalacios, F., & Grossmann, I. E. (2018). Global optimization algorithm for capacitated multi-facility continuous location-allocation problems. Journal of Global Optimization, 71(4), 871-889. https://doi.org/10.1007/s10898-018-0621-6 + [2] Chen, Q., & Grossmann, I. E. (2019). Effective generalized disjunctive programming models for modular process synthesis. Industrial & Engineering Chemistry Research, 58(15), 5873-5886. https://doi.org/10.1021/acs.iecr.8b04600 +""" import os from math import fabs @@ -25,7 +32,8 @@ def build_model(): - """_summary_ + """ + Build a concrete model that describes a cost minimization for a multi-period biofuel processing network. Returns ------- @@ -37,7 +45,7 @@ def build_model(): [1] Lara, C. L., Trespalacios, F., & Grossmann, I. E. (2018). Global optimization algorithm for capacitated multi-facility continuous location-allocation problems. Journal of Global Optimization, 71(4), 871-889. https://doi.org/10.1007/s10898-018-0621-6 [2] Chen, Q., & Grossmann, I. E. (2019). Effective generalized disjunctive programming models for modular process synthesis. Industrial & Engineering Chemistry Research, 58(15), 5873-5886. https://doi.org/10.1021/acs.iecr.8b04600 """ - m = ConcreteModel() + m = ConcreteModel('Biofuel processing network') m.bigM = Suffix(direction=Suffix.LOCAL) m.time = RangeSet(0, 120, doc="months in 10 years") m.suppliers = RangeSet(10) # 10 suppliers From 54d3b7d94b6d52d3da9724aceda1f762c0d7e169 Mon Sep 17 00:00:00 2001 From: Albert Lee Date: Wed, 8 May 2024 23:20:47 -0400 Subject: [PATCH 95/99] Set the default value of the bigM --- gdplib/biofuel/model.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/gdplib/biofuel/model.py b/gdplib/biofuel/model.py index 348ff52..a568c6c 100644 --- a/gdplib/biofuel/model.py +++ b/gdplib/biofuel/model.py @@ -2,6 +2,12 @@ model.py This model describes a cost minimization for a multi-period biofuel processing network. +The model enforces constraints to ensure that raw material supplies do not exceed available amounts, product shipments meet market demands exactly, and production at each site matches outgoing shipments and available resources. +It also optimizes transportation costs by managing both variable and fixed costs associated with active transportation routes. +The disjunctions in the model define the operational modes for facility sites (modular, conventional, or inactive) and the activity status of supply and product routes (active or inactive). +These elements allow the model to simulate different operational scenarios and strategic decisions, optimizing the network's layout and logistics based on economic and market conditions. +The objective of the model is to optimize the network layout and production allocation to minimize total costs, which include setup and teardown of facilities, production costs, and transportation costs. + References: [1] Lara, C. L., Trespalacios, F., & Grossmann, I. E. (2018). Global optimization algorithm for capacitated multi-facility continuous location-allocation problems. Journal of Global Optimization, 71(4), 871-889. https://doi.org/10.1007/s10898-018-0621-6 [2] Chen, Q., & Grossmann, I. E. (2019). Effective generalized disjunctive programming models for modular process synthesis. Industrial & Engineering Chemistry Research, 58(15), 5873-5886. https://doi.org/10.1021/acs.iecr.8b04600 @@ -46,7 +52,7 @@ def build_model(): [2] Chen, Q., & Grossmann, I. E. (2019). Effective generalized disjunctive programming models for modular process synthesis. Industrial & Engineering Chemistry Research, 58(15), 5873-5886. https://doi.org/10.1021/acs.iecr.8b04600 """ m = ConcreteModel('Biofuel processing network') - m.bigM = Suffix(direction=Suffix.LOCAL) + m.bigM = Suffix(direction=Suffix.LOCAL, initialize=7000) m.time = RangeSet(0, 120, doc="months in 10 years") m.suppliers = RangeSet(10) # 10 suppliers m.markets = RangeSet(10) # 10 markets From 700e010043ecbf0f4826656fbf753bc02708541b Mon Sep 17 00:00:00 2001 From: Albert Lee Date: Thu, 9 May 2024 00:04:18 -0400 Subject: [PATCH 96/99] Modified the header and fix the documentation of the objective function. --- gdplib/logical/spectralog.py | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/gdplib/logical/spectralog.py b/gdplib/logical/spectralog.py index c34496b..ea0c7f3 100644 --- a/gdplib/logical/spectralog.py +++ b/gdplib/logical/spectralog.py @@ -1,16 +1,14 @@ -# coding: utf-8 +""" +spectrolog.py +IR Spectroscopy Parameter Estimation -# # [Pyomo.GDP](./index.ipynb) Logical Expression System Demo - IR Spectroscopy Parameter Estimation -# -# This is a reproduction of the IR spectroscopy parameter estimation problem found in: -# -# > Vecchietti A. & Grossmann I. E. -# > LOGMIP: A disjunctive 0-1 non-linear optimizer for process system models, -# > *Comp. & Chem Eng.* 23, p. 555-565, 1999. -# -# This code relies on the logic-v1 branch at https://github.com/qtothec/pyomo/tree/logic-v1 +This is a reproduction of the IR spectroscopy parameter estimation problem found in: -# Optimal value: 12.0893 +[1] Vecchietti, A., & Grossmann, I. E. (1997). LOGMIP: a disjunctive 0-1 nonlinear optimizer for process systems models. Computers & chemical engineering, 21, S427-S432. https://doi.org/10.1016/S0098-1354(97)87539-4 +[2] Brink, A., & Westerlund, T. (1995). The joint problem of model structure determination and parameter estimation in quantitative IR spectroscopy. Chemometrics and intelligent laboratory systems, 29(1), 29-36. https://doi.org/10.1016/0169-7439(95)00033-3 + +Optimal value: 12.0893 +""" from pyomo.environ import * from pyomo.gdp import * @@ -38,7 +36,7 @@ def build_model(): References ---------- - [1] Vecchietti, A., & Grossmann, I. E. (1997). LOGMIP: a disjunctive 0–1 nonlinear optimizer for process systems models. Computers & chemical engineering, 21, S427-S432. https://doi.org/10.1016/S0098-1354(97)87539-4 + [1] Vecchietti, A., & Grossmann, I. E. (1997). LOGMIP: a disjunctive 0-1 nonlinear optimizer for process systems models. Computers & chemical engineering, 21, S427-S432. https://doi.org/10.1016/S0098-1354(97)87539-4 [2] Brink, A., & Westerlund, T. (1995). The joint problem of model structure determination and parameter estimation in quantitative IR spectroscopy. Chemometrics and intelligent laboratory systems, 29(1), 29-36. https://doi.org/10.1016/0169-7439(95)00033-3 """ # Matrix of absorbance values across different wave numbers (rows) and spectra numbers (columns) @@ -198,7 +196,7 @@ def eq1(m, j): m.profit = Objective( expr=sum(m.val[j] for j in m.spectra_data) + 2 * sum(m.ent[k, i] for k in m.compounds for i in m.wave_number), - doc='Objective to maximize spectroscopic agreement and encourage compound presence.', + doc='Maximizes the total spectroscopic agreement across data points and promotes the activation of compound-wave number pairs.', ) # The first sum represents total spectroscopic value across data points, and the second weighted sum promotes activation of compound-wave number pairs. From 4e74928e06d27a44a66a19dbf3770d57a2a0cd53 Mon Sep 17 00:00:00 2001 From: Albert Lee Date: Thu, 9 May 2024 00:23:27 -0400 Subject: [PATCH 97/99] remove the import division and add the name of the concrete model. --- gdplib/stranded_gas/model.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/gdplib/stranded_gas/model.py b/gdplib/stranded_gas/model.py index b03a181..020a3d4 100644 --- a/gdplib/stranded_gas/model.py +++ b/gdplib/stranded_gas/model.py @@ -1,4 +1,7 @@ -from __future__ import division +""" +model.py + +""" import os @@ -26,7 +29,7 @@ def build_model(): ---------- [1] Chen, Q., & Grossmann, I. E. (2019). Economies of numbers for a modular stranded gas processing network: Modeling and optimization. In Computer Aided Chemical Engineering (Vol. 47, pp. 257-262). Elsevier. DOI: 10.1016/B978-0-444-64241-7.50100-3 """ - m = ConcreteModel() + m = ConcreteModel('Stranded gas production') m.BigM = Suffix(direction=Suffix.LOCAL) m.periods_per_year = Param(initialize=4, doc="Quarters per year") From e06863de8f854e23c79debf1d589fa84d66ff640 Mon Sep 17 00:00:00 2001 From: Albert Lee Date: Thu, 9 May 2024 00:29:57 -0400 Subject: [PATCH 98/99] Added Header. --- gdplib/stranded_gas/model.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/gdplib/stranded_gas/model.py b/gdplib/stranded_gas/model.py index 020a3d4..1989d7d 100644 --- a/gdplib/stranded_gas/model.py +++ b/gdplib/stranded_gas/model.py @@ -1,6 +1,15 @@ """ model.py +Pyomo ConcreteModel for optimizing a modular stranded gas processing network. +The model is designed to convert stranded gas into gasoline using a modular and intensified GTL process. +It incorporates the economic dynamics of module investments, gas processing, and product transportation. +Constraints manage the balance of gas supply and consumption, module availability and movement, and production capacities at potential sites. +Disjunctions delineate operational scenarios, such as the existence or absence of pipelines and the activation status of sites, enabling dynamic and flexible system configuration. +The objective function aims to maximize the network's net profit by optimizing revenue from gasoline sales while minimizing operational and capital expenditures across the network. + +References: + [1] Chen, Q., & Grossmann, I. E. (2019). Economies of numbers for a modular stranded gas processing network: Modeling and optimization. In Computer Aided Chemical Engineering (Vol. 47, pp. 257-262). Elsevier. DOI: 10.1016/B978-0-444-64241-7.50100-3 """ import os From d2daa3570487c3e672754bd6446491098c06ab82 Mon Sep 17 00:00:00 2001 From: Albert Lee Date: Thu, 9 May 2024 13:46:08 -0400 Subject: [PATCH 99/99] Add documentation on the common.py for explaining the common constraints and the disjunction of the model. --- gdplib/mod_hens/common.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/gdplib/mod_hens/common.py b/gdplib/mod_hens/common.py index 8374b87..04722cb 100644 --- a/gdplib/mod_hens/common.py +++ b/gdplib/mod_hens/common.py @@ -1,11 +1,17 @@ -"""Heat integration case study. +""" +Heat integration case study. + +This is example 1 of the Yee & Grossmann, 1990 paper "Simultaneous optimization models for heat integration--II". DOI: 10.1016/0098-1354(90)85010-8 -This is example 1 of the Yee & Grossmann, 1990 paper "Simultaneous optimization -models for heat integration--II". -DOI: 10.1016/0098-1354(90)85010-8 +This file provides common modeling elements of the heat exchanger network. +The model utilizes sets to organize hot and cold process streams, utility streams, and stages of heat exchange, with parameters defining the essential properties like temperatures and flow capacities. This structure facilitates detailed modeling of the heat transfer process across different stages and stream types. +Disjunctions are employed to model the binary decision of either installing or not installing a heat exchanger between specific stream pairs at each stage, enhancing the model's flexibility and ability to find an optimal solution that balances cost and efficiency. +The objective function aims to minimize the total cost of the heat exchanger network, which includes the costs associated with utility usage and the capital and operational expenses of the heat exchangers, ensuring economic feasibility alongside energy optimization. -This file provides common modeling elements. +Given the common.py, the model can be shown as the conventional model, or can be modified into single module type, integer or discretized formulation, and other various formulations. +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