# Farm Planning

### Farm Planning Example

This model is an example of a production problem. In production planning problems, choices must be made regarding the what resources to use to produce what products. These problems are common across a broad range of industries. In this example we’ll model and solve a multi-period production planning problem: In this case the application is to optimize the operation of a farm over 5 years. The farmer has a certain number of farmable acres and cows. The cows can be sold or used to produce milk, which can also be sold for profit. The food for the cows is grown on the farmable acres. The aim is to create an optimal operation plan for the next 5 years to maximize the total profit. This includes figuring out what is the optimal cultivation of the acres, the optimal operations related to the cows, and the operations for the whole farm. Note: you can download the model, implemented in Python, here. More information on this type of model can be found in the fifth edition of Model Building in Mathematical Programming, by H. Paul Williams.

H. Paul Williams, Model Building in Mathematical Programming, fifth edition (Page 262-263, 358-359)

## Problem Description

A farmer with a 200 acre farm wishes to create a five-year production plan. Currently, he has a herd of 120 cows comprising 20 heifers (young female cows) and 100 adult dairy cows. Each heifer requires 2/3rds of an acre and each dairy cow requires one acre to support it. A dairy cow produces 1.1 calves per year on average. Half of these calves will be bullocks that are sold shortly after birth for an average of $30 each. The remaining calves, heifers, can either be sold for$40 or raised until age two when they will be come dairy cows. For the current year, all heifers identified for sale have already been sold. The general practice is to sell all dairy cows at the age of 12 for an average of $120 each. However, each year an average of 5% of heifers and 2% of dairy cows die. At present, the farmer’s herd of 120 cows is evenly distributed with 10 cows per age from newborn to 11 years old. The milk from a dairy cow can be sold for$370 annually. The farmer can currently house up to 130 cows, but this capacity limit can be increased for $200 per additional cow. Each dairy cow requires 0.6 tons of grain and 0.7 tons of sugar beet per year. Both of these can be grown on the farm. Each acre can yield 1.5 tons of sugar beet. However, only 80 acres are suitable for growing grain, and those acres have different levels of productivity as follows: Land Group Acres Grain Production Group 1 20 acres 1.1 tons per acre Group 2 30 acres 0.9 tons per acre Group 3 20 acres 0.8 tons per acre Group 4 10 acres 0.65 tons per acre Sugar beet can be bought for$70 a ton and sold for $58 a ton. Grain can be bought for$90 a ton and sold for $75 a ton. The annual labor requirements for cows as well as grain and sugar beet production are as follows: Labor Required Each Heifer 10 hours per year Each milk-producing cow 42 hours per year Each acre put to grain 4 hours per year Each acre put to sugar beat 14 hours per year Other annual costs are as follows: Cost Each Heifer$50 per year
Each milk-producing cow $100 per year Each acre put to grain$15 per year

## Implementation with comments

First, we import the Gurobi Python Module and initalize the data structures:

from gurobipy import *
#tested with Python 3.5.2 & Gurobi 7.0.1
GrainYld = [1.1,  0.9,  0.8,  0.65]
GrainArea = [20.0, 30.0, 20.0, 10.0]
years = range(1,5+1)
land_groups = range(1,4+1)
cow_ages = range(1,12+1)
s_cow_ages = range(2,11+1)
max_accommodation = 130
grain_per_cow = 0.6
sugar_beet_per_cow = 0.7
acre_per_heifer = 2/3.0
max_acre = 200
labour_per_heifer = 10/100.0
labour_per_cow = 42/100.0
labour_per_acre_to_grain = 4/100.0
labour_per_acre_to_sb = 14/100.0
total_available_work = 5500/100.0
survivial_rate_cow = 0.98
survivial_rate_heifer = 0.95
initial_heifers = 9.5
initial_cows = 9.8
new_heifers_per_cow = 1.1/2
min_end_total = 50
max_end_total = 175
num_bulldogs_per_cow = 1.1/2
price_selling_bulldogs = 30
price_selling_heifers = 40
price_selling_12year_old_cows = 120
price_selling_milk = 370
price_selling_grain = 75
price_selling_sugar_beet = 58
price_buying_grain = 90
price_buying_sugar_beet = 70
price_extra_labour = 120
heifer_costs = 50
cow_costs = 100
acre_grain_labour_cost = 15
acre_sb_labour_cost = 10
annual_loan_repayment = 39.71
available_labour_cost = 4000
capital_expenditures = 158.85

Next, we create a model and the variables. For each year we have continous variables, which tell us how much sugar beet is grown (in tons), how much grain is bought (in tons), how much grain is sold (in tons), how much sugar beet is bought (in tons), how much sugar beet is sold (in tons), how much extra labour is recruited, how much capital outlay there is, how many heifers are sold at birth, how much profit there is, and how many cows are age 0. For each year and each land group there is a continous variable which tells us how much grain is grown on that land group. For each year and each cow_age, there is a continuous which tells us how many cows exists in the current year of that age.

