diff --git a/algorithm/example_input_coordinates.yaml b/algorithm/example_input_coordinates.yaml new file mode 100644 index 0000000..2ef815f --- /dev/null +++ b/algorithm/example_input_coordinates.yaml @@ -0,0 +1,247 @@ +# specify input format +input_format: coordinates + +moisture: .7 +demand: 200000 # dry Mg +horizon: 26 # week +num_fields: 120 +num_ssls: 60 +ssl_sizes: [2500, 5000, 10000] # wet Mg +harvest_progress: [5, 5, 6, 7, 10, 11, 12, 11, 9, 8, 6, 5, 5] + +field: + dry_yield: 21 + radius: 32 + proportion_devoted: .03 + area_ratio: [1, 10] + +price: 65 +interest_rate: .05 +insurance_rate: .008 +tax_rate: .01 + +cost: + equipment: + # purchase cost, lifetime (year), salvage value, operation cost ($/Mg), capacity (Mg/week) + loadout: [94000, 5, 28200, .37, 847] + press: [300000, 5, 120000, 0.1374, 908] + chopper: [22000 , 5, 8800, 0, 9240] + bagger: [50000, 5, 20000, .7, 2000] + module_former: [450000, 5, 217234, 1.0319, 800] + module_hauler: [375000, 8, 73530, 0.2766, 3620] + bunker_annual_own: 5600 + ssl_annual_own: .36 # $/Mg + base_infield: .58 # $/Mg/km, note this does not account for moisture + base_highway: .1 # $/Mg/km, note this does not account for moisture + transport_coef: + compressed: .8 # proportion, same for the following + whole_stalk: 1.1 + in_module: .7 + +degrade: + whole_stalk: 9 # week + chopped: 5 + in_bunker: 80 + in_bag: 100 + +configurations: + - [whole_stalk, loadout, chopper] + - [whole_stalk, loadout, chopper, bagger] + - [whole_stalk, loadout, chopper, bunker] + - [whole_stalk, loadout, chopper, module_former, module_hauler] + - [whole_stalk, loadout, chopper, press] + - [whole_stalk, loadout, chopper, press, bagger] + - [whole_stalk, loadout, chopper, press, bunker] + - [whole_stalk, loadout, chopper, press, module_former, module_hauler] + - [forage_chop, loadout] + - [forage_chop, loadout, bagger] + - [forage_chop, loadout, bunker] + - [forage_chop, loadout, module_former, module_hauler] + - [forage_chop, loadout, press] + - [forage_chop, loadout, press, bagger] + - [forage_chop, loadout, press, bunker] + - [forage_chop, loadout, press, module_former, module_hauler] + + +Coord_f: + 0: [-5.310591699035264, 14.100767580298118] + 1: [-20.079346471829062, -9.884113469244944] + 2: [-6.606881649237124, 2.4842709762148445] + 3: [-5.1715510781891325, 11.854048025392608] + 4: [-18.915056017182884, 24.199515929020507] + 5: [-5.292492648503874, 3.756149020528106] + 6: [-23.01523592990504, -19.321504698567757] + 7: [-11.940852597808458, 12.308647402836101] + 8: [-25.705802634684794, -5.049111999676661] + 9: [29.304929929632124, 2.1225782382730927] + 10: [12.280135292830295, -11.806999615611971] + 11: [11.936059371621354, 21.416043001431866] + 12: [-14.051584507878069, 18.513877020895265] + 13: [-25.39353557903091, -3.3348143247420694] + 14: [26.150112197958116, -13.208694504084512] + 15: [-13.58237833047368, -23.67817138443023] + 16: [-18.45580057599622, -15.005013800177522] + 17: [-0.5393178060583494, -28.584797112506855] + 18: [4.743526751488837, -22.60937120602815] + 19: [5.715554361810192, 12.784535041339595] + 20: [-25.450596555019146, -5.500416779547628] + 21: [12.441610094575687, -5.4925267502782305] + 22: [-28.802978627450422, 2.297369978592741] + 23: [10.482857294066484, 0.952903171731748] + 24: [28.454064383412053, 5.539522592127547] + 25: [-23.086313775951453, 19.673042477409524] + 26: [-6.548682432925851, -21.417331384516302] + 27: [27.36054914534617, -9.74298497628758] + 28: [16.051974600713955, 14.463871062428893] + 29: [24.53158983717183, 7.915021251558969] + 30: [16.06031577774958, -9.670506113418078] + 31: [-14.724614927038331, 25.336717964548278] + 32: [-4.602163848237126, 29.749763017496676] + 33: [10.460255860380677, 7.788526093383794] + 34: [-3.2056234572838065, 5.016935320776433] + 35: [-5.879244623278005, -16.830273264446227] + 36: [25.816289315984243, 4.715487147026295] + 37: [-11.094726286585846, 1.731718544486995] + 38: [24.700294355889568, -9.134735359840015] + 39: [26.14624965886715, 7.8950474106753745] + 40: [-20.970207465898973, -23.223312023751824] + 41: [27.686109634378468, 12.59636233535361] + 42: [16.248076061519768, 27.073570274974934] + 43: [13.537584552222192, -24.04665843378146] + 44: [23.041788715704833, 2.485188117865782] + 45: [3.3806066358890163, 21.889977111014765] + 46: [-24.052907832325687, -14.132244543287072] + 47: [5.488593373330424, 30.05412789245917] + 48: [3.905934032365437, -30.806573480131647] + 49: [19.240491051559445, -17.089646474174693] + 50: [19.654732519601865, -7.176918779893008] + 51: [23.266678691803435, 15.815785135179816] + 52: [3.5993749753868087, -23.26686555771616] + 53: [-17.5546023290973, 13.631294744491306] + 54: [3.8218868514651163, -31.196417269816585] + 55: [4.358429562876296, -18.98923298169661] + 56: [-15.851152347499301, 15.604854660805948] + 57: [-19.492513209003597, 5.206971345488498] + 58: [-16.649743414554486, -0.398738286801283] + 59: [7.677165976408304, 21.05477757121144] + 60: [-27.51858280196977, -0.8739129000299641] + 61: [6.805085545813142, 4.406491973534806] + 62: [5.103694031731003, -7.670964952092774] + 63: [3.2606860235453965, 15.701403578016134] + 64: [10.830905181003814, -15.045148309580199] + 65: [-27.75457059657974, -8.314611333497197] + 66: [8.301920449380127, -18.548863365450266] + 67: [-15.339833690973379, 19.504292079574107] + 68: [-19.620205912107025, 8.925496376316168] + 69: [1.5788997839189562, 27.187710105558445] + 70: [-15.149006688824898, -27.778490196222478] + 71: [15.044221650474846, 17.419393890767793] + 72: [7.4738148481060875, 28.73704452400745] + 73: [28.8112716318131, 3.6258040444900104] + 74: [26.59880638504157, 9.060237372565602] + 75: [-7.0395062949602405, -0.8965973057977763] + 76: [6.675870906878288, 3.171066976548005] + 77: [-6.727960772892487, 29.648801820205556] + 78: [-20.86683733245028, -23.91491075586321] + 79: [-23.354933885009196, 0.36237860332138894] + 80: [-20.723439643196464, -10.747931240522433] + 81: [-23.61620193210132, 19.807404296101254] + 82: [-9.936854228269219, 28.1668788693355] + 83: [5.248907516613123, 24.24524700235802] + 84: [-2.567662987724354, 2.9661962253038894] + 85: [19.11062983373052, -13.713993489014555] + 86: [-0.6237745523246261, 6.343059689336215] + 87: [-31.005870364746514, 5.9828101245847805] + 88: [-4.24471366467467, 19.671073848629852] + 89: [-11.824332601896131, 25.144877345609686] + 90: [4.982861778210271, -20.223347095842236] + 91: [18.427470964832274, 7.169995330875771] + 92: [-28.54980658727981, -5.107604479927538] + 93: [11.460405540187494, 26.790513790561654] + 94: [-7.898859856270434, 30.322146454932643] + 95: [6.7018304623393945, 21.04613171076435] + 96: [4.781536301318546, 8.196876691670425] + 97: [-13.723117971475588, 5.557333801989309] + 98: [16.001392876970236, 22.93208553145871] + 99: [16.32526006193153, 12.675663900627391] + 100: [23.32668352349439, -11.348416202448227] + 101: [10.930482610405583, -3.1440680695456535] + 102: [-7.545423869982898, -5.708073604980122] + 103: [-6.3053066579494015, -11.687427458670278] + 104: [7.802839546899293, -4.464174667439082] + 105: [-19.291527140664904, -4.6911354017259015] + 106: [-10.025840654436927, 19.04888345334826] + 107: [10.414067992016783, -14.706671230095502] + 108: [-15.848531103706257, 22.713468332633752] + 109: [1.7737373637597855, 19.338309376294276] + 110: [4.639265100262804, 14.921121618400711] + 111: [1.2167441576995728, 17.33657027212083] + 112: [4.406911405101795, -2.194567770113224] + 113: [-10.067909890989966, -27.634601701330936] + 114: [-7.8128525230016095, -26.90393102731185] + 115: [19.958956654114544, 23.99754527717748] + 116: [12.058448152700372, 4.4476424157040455] + 117: [-21.69782804380104, -2.119678543148396] + 118: [-9.90898872606624, -17.597442699898586] + 119: [5.920759601010992, -12.014730387077314] +Coord_s: + 0: [-14.456334389321462, 3.8739193943077623] + 1: [10.990706228314899, -9.444503666149835] + 2: [22.773015504251376, -19.517600895442733] + 3: [15.828531861243086, -13.465424244039845] + 4: [17.52315438362765, -4.624810922491122] + 5: [19.692698183181406, -9.373768789248615] + 6: [-18.323632791706807, 17.10620853899306] + 7: [-12.246915351638329, 14.92768433449136] + 8: [15.646281796974854, -17.83061114187513] + 9: [-18.2968232434785, -19.267332900667732] + 10: [-22.87882638298594, -7.866713650730617] + 11: [11.172097520858273, 19.18569836143272] + 12: [-26.846110263783302, -17.17105212890931] + 13: [13.52412916073515, 3.4486151072905145] + 14: [-12.510848773080305, 21.430659048435103] + 15: [-4.140418711710723, 27.10119776760161] + 16: [13.187315477499723, -1.4059962902218643] + 17: [-21.770646377978025, -19.033463985941594] + 18: [-4.40436766973626, -6.131078030390576] + 19: [-22.607905231085176, 14.676410839111895] + 20: [-19.9203157198601, 9.209321235946419] + 21: [16.275580836883115, -18.513126906667473] + 22: [6.4610718788889585, 15.931416015733532] + 23: [8.845997479701339, 6.21614738858554] + 24: [-13.089133718048188, 14.822814159456563] + 25: [28.499740159996783, -4.764071023260996] + 26: [21.457382559357818, -19.695998923382547] + 27: [-6.713800391041715, -12.794813060777187] + 28: [-8.310132636560752, 1.9646360624729269] + 29: [-0.3765589826351956, -23.541686915651958] + 30: [0.5069887964983977, -15.260828682688825] + 31: [-9.148057031501317, -25.083818963376842] + 32: [18.40331773776046, -25.17863188551673] + 33: [4.633927195817556, -29.12989858629836] + 34: [18.375442537973385, -19.86521933962902] + 35: [1.785854618120105, 15.364962762180582] + 36: [-22.40438496529562, 3.2695791479661906] + 37: [-18.13649866695812, 16.58854715629726] + 38: [14.266572387597677, -20.700861806266687] + 39: [23.055167835079374, 3.769843914300054] + 40: [-6.19388978522565, 16.559803585429542] + 41: [14.64645616123238, 1.0680821069784585] + 42: [13.245199695920704, 17.953891640111536] + 43: [-8.007939613641, 17.300641621117286] + 44: [16.039956427211223, 7.2455175388326225] + 45: [-6.280580866058408, 12.62771323291782] + 46: [25.370662557238205, -16.683794723999704] + 47: [-24.270900204209298, -17.901824781201434] + 48: [-12.66580919743371, 24.51382454808539] + 49: [2.7626515352380494, -13.650454425188443] + 50: [-23.145299853251423, -13.430754361309333] + 51: [7.287749743440166, -11.255134086563416] + 52: [-2.7289483717760987, -3.5765051509366756] + 53: [21.000663086816772, -4.71371812888124] + 54: [-9.875275444767851, 11.198182707782735] + 55: [-17.82514843305416, -2.096267268700558] + 56: [-11.854996011262948, 8.118758538421417] + 57: [24.151070380468006, -3.347903990679775] + 58: [18.20527480268308, -2.754195164060775] + 59: [9.998677229896408, -23.562177598239863] diff --git a/algorithm/s_bfl.py b/algorithm/s_bfl.py index 87857a3..b69eb19 100644 --- a/algorithm/s_bfl.py +++ b/algorithm/s_bfl.py @@ -29,36 +29,48 @@ def input(self, input_data, sysnum, t_lim = 10, jit=False, **kwargs): self.jit = jit self.sysnum = sysnum self.params = create_data(input_data, self.sysnum, **kwargs) - (*_, self.a, self.c_op, self.c_op_jit, self.c_pen, self.cfs, self.cs, self.cs_jit, - self.csk, self.d, self.hf, self.hs, self.U, self.UE, self.UE_jit) = self.params.values() + (*_, self.harvested, self.operating_cost, self.operating_cost_jit, self.c_pen, self.farm_ssl_trans_cost, self.ssl_refinery_trans_cost, self.ssl_refinery_jit_trans_cost, + self.fixed_cost_ssls, self.demand, self.farm_holding_cost, self.ssl_holding_cost, self.upperbound_inventory, self.upperbound_equip_proc_rate, self.upperbound_equip_proc_rate_jit) = self.params.values() def solve(self): - T = list(range(1, len(self.d))) - F = list(range(len(self.cfs))) - S = list(range(len(self.csk))) - K = list(range(len(self.U))) - a = np.array(self.a) - M = a.sum(axis=0) - c_zfs = (np.array(self.cfs) + self.c_op).tolist() - c_z_jit = (np.array(self.cs_jit)[None] + np.array(self.cfs) + self.c_op_jit).tolist() + # T, F, S, K : Set of time periods, farms, potential SSL sites, and available types of SSLs, respectively + # t, f, s, k : element of T, F, S, K + T = list(range(1, len(self.demand))) + F = list(range(len(self.farm_ssl_trans_cost))) + S = list(range(len(self.fixed_cost_ssls))) + K = list(range(len(self.upperbound_inventory))) + + harvested = np.array(self.harvested) + M = harvested.sum(axis=0) + farm_ssl_cost_per_period = (np.array(self.farm_ssl_trans_cost) + self.operating_cost).tolist() + jit_trans_costs = (np.array(self.ssl_refinery_jit_trans_cost)[None] + np.array(self.farm_ssl_trans_cost) + self.operating_cost_jit).tolist() logger.info("Parameters created. Begin building model.") m = Model('ms1m_base') + # ++++++ Linear Programming Notation ++++++ # ====== Create vars and objectives ====== - w = m.addVars(S, K, obj=self.csk, vtype='B', name='w') + + # w[s][k] = 1 => is an ssl of type k is built at site s; otherwise 0 + w = m.addVars(S, K, obj=self.fixed_cost_ssls, vtype='B', name='w') + + # y[f][s] = 1 => if farm f supplies ssl s; 0 otherwise y = m.addVars(F, S, vtype='B', name='y') - zfs = m.addVars(T, F, S, obj=c_zfs * len(T), name='zfs') - zs = m.addVars(T, S, obj=self.cs * len(T), name='zs') - Is = m.addVars([0] + T, S, obj=self.hs, name='Is') - If = m.addVars([0] + T, F, obj=self.hf, name='If') + + shipped_farm_ssl = m.addVars(T, F, S, obj=farm_ssl_cost_per_period * len(T), name='shipped_farm_ssl') + shipped_ssl_refinery = m.addVars(T, S, obj=self.ssl_refinery_trans_cost * len(T), name='shipped_ssl_refinery') + + # Note: inventory levels taken at the end of period t + inventory_level_ssl = m.addVars([0] + T, S, obj=self.ssl_holding_cost, name='inventory_level_ssl') + inventory_level_farm = m.addVars([0] + T, F, obj=self.farm_holding_cost, name='inventory_level_farm') + if self.jit: - z_jit = m.addVars(T, F, S, obj=c_z_jit * len(T), name='z_jit') + z_jit = m.addVars(T, F, S, obj=jit_trans_costs * len(T), name='z_jit') penalty = m.addVars(T, obj=self.c_pen, name='penalty') - m.setAttr('UB', Is.select(0, '*'), [0] * len(S)) - m.setAttr('UB', If.select(0, '*'), [0] * len(F)) + m.setAttr('UB', inventory_level_ssl.select(0, '*'), [0] * len(S)) + m.setAttr('UB', inventory_level_farm.select(0, '*'), [0] * len(F)) m.ModelSense = GRB.MINIMIZE @@ -66,42 +78,42 @@ def solve(self): m.addConstrs((w.sum(s, '*') <= 1 for s in S), name='c1') m.addConstrs((y[f, s] <= w.sum(s, '*') for f in F for s in S), name='c2') m.addConstrs((y.sum(f, '*') == 1 for f in F), name='c3') - m.addConstrs((Is[t, s] == Is[t - 1, s] - zs[t, s] + zfs.sum(t, '*', s) + m.addConstrs((inventory_level_ssl[t, s] == inventory_level_ssl[t - 1, s] - shipped_ssl_refinery[t, s] + shipped_farm_ssl.sum(t, '*', s) for t in T for s in S), name='c4') - m.addConstrs((Is[t, s] <= quicksum([self.U[k] * w[s, k] for k in K]) for t in T + m.addConstrs((inventory_level_ssl[t, s] <= quicksum([self.upperbound_inventory[k] * w[s, k] for k in K]) for t in T for s in S), name='c7') - m.addConstrs((zfs.sum(t, '*', s) <= quicksum([self.UE[k] * w[s, k] for k in K]) + m.addConstrs((shipped_farm_ssl.sum(t, '*', s) <= quicksum([self.upperbound_equip_proc_rate[k] * w[s, k] for k in K]) for t in T for s in S), name='c9a') if not self.jit: - m.addConstrs((If[t, f] == If[t - 1, f] + a[t, f] - zfs.sum(t, f, '*') + m.addConstrs((inventory_level_farm[t, f] == inventory_level_farm[t - 1, f] + harvested[t, f] - shipped_farm_ssl.sum(t, f, '*') for t in T for f in F), name='c5') - m.addConstrs((zs.sum(t, '*') + penalty[t] == self.d[t] for t in T), + m.addConstrs((shipped_ssl_refinery.sum(t, '*') + penalty[t] == self.demand[t] for t in T), name='c6') m.addConstrs( - (zfs.sum('*', f, s) <= M[f] * y[f, s] for f in F for s in S), + (shipped_farm_ssl.sum('*', f, s) <= M[f] * y[f, s] for f in F for s in S), name='c8') m.addConstrs( - (zfs.sum(t, '*', s) <= quicksum([self.UE_jit[k] * w[s, k] for k in K]) + (shipped_farm_ssl.sum(t, '*', s) <= quicksum([self.upperbound_equip_proc_rate_jit[k] * w[s, k] for k in K]) for t in T for s in S), name='c9b') else: - m.addConstrs((If[t, f] == If[t - 1, f] + a[t, f] - zfs.sum(t, f, '*') - + m.addConstrs((inventory_level_farm[t, f] == inventory_level_farm[t - 1, f] + harvested[t, f] - shipped_farm_ssl.sum(t, f, '*') - z_jit.sum(t, f, '*') for t in T for f in F), name='c5') m.addConstrs( - (z_jit.sum(t, '*', '*') + zs.sum(t, '*') + penalty[t] == self.d[t] + (z_jit.sum(t, '*', '*') + shipped_ssl_refinery.sum(t, '*') + penalty[t] == self.demand[t] for t in T), name='c6') m.addConstrs( - (z_jit.sum('*', f, s) + zfs.sum('*', f, s) <= M[f] * y[f, s] + (z_jit.sum('*', f, s) + shipped_farm_ssl.sum('*', f, s) <= M[f] * y[f, s] for f in F for s in S), name='c8') - m.addConstrs((z_jit.sum(t, '*', s) + zfs.sum(t, '*', s) <= quicksum( - [self.UE_jit[k] * w[s, k] for k in K]) for t in T for s in S), + m.addConstrs((z_jit.sum(t, '*', s) + shipped_farm_ssl.sum(t, '*', s) <= quicksum( + [self.upperbound_equip_proc_rate_jit[k] * w[s, k] for k in K]) for t in T for s in S), name='c9b') logger.info("Model created. Begin solving.") @@ -143,60 +155,68 @@ def solve(self): m.write(f'warm_starts/base_sys{self.sysnum}.mst') cost_total_lb = m.objBound + # Total cost cost_total = m.objVal gap = (cost_total - cost_total_lb) / cost_total - cost_loc = w.prod({(s, k): self.csk[s][k] for s in S for k in K}).getValue() + # what does cost locations mean? + cost_loc = w.prod({(s, k): self.fixed_cost_ssls[s][k] for s in S for k in K}).getValue() + + cost_op = shipped_farm_ssl.sum().getValue() * self.operating_cost + cfs_dict = {(t, f, s): self.farm_ssl_trans_cost[f][s] for t, f, s in setprod(T, F, S)} - cost_op = zfs.sum().getValue() * self.c_op - cfs_dict = {(t, f, s): self.cfs[f][s] for t, f, s in setprod(T, F, S)} - cost_tran_fs = zfs.prod(cfs_dict).getValue() - cost_tran_sb = zs.prod({(t, s): self.cs[s] + # cost of transporting from f to s in time t + cost_tran_fs = shipped_farm_ssl.prod(cfs_dict).getValue() + cost_tran_sr = shipped_ssl_refinery.prod({(t, s): self.ssl_refinery_trans_cost[s] for t in T for s in S}).getValue() if self.jit: - cost_op += z_jit.sum().getValue() * self.c_op_jit + cost_op += z_jit.sum().getValue() * self.operating_cost_jit cost_tran_fs += z_jit.prod(cfs_dict).getValue() - cost_tran_sb += z_jit.prod({(t, f, s): self.cs_jit[s] + cost_tran_sr += z_jit.prod({(t, f, s): self.ssl_refinery_jit_trans_cost[s] for t in T for f in F for s in S}).getValue() - cost_inv_S = Is.sum().getValue() * self.hs - cost_inv_F = If.sum().getValue() * self.hf + cost_inv_S = inventory_level_ssl.sum().getValue() * self.ssl_holding_cost + cost_inv_F = inventory_level_farm.sum().getValue() * self.farm_holding_cost K_cnt = dict(Counter(k for s in S for k in K if w[s, k].x > 0.5)) jit_amount = z_jit.sum().getValue() if self.jit else np.nan + # maybe a function for getting all the variables that are active and convert to lat, lng? + # getActiveRoutes() + # Binary encoding of open ssls solution = [[v.VarName, v.X] for v in m.getVars() if v.X > 1e-6] summary = dict() summary['others'] = { 'status': status[m.status], 'CPU_time': m.runtime, 'gap': gap, - 'K': len(K), - 'T': len(T), - 'F': len(F), - 'S': len(S), - 'num_SSL': w.sum().getValue(), + 'available_types_ssl': len(K), + 'num_weeks_horizon': len(T), + 'num_farms': len(F), + # I'm not super sure about the ssl one + 'num_ssls_considered': len(S), + 'num_ssls_used': w.sum().getValue(), 'SSL_type_cnt': K_cnt, } summary['cost'] = { - 'lb': cost_total_lb, - 'ub': cost_total, + 'total_lb': cost_total_lb, + 'total_ub': cost_total, 'penalty': penalty.sum().getValue(), 'operation': cost_op, 'loc_own': cost_loc, - 'tran_fs': cost_tran_fs, - 'tran_sb': cost_tran_sb, - 'inv_f': cost_inv_F, - 'inv_s': cost_inv_S, + 'tran_farms_ssl': cost_tran_fs, + 'tran_ssl_refinery': cost_tran_sr, + 'farm_inventory': cost_inv_F, + 'ssl_inventory': cost_inv_S, } - a_sum = float(a.sum()) + harvested_sum = float(harvested.sum()) summary['per_dry_Mg'] = { - k: v / a_sum + k: v / harvested_sum for k, v in summary['cost'].items() } summary['trans_amount'] = { - 'base_fs': zfs.sum().getValue(), - 'base_sb': zs.sum().getValue(), + 'base_fs': shipped_farm_ssl.sum().getValue(), + 'base_sb': shipped_ssl_refinery.sum().getValue(), 'jit': jit_amount } diff --git a/algorithm/s_bfl_utility.py b/algorithm/s_bfl_utility.py index 8236abc..5698224 100644 --- a/algorithm/s_bfl_utility.py +++ b/algorithm/s_bfl_utility.py @@ -16,7 +16,8 @@ from matplotlib import pyplot as plt -def create_data(raw_data, sysnum, seed=None, out_file=None, plot_coords=False): +def create_data(raw_data, sysnum, mode="paper", seed=None, out_file=None, + plot_coords=False): """ Create data for the Sorghum-BFL model. @@ -26,6 +27,9 @@ def create_data(raw_data, sysnum, seed=None, out_file=None, plot_coords=False): can be a *.yaml or *.json file name (str) or a dict object. sysnum : int system number. + mode : str + type of information available input file. + options: paper, coordinates. seed : int, optional random seed. out_file : str, optional @@ -33,6 +37,21 @@ def create_data(raw_data, sysnum, seed=None, out_file=None, plot_coords=False): plot_coords : bool, optional plot all the coordinates. + Variable name replacements + T = num_weeks_horizon + F = num_farms + S = num_ssls + a = harvested + cfs = farm_ssl_trans_cost + cs = ssl_refinery_trans_cost + csk = fixed_cost_ssls + ck = cost_ssls + hf = farm_holding_cost + hs = ssl_holding_cost + d = demand + u = upperbound_inventory + ue = upperbound_equip_proc_rate + Returns ------- all_data : dict @@ -50,22 +69,29 @@ def create_data(raw_data, sysnum, seed=None, out_file=None, plot_coords=False): else: raise TypeError('raw_data must be str (filename) or dict.') - T = raw['horizon'] - F = raw['num_fields'] - S = raw['num_ssls'] + num_weeks_horizon = raw['horizon'] + num_farms = raw['num_fields'] + num_ssls = raw['num_ssls'] # ========== coordinates, harvest, demand data ========== radius = raw['field']['radius'] np.random.seed(seed=seed) - sites = np.random.uniform(-radius, radius, size=(2 * (F + S), 2)) - sits_in = sites[np.sum(sites * sites, axis=1) <= radius**2] - coord_f = sits_in[:F] - coord_s = sits_in[-S:] + if mode == "paper": + # generates random ssl locations + sites = np.random.uniform(-radius, radius, size=(2 * (num_farms + num_ssls), 2)) + sits_in = sites[np.sum(sites * sites, axis=1) <= radius**2] + coord_farms = sits_in[:num_farms] + coord_ssls = sits_in[-num_ssls:] + elif mode == "coordinates": + coord_farms = np.array(list(raw["Coord_f"].values())) + coord_ssls = np.array(list(raw["Coord_s"].values())) + assert len(coord_farms) == num_farms + assert len(coord_ssls) == num_ssls if plot_coords: l1, = plt.plot(0, 'g^', markersize=7) - l2, = plt.plot(*coord_s.T, 'ro', markersize=2) - l3, = plt.plot(*coord_f.T, 'bx', markersize=3) + l2, = plt.plot(*coord_ssls.T, 'ro', markersize=2) + l3, = plt.plot(*coord_farms.T, 'bx', markersize=3) plt.legend([l3, l2, l1], ['Farm', 'SSL', 'Bio-refinery']) circles = [ plt.Circle((0, 0), @@ -84,19 +110,22 @@ def create_data(raw_data, sysnum, seed=None, out_file=None, plot_coords=False): proportion_devoted = raw['field']['proportion_devoted'] # 1 sqkm = 100 ha total_supply = 100 * pi * radius**2 * proportion_devoted * dry_yield - a_weight = np.zeros(T + 1, dtype=int) + # what is the a_weight? Is it just a way of configuring the matrix? + a_weight = np.zeros(num_weeks_horizon + 1, dtype=int) a_weight[1:len(harvest_progress) + 1] = harvest_progress weekly_supply = a_weight / a_weight.sum() * total_supply - field_weight = np.random.uniform(1, 10, size=F) + field_weight = np.random.uniform(1, 10, size=num_farms) field_weight = field_weight / field_weight.sum() - a = weekly_supply[:, None] * field_weight[None] - a = a.round(0).astype(int) + + #if a is the amount harvested in farm f at time t why is it a single number here? + harvested = weekly_supply[:, None] * field_weight[None] + harvested = harvested.round(0).astype(int) total_demand = raw['demand'] - d = [0] + [int(total_demand / T)] * T + demand = [0] + [int(total_demand / num_weeks_horizon)] * num_weeks_horizon # =================== system dependet data ========================== - tran_coef = raw['cost']['transport_coef'] + tran_coef = raw['cost']['transport_coef'] #raw is the input file equipments = raw['cost']['equipment'] config = raw['configurations'][sysnum] dry_part = 1 - raw['moisture'] @@ -106,37 +135,45 @@ def create_data(raw_data, sysnum, seed=None, out_file=None, plot_coords=False): K = [(i, *j) for i in ssl_sizes for j in equip_arr] if 'whole_stalk' in config: - hf = raw['price'] / raw['degrade']['whole_stalk'] + # farm_unit_holding_cost + farm_holding_cost = raw['price'] / raw['degrade']['whole_stalk'] else: - hf = raw['price'] / raw['degrade']['chopped'] + farm_holding_cost = raw['price'] / raw['degrade']['chopped'] if 'bunker' in config: - hs = raw['price'] / raw['degrade']['in_bunker'] + # ssl_unit_holding_cost + ssl_holding_cost = raw['price'] / raw['degrade']['in_bunker'] elif 'bagger' in config or 'module_former' in config: - hs = raw['price'] / raw['degrade']['in_bag'] + ssl_holding_cost = raw['price'] / raw['degrade']['in_bag'] else: - hs = raw['price'] / raw['degrade']['chopped'] + ssl_holding_cost = raw['price'] / raw['degrade']['chopped'] - c_op = 0 + # c_op = operating cost? + operating_cost = 0 for e in config[1:]: if e == 'bunker': continue - c_op += equipments[e][3] - c_op /= dry_part - c_op_jit = equipments['loadout'][3] - c_op_jit += equipments['chopper'][3] if 'chopper' in config else 0 - c_op_jit /= dry_part - - cfs_rate = raw['cost']['base_infield'] / dry_part - cfs_rate *= tran_coef['whole_stalk'] if 'whole_stalk' in config else 1 - cfs = cdist(coord_f, coord_s) * cfs_rate - - cs_jit_rate = cs_rate = raw['cost']['base_highway'] / dry_part - cs_rate *= tran_coef['compressed'] if 'press' in config else 1 - cs_rate *= tran_coef['in_module'] if 'module_former' in config else 1 - cs = np.linalg.norm(coord_s, axis=1) * cs_rate - cs_jit = np.linalg.norm(coord_s, axis=1) * cs_jit_rate + operating_cost += equipments[e][3] + operating_cost /= dry_part + operating_cost_jit = equipments['loadout'][3] + operating_cost_jit += equipments['chopper'][3] if 'chopper' in config else 0 + operating_cost_jit /= dry_part + + # cost per Mg to transport from farm f to ssl s + farm_ssl_trans_cost_rate = raw['cost']['base_infield'] / dry_part + farm_ssl_trans_cost_rate *= tran_coef['whole_stalk'] if 'whole_stalk' in config else 1 + farm_ssl_trans_cost = cdist(coord_farms, coord_ssls) * farm_ssl_trans_cost_rate + # Cost per Mg to send from ssl to refinery + ssl_refinery_trans_cost_jit_rate = ssl_refinery_trans_cost_rate = raw['cost']['base_highway'] / dry_part + ssl_refinery_trans_cost_rate *= tran_coef['compressed'] if 'press' in config else 1 + ssl_refinery_trans_cost_rate *= tran_coef['in_module'] if 'module_former' in config else 1 + + # use geolocation here, backward compatable (works with all ways it used to run) + ssl_refinery_trans_cost = np.linalg.norm(coord_ssls, axis=1) * ssl_refinery_trans_cost_rate + ssl_refinery_jit_trans_cost = np.linalg.norm(coord_ssls, axis=1) * ssl_refinery_trans_cost_jit_rate + + # UE upperbound equipment processing rate UE, UE_jit = dict(), dict() for v in equip_arr: caps_jit = [ @@ -152,47 +189,49 @@ def create_data(raw_data, sysnum, seed=None, out_file=None, plot_coords=False): UE[v] = int(min(caps_other) * dry_part) UE_jit[v] = int(min(caps_jit) * dry_part) - own_cost = {'bunker': raw['cost']['bunker_annual_own'] / 52 * T} + # what is own_cost? owner cost? owner of what? + own_cost = {'bunker': raw['cost']['bunker_annual_own'] / 52 * num_weeks_horizon} for k, v in equipments.items(): depreciation = (v[0] - v[2]) / v[1] interest = v[0] * raw['interest_rate'] insurance_tax = (v[0] + v[2]) / 2 * ( raw['insurance_rate'] + raw['tax_rate']) - own_cost[k] = (depreciation + interest + insurance_tax) / 52 * T + own_cost[k] = (depreciation + interest + insurance_tax) / 52 * num_weeks_horizon - ck = dict() + # cost per type of ssl + cost_ssls = dict() for k in K: - ssl_cost = k[0] * raw['cost']['ssl_annual_own'] / 52 * T + ssl_cost = k[0] * raw['cost']['ssl_annual_own'] / 52 * num_weeks_horizon equip_cost = [k[i + 1] * own_cost[e] for i, e in enumerate(config[1:])] - ck[k] = int(ssl_cost + sum(equip_cost)) + cost_ssls[k] = int(ssl_cost + sum(equip_cost)) # =================== output data ========================== all_data = { 'Configuration': config, - 'Coord_f': {i: v.tolist() - for i, v in enumerate(coord_f)}, - 'Coord_s': {i: v.tolist() - for i, v in enumerate(coord_s)}, + 'Coord_farms': {i: v.tolist() + for i, v in enumerate(coord_farms)}, + 'Coord_ssls': {i: v.tolist() + for i, v in enumerate(coord_ssls)}, 'K': {i: list(k) for i, k in enumerate(K)}, 'Seed': seed, 'Sysnum': sysnum, - 'a': a.tolist(), - 'c_op': c_op, - 'c_op_jit': c_op_jit, + 'harvested': harvested.tolist(), + 'operating_cost': operating_cost, + 'operating_cost_jit': operating_cost_jit, + # what is this the "price" of? 'c_pen': raw['price'] * 3, - 'cfs': np.round(cfs, 2).tolist(), - 'cs': np.round(cs, 2).tolist(), - 'cs_jit': np.round(cs_jit, 2).tolist(), - 'csk': [list(ck.values())] * S, - 'd': d, - 'hf': hf, - 'hs': hs, - 'u': np.repeat(ssl_sizes, len(equip_arr)).tolist(), - 'ue': list(UE.values()) * len(ssl_sizes), - 'ue_jit': list(UE_jit.values()) * len(ssl_sizes) + 'farm_ssl_trans_cost': np.round(farm_ssl_trans_cost, 2).tolist(), + 'ssl_refinery_trans_cost': np.round(ssl_refinery_trans_cost, 2).tolist(), + 'ssl_refinery_jit_trans_cost': np.round(ssl_refinery_jit_trans_cost, 2).tolist(), + 'fixed_cost_ssls': [list(cost_ssls.values())] * num_ssls, + 'demand': demand, + 'farm_holding_cost': farm_holding_cost, + 'ssl_holding_cost': ssl_holding_cost, + 'upperbound_inventory': np.repeat(ssl_sizes, len(equip_arr)).tolist(), + 'upperbound_equip_proc_rate': list(UE.values()) * len(ssl_sizes), + 'upperbound_equip_proc_rate_jit': list(UE_jit.values()) * len(ssl_sizes) } - # %% if out_file is not None: with open(out_file, 'w') as f: if out_file.split('.')[-1] == 'json': @@ -225,9 +264,11 @@ def cli_s_bfl(): parser.add_argument( '-t', '--t_lim', + # not sure about this metaVar metavar='T', type=float, default=60, + # Looks like T was used for another purpose here help='set computation time limit T in seconds (default: 60)') parser.add_argument( '--seed', diff --git a/myapp.py b/myapp.py index d2aefd0..c36b495 100644 --- a/myapp.py +++ b/myapp.py @@ -11,9 +11,9 @@ from flask import Flask, render_template, request, send_from_directory, jsonify, send_file from algorithm.tsp import tsp from algorithm.s_bfl import s_bfl -from email_credentials import credentials -from flask_mail import Mail -from flask_mail import Message +# from email_credentials import credentials +# from flask_mail import Mail +# from flask_mail import Message from flask_cors import CORS class CustomFlask(Flask): @@ -27,20 +27,20 @@ class CustomFlask(Flask): app = CustomFlask(__name__,template_folder='') # This replaces your existing "app = Flask(__name__)" CORS(app) -c = credentials() -c.setPassword() +# c = credentials() +# c.setPassword() -app.config.update( - DEBUG = True, - MAIL_SERVER = 'smtp.gmail.com', - MAIL_PORT = 465, - MAIL_USE_SSL = True, - MAIL_USERNAME = 'robert.b.shelton.42@gmail.com', - MAIL_PASSWORD = c.password, -) +# app.config.update( +# DEBUG = True, +# MAIL_SERVER = 'smtp.gmail.com', +# MAIL_PORT = 465, +# MAIL_USE_SSL = True, +# MAIL_USERNAME = 'robert.b.shelton.42@gmail.com', +# MAIL_PASSWORD = c.password, +# ) -mail = Mail(app) +# mail = Mail(app) @app.route('/s-bfls/', methods=['POST']) def Sbfl(): @@ -50,13 +50,13 @@ def Sbfl(): my_s_bfl.solve() print(my_s_bfl.optimization_result) response = jsonify(my_s_bfl.optimization_result) - response.headers.add('Access-Control-Allow-Origin', '*') + # response.headers.add('Access-Control-Allow-Origin', '*') - msg = Message('S-BFLS', - sender="robert.b.shelton.42@gmail.com", - recipients=["robes98@vt.edu"]) - msg.body = "Thanks for using SBFLS! Attached are our results." - mail.send(msg) + # msg = Message('S-BFLS', + # sender="robert.b.shelton.42@gmail.com", + # recipients=["robes98@vt.edu"]) + # msg.body = "Thanks for using SBFLS! Attached are our results." + # mail.send(msg) return response @app.errorhandler(404)