I am working on a rail scheduling problem that moves product from a production plant to a storage facility to satisfy demand. I am new to PuLP so finding this difficult to understand why this isn't working, and unfortunately there is very little documentation on the subject.
There are three decision variables to monitor:
- The availability/inventory of product at each plant - note each plant can manufacture different products.
- Rail - how much to move of each product from each plant. Each train can move 8400 tons.
- The inventory of product at the storage facility.
Upon running the program, the rail decision variable works correctly i.e. the output is as expected, however the inventory at the plant and storage facility is not showing the amount removed and subsequently added by the rail.
Data as per below:
#rail capacity df (plant: no_trains_per_day)
rail_capacity_df_daily = {'ABC': 3,
'DEF':1}
# facilities_df
facilities_inventory = {'BZL': 98057,
'AFM': 8663,
'PRE': 28997}
facilities_max = {'BZL': 210000,
'AFM': 190000,
'PRE': 245000}
# plants_df
plant_df_inventory = {('ABC', 'PRE'): 196710,
('ABC', 'AFM'): 197940,
('DEF', 'BZL'): 294750,
('DEF', 'PRE'): 129180}
# Plant production daily
plants_production_daily = {('ABC', 'PRE'): 6000,
('ABC', 'AFM'): 1000,
('DEF', 'BZL'): 5000,
('DEF', 'PRE'): 4000}
Code:
# PLANNING HORIZON PARAMS
_current_date = pd.to_datetime(datetime.datetime.today().strftime('%Y%m%d'))
planning_horizon_max = datetime.datetime.today() + datetime.timedelta(30)
planning_horizon_max = pd.to_datetime(planning_horizon_max.strftime('%Y%m%d'))
# COMBINATION VARS
dates = [d.strftime('%F') for d in pd.date_range(_current_date,planning_horizon_max)]
# INVENTORY
# Initial Storage Inventory
storage_inv = dict(zip(facilities_df.index,
facilities_df['current']))
# Initial Plant Inventory
plant_current_inventory = dict(zip(plant.index, plant.inventory))
# DECISION VARIABLES
# Plant facility vars
plant_inventory_vars = pulp.LpVariable.dicts(
'Plant Inventory',
((date, plant, product) for date in dates for (plant, product) in plant_combinations),
cat='Integer',
lowBound=0)
# Storage Facility Vars
storage_facility_vars = pulp.LpVariable.dicts(
'Storage Inventory',
((d, p) for d in dates for p in products),
cat='Integer',
lowBound=0)
# Total train capacity per plant dict
train_load_limit_daily = dict(zip(rail_capacity_df.index,
rail_capacity_df.capacity_per_day))
# Decision Vars: date, plant, product
train_consignment_variables = pulp.LpVariable.dicts(
'Rail Loadings From plant',
((date, plant, product) for date in dates for (plant, product) in plant_combinations),
cat='Integer',
lowBound=0)
# OPTIMISATION
# Objective Function
model += pulp.lpSum(stockpile_max[product]
- inventory_vars[(date, product)] for (date, product) in inventory_vars), 'Minimise stockpile shortfalls'
# PLANT INVENTORY
for date in dates:
current_date = datetime.date.today().strftime('%F')
date_t_minus_one = datetime.datetime.strptime(date, '%Y-%m-%d') - datetime.timedelta(days=1)
date_t_minus_one = date_t_minus_one.strftime('%F')
for plant, product in plant_combinations:
if date == current_date:
# Set current inventory
plant_inventory_vars[(date, plant, product)] = plant_current_inventory[(plant, product)] + plant_daily_production[(plant, product)]
else:
# Get inventory from t-1
plant_inventory_vars[(date, plant, product)] = plant_inventory_vars[(f'{date_t_minus_one}', wplant, product)] + plant_daily_production[(plant, product)]
model += pulp.lpSum(plant_inventory_vars[(date, plant, product)]) - pulp.lpSum(train_consignment_variables[(date, plant, product)])
# Trains: Daily Rail Out Constraint
for date in dates:
for plant in plants:
plant_product_combination = [tup for tup in plant_combinations if tup[0] == plant]
variable_list = []
for (plant_, product_) in plant_product_combination:
variable = train_consignment_variables[(date, plant_, product_)]
variable_list.append(variable)
model += pulp.lpSum(var for var in variable_list) == train_load_limit[plant] * 8400
# STORAGE FACILITY
for date in dates:
current_date = datetime.date.today().strftime('%F')
date_t_minus_one = datetime.datetime.strptime(date, '%Y-%m-%d') - datetime.timedelta(days=1)
date_t_minus_one = date_t_minus_one.strftime('%F')
for product in products:
if date == current_date:
storage_facility_vars[(date, product)] = plant_current_inv[product]
else:
port_inventory_vars[(date, product)] = port_inventory_vars[(f'{date_t_minus_one}', product)]
model += pulp.lpSum(port_inventory_vars[(date, product)]) + pulp.lpSum(train_consignment_variables[(date, plant, product)] for plant, product in plant_combinations)
# Run solver
model.solve(solver)
pulp.LpStatus[model.status]
When I access the outputs of each decision variable:
train_consignment_vars.varValue
= output ok.
For both plant and storage facilities I get the following:
storage_facility_vars.varValue = AttributeError: 'float' object has no attribute 'value'.
If I don't call .varValue
, I simply get the dictionary values without accounting for the amount added/removed by rail.
Any help greatly appreciated, thank you.
storage_facility_vars
something likemodel + = storage_facility_vars[(date, product)] == plant_current_inv[product]
$\endgroup$