model = Model('Farming')
SB = model.addVars(years, vtype=GRB.CONTINUOUS, name="SB")
GRBUY = model.addVars(years, vtype=GRB.CONTINUOUS, name="GRBUY")
GRSELL = model.addVars(years, vtype=GRB.CONTINUOUS, name="GRSELL")
SBBUY = model.addVars(years, vtype=GRB.CONTINUOUS, name="SBBUY")
SBSELL = model.addVars(years, vtype=GRB.CONTINUOUS, name="SBSELL")
EXLAB = model.addVars(years, vtype=GRB.CONTINUOUS, name="EXLAB")
EXCAP = model.addVars(years, vtype=GRB.CONTINUOUS, name="EXCAP")
HFSELL = model.addVars(years, vtype=GRB.CONTINUOUS, name="HFSELL")
NEWCOW = model.addVars(years, vtype=GRB.CONTINUOUS, name="NEWCOW")
PROF = model.addVars(years, vtype=GRB.CONTINUOUS, name="PROF")
GR = model.addVars(years, land_groups, vtype=GRB.CONTINUOUS, name="GR")
COWS = model.addVars(years, cow_ages, vtype=GRB.CONTINUOUS, name="COWS")

Next we insert the constraints: There are only a housing capacities for 130 cows per year. It is possible that there are more than 130 cows, but they come with additional costs related to renting houses, which is covered with the EXCAP variables.

# Accommodation for the cows
model.addConstrs((NEWCOW[year] +
COWS[year,1] +
quicksum(COWS[year,i] for i in s_cow_ages) -
quicksum(EXCAP[i] for i in years if i <= year)
<= max_accommodation for year in years), name="Accommodation")

Enough grain needs to be produced to feed all cows in the current year. In the model, Grain cannot be bought, unlike sugar beet.

# Grain consumption
model.addConstrs((quicksum(grain_per_cow*COWS[year, i] for i in s_cow_ages) <=
GR.sum(year, '*') +
GRBUY[year] -
GRSELL[year] for year in years),
name="Grain_consumption")

After selling, buying and producing sugar beet, there needs to be enough sugar beet in the storage to feed all the cows.

# Sugar beet consumption
model.addConstrs((quicksum(sugar_beet_per_cow*COWS[year, i] for i in s_cow_ages) <=
SB[year] + SBBUY[year] - SBSELL[year] for year in years), name="Sugar_beet_consumption")


The grain produced on each land group cannot exceed the specified production capacity of each land group.

# Grain growing capacity
model.addConstrs((GR[year, land_group] <=
GrainYld[land_group-1]*GrainArea[land_group-1] for year in years for land_group in land_groups),
name="Grain_growing_capacity")

Each cow needs a certain number of acres to support it. The amount depends on the age of the cow. There are at most 200 acres available.

# Acreage limitation
model.addConstrs((acre_per_heifer*SB[year] +
acre_per_heifer*NEWCOW[year] + acre_per_heifer*COWS[year,1] +
quicksum((1/GrainYld[land_group-1])*GR[year, land_group] for land_group in land_groups)+
quicksum(COWS[year, i] for i in s_cow_ages) <= max_acre for year in years), name="Acreage_limitation")

Each cow and each acre requires a certain amount of worker time to maintain them. The farm is currently able to provide a fixed number of worker hours in a year. Any additional work that needs to be done can be bought externally at additional cost.

# Labour
model.addConstrs((labour_per_heifer*NEWCOW[year] +
labour_per_heifer*COWS[year,1] +
quicksum(labour_per_cow*COWS[year, i] for i in s_cow_ages) +
quicksum((labour_per_acre_to_grain/GrainYld[land_group-1])*GR[year,land_group]
for land_group in land_groups) + labour_per_acre_to_sb*acre_per_heifer*SB[year]
<= total_available_work + EXLAB[year] for year in years), name="Labour")

Each year a certain percentage of the cows die, depending on their age.

# Continuity
model.addConstrs((COWS[year+1,1] == survivial_rate_heifer*NEWCOW[year]
for year in years if year < max(years)), "ContinuityA")
model.addConstrs((COWS[year+1,2] == survivial_rate_heifer*COWS[year,1]
for year in years if year < max(years)), "ContinuityB")
model.addConstrs((COWS[year+1,s_cow_age+1] == survivial_rate_cow*COWS[year,s_cow_age]
for year in years for s_cow_age in s_cow_ages if year < max(years)),
name="Continuity")


To keep the number of cows correct, cows can come into/out of the model through buying them, selling them or through birth.

# Continuity C
model.addConstrs((NEWCOW[year] -
quicksum(new_heifers_per_cow*COWS[year,i] for i in s_cow_ages) +
HFSELL[year] == 0 for year in years), name="ContinuityC")

At the end of the five years, the farmer wants at least 50 and at most 175 diary cows.

# End Total
model.addConstr(min_end_total <= quicksum(COWS[max(years), i] for i in s_cow_ages) <= max_end_total, "END")

The following constraints make sure that the variable PROF takes the value of the profit in this year. The costs for labor current run $4000. The profit is influenced by the selling of heifers, selling of 12-year-old-cows, selling of milk, selling of grain, selling of sugar beet, buying of grain, buying of sugar beet, labor costs, heifer costs, dairy cow costs, grain costs, sugar beet costs, and capital costs. # Profit Constraint model.addConstrs((quicksum(price_selling_bulldogs*num_bulldogs_per_cow*COWS[year, i] for i in s_cow_ages) + price_selling_heifers*HFSELL[year] + price_selling_12year_old_cows*COWS[year, 12] + quicksum(price_selling_milk*COWS[year, i] for i in s_cow_ages) + price_selling_grain*GRSELL[year] + price_selling_sugar_beet*SBSELL[year] - price_buying_grain*GRBUY[year] - price_buying_sugar_beet*SBBUY[year] - price_extra_labour*EXLAB[year] - heifer_costs*NEWCOW[year] - heifer_costs*COWS[year,1] - quicksum(cow_costs*COWS[year, i] for i in s_cow_ages) - quicksum((acre_grain_labour_cost/GrainYld[i-1])*GR[year, i] for i in land_groups) - (acre_sb_labour_cost*acre_per_heifer)*SB[year] - quicksum(annual_loan_repayment*EXCAP[i] for i in years if i <= year) - PROF[year] == available_labour_cost for year in years), "PROFIT") In the first year, there are 9.5 one year old cows and 9.5 two year old cows. In addition, there are 9.8 cows for each age from three to 12. Note that we are solving a LP model to make the model computationally easier to calculate. This can lead to fractional values for variables, which are in reality integers. The implementation needs to take this into account. model.addConstrs((initial_heifers == COWS[1, cow_age] for cow_age in cow_ages if cow_age < 3), name="Initial_condition_heifers") model.addConstrs((initial_cows == COWS[1, cow_age] for cow_age in cow_ages if cow_age >= 3), name="Initial_condition_cows") The total profit consists of the calculated profit minus the costs for additional labor and housing that need to be bought: # Profit model.setObjective(quicksum(PROF[t] - capital_expenditures*EXCAP[t] - annual_loan_repayment*t*EXCAP[t] for t in years), GRB.MAXIMIZE) Next, we start the optimization and Gurobi tries to find the optimal solution. model.optimize() for v in model.getVars(): if v.X != 0: print("%s %f" % (v.Varname, v.X)) Note: If you want to write your solution to a file, rather than print it to the terminal, you can use the model.write() command. An example implementation is:  model.write("farm-planning-output.sol") ## Gurobi Output and Analysis Changed value of parameter UpdateMode to 1 Prev: 0 Min: 0 Max: 1 Default: 0 Optimize a model with 116 rows, 130 columns and 733 nonzeros Coefficient statistics: Matrix range [4e-02, 3e+02] Objective range [1e+00, 4e+02] Bounds range [0e+00, 0e+00] RHS range [6e+00, 4e+03] Presolve removed 84 rows and 66 columns Presolve time: 0.00s Presolved: 32 rows, 64 columns, 252 nonzeros Iteration Objective Primal Inf. Dual Inf. Time 0 1.7920000e+33 5.000000e+30 1.792000e+03 0s 21 1.2171917e+05 0.000000e+00 0.000000e+00 0s Solved in 21 iterations and 0.00 seconds Optimal objective 1.217191729e+05  ## Analyzing the Output The optimal plan results in a total profit of$121 719 over the five-year period the model covers. The detailed plan for each year is as follows. Note: In order to create the final action plan, the optimal values shown below have been rounded so they are integers.

Year Decisions
Year One: Profit $21 906 Grow 22 tons of grain on group 1 land Grow 91 tons of sugar beet Buy 37 tons of grain Sell 23 tons of sugar beet Sell 31 heifers (leaving) 23 Year Two: Profit$21 888 Grow 22 tons of grain on group 1 land Grow 94 tons of sugar beet Buy 35 tons of grain Sell 27 tons of sugar beet Sell 41 heifers (leaving) 12
Year Three: Profit $25 816 Grow 22 tons of grain on group 1 land Grow 3 tons of grain on group 2 land Grow 98 tons of sugar beet Buy 38 tons of grain Sell 25 tons of sugar beet Sell 57 heifers (leaving none) Year Four: Profit$26 826 Grow 22 tons of grain on group 1 land Grow 115 tons of sugar beet Buy 40 tons of grain Sell 42 tons of sugar beet Sell 57 heifers (leaving none)
Year Five: Profit \$25 283 Grow 22 tons of grain on group 1 land Grow 131  tons of sugar beet Buy 33 tons of grain Sell 67 tons of sugar beet Sell 51 heifers (leaving none)

The maximum number of cows limit is only reached in year one. No investment should be made to expand housing.