# Refinery

## Objective and Prerequisites

This model is an example of a production planning problem. In such problems, decisions must be made regarding which products to produce, and which resources to use to produce those products. These problems are common across a broad range of manufacturing and mining industries. In this example, we will build a model to optimize the output of a refinery. 

The company can buy a restricted amount of two different kinds of crude oils, which can be processed to gasoline (motor fuel), jet fuel, or oil. The aim is to create an optimal production plan that maximizes the total profit, while considering production capacity and other restrictions.

More information on this type of model can be found in example # 6 of the fifth edition of Modeling Building in Mathematical Programming by H. P. Williams on pages 258 and 306-310.

This modeling example is at the intermediate level, where we assume that you know Python and are familiar with the Gurobi Python API. In addition, you should have some knowledge about building mathematical optimization models.

**Note:** You can download the repository containing this and other examples by clicking [here](https://github.com/Gurobi/modeling-examples/archive/master.zip). In order to run this Jupyter Notebook properly, you must have a Gurobi license. If you do not have one, you can request an [evaluation license](https://www.gurobi.com/downloads/request-an-evaluation-license/?utm_source=Github&utm_medium=website_JupyterME&utm_campaign=CommercialDataScience) as a *commercial user*, or download a [free license](https://www.gurobi.com/academia/academic-program-and-licenses/?utm_source=Github&utm_medium=website_JupyterME&utm_campaign=AcademicDataScience) as an *academic user*.

---
## Problem Description

An oil refinery purchases two types of crude oil (Crude 1 and Crude 2) and refines them through a four-step process (distillation, reforming, cracking, and blending) to produce gas and fuels that are sold.

### Step One: Distillation

The distillation process separates each crude oil into six fractions according to their boiling points. These fractions are: light naphtha, medium naphtha, heavy naphtha, light oil, heavy oil, and residuum. The octane numbers for the light, medium and heavy naphthas are, respectively, 90, 80, and 70.

One barrel of crude separates into following fractions:

| <i></i> | Light Naphta | Medium Naphta | Heavy Naphta | Light Oil | Heavy Oil | Residuum |
| --- | --- | --- | --- | --- | --- | --- |
| Crude 1 | 0.1 | 0.2 | 0.2 | 0.12 | 0.2 | 0.13 |
| Crude 2 | 0.15 | 0.25 | 0.18 | 0.08 | 0.19 | 0.12 |

There is a small amount of waste in distillation (5% and 3% for Crude 1 and 2, respectively).

### Step Two: Reforming

After distillation, the resulting naphthas can be blended into different grades of gas, or they can go through a process called reforming. The output of the reforming process is a product known as reformed gasoline with an octane number of 115.

The yields of reformed gasoline from each barrel of the different naphthas are given as follows:

- One barrel of light naphtha yields 0.6 barrels of reformed gasoline.
- One barrel of medium naphtha yields 0.52 barrels of reformed gasoline.
- One barrel of heavy naphtha yields 0.45 barrels of reformed gasoline.

### Step Three: Cracking

Light and heavy oils can be blended into jet fuel or be put through a process known as catalytic cracking. The catalytic cracker produces cracked oil and cracked gasoline.

Cracked gasoline has an octane number of 105 with the following yields:

- One barrel of light oil yields 0.68 barrels of cracked oil and 0.28 barrels of cracked gasoline.
- One barrel of heavy oil yields 0.75 barrels of cracked oil and 0.2 barrels of cracked gasoline.

Cracked oil is used for blending fuel oil and jet fuel; cracked gasoline is used for blending gasoline. Residuum can be used for either producing lube-oil or blending into jet fuel and fuel oil:

- One barrel of residuum yields 0.5 barrels of lube-oil.

### Step Four: Blending

#### Gasoline

There are two kinds of gasoline — regular and premium — made by blending naphtha, reformed gasoline, and cracked gasoline. The only requirement is that regular gasoline must have an octane of at least 84 and premium gasoline must have an octane number of at least 94. It is assumed that octane numbers blend linearly by volume.

#### Jet Fuel

Jet fuel must have a vapor pressure that does not exceed $1.0 \frac{\text{kg}}{\text{cm}^2}$. The vapor pressures for light, heavy, cracked oils and residuum are, respectively, 1.0, 0.6, 1.5 and 0.05 $\frac{\text{kg}}{\text{cm}^2}$. It is assumed that vapor pressures blend linearly by volume.

#### Fuel Oil

To produce fuel oil, you must blend light oil, cracked oil, heavy oil and residuum in the ratio of 10:4:3:1. 

The availability and capacity limitations are as follows:

- The daily availability of crude 1 is 20 000 barrels.
- The daily availability of crude 2 is 30 000 barrels.
- At most 45 000 barrels of crude can be distilled per day.
- At most 10 000 barrels of naphtha can be reformed per day.
- At most 8000 barrels of oil can be cracked per day.
- The daily production of lube oil must be between 500 and 1000 barrels.
- Premium gasoline production must be at least 40% of regular gasoline production.

Each final product has the following profit contribution per barrel (pennies per barrel):

| <i></i> | Profit Contribution |
| --- | --- |
| Premium Gasoline | 700 |
| Regular Gasoline | 600 |
| Jet Fuel | 400 |
| Fuel Oil | 350 |
| Lube Oil | 150 |

The key question is: How should the operations of the refinery be planned in order to maximize total profit?

---
## Model Formulation

### Sets and Indices

$i \in \text{Crudes}=\{1,2\}$: Set of crude oils.

### Parameters

$\text{buy_limit}_i \in \mathbb{N}$: Maximum number of barrels of crude $i$ to buy.

$\text{distill_cap} \in \mathbb{N}$: Maximum number of barrels of crude oil to distill. 

$\text{reform_cap} \in \mathbb{N}$: Maximum number of barrels of naphtha to reform. 

$\text{crack_cap} \in \mathbb{N}$ Maximum number of barrels of oil to crack.

$\text{LBO_min}, \text{LBO_max} \in \mathbb{N}$ Minimum and Maximum number of barrels of lube oil to produce.

### Decision Variables

$\text{CR}_i \in [0,\text{buy_limit}_i] \subset \mathbb{R}^+$: Number of barrels of crude $i$ to buy.

$\text{LN} \in \mathbb{R}^+$: Number of barrels of light naphtha to distill.

$\text{MN} \in \mathbb{R}^+$: Number of barrels of medium naphtha to distill.

$\text{HN} \in \mathbb{R}^+$: Number of barrels of heavy naphtha to distill.

$\text{LO} \in \mathbb{R}^+$: Number of barrels of light oil to distill.

$\text{HO} \in \mathbb{R}^+$: Number of barrels of heavy oil to distill.

$\text{R} \in \mathbb{R}^+$: Number of barrels of residuum to distill.

$\text{LNRG} \in \mathbb{R}^+$: Number of barrels of light naphtha used to produce reformed gasoline.

$\text{MNRG} \in \mathbb{R}^+$: Number of barrels of medium naphtha used to produce reformed gasoline.

$\text{HNRG} \in \mathbb{R}^+$: Number of barrels of heavy naphtha used to produce reformed gasoline.

$\text{RG} \in \mathbb{R}^+$: Number of barrels of reformed gasoline to produce.

$\text{LOCGO} \in \mathbb{R}^+$: Number of barrels of light oil used to produce cracked gasoline and cracked oil.

$\text{HOCGO} \in \mathbb{R}^+$: Number of barrels of heavy oil used to produce cracked gasoline and cracked oil.

$\text{CG} \in \mathbb{R}^+$: Number of barrels of cracked gasoline to produce.

$\text{CO} \in \mathbb{R}^+$: Number of barrels of cracked oil to produce.

$\text{LNPMF} \in \mathbb{R}^+$: Number of barrels of light naphtha used to produce premium motor fuel.

$\text{LNRMF} \in \mathbb{R}^+$: Number of barrels of light naphtha used to produce regular motor fuel.

$\text{MNPMF} \in \mathbb{R}^+$: Number of barrels of medium naphtha used to produce premium motor fuel.

$\text{MNRMF} \in \mathbb{R}^+$: Number of barrels of medium naphtha used to produce regular motor fuel.

$\text{HNPMF} \in \mathbb{R}^+$: Number of barrels of heavy naphtha used to produce premium motor fuel.

$\text{HNRMF} \in \mathbb{R}^+$: Number of barrels of heavy naphtha used to produce regular motor fuel.

$\text{RGPMF} \in \mathbb{R}^+$: Number of barrels of reformed gasoline used to produce premium motor fuel.

$\text{RGRMF} \in \mathbb{R}^+$: Number of barrels of reformed gasoline used to produce regular motor fuel.

$\text{CGPMF} \in \mathbb{R}^+$: Number of barrels of cracked gasoline used to produce premium motor fuel.

$\text{CGRMF} \in \mathbb{R}^+$: Number of barrels of cracked gasoline used to produce regular motor fuel.

$\text{LOJF} \in \mathbb{R}^+$: Number of barrels of light oil used to produce jet fuel.

$\text{HOJF} \in \mathbb{R}^+$: Number of barrels of heavy oil used to produce jet fuel.

$\text{RJF} \in \mathbb{R}^+$: Number of barrels of residuum used to produce jet fuel.

$\text{COJF} \in \mathbb{R}^+$: Number of barrels of cracked oil used to produce jet fuel.

$\text{RLBO} \in \mathbb{R}^+$: Number of barrels of residuum used to produce lube oil.

$\text{PMF} \in \mathbb{R}^+$: Number of barrels of premium motor fuel to produce.

$\text{RMF} \in \mathbb{R}^+$: Number of barrels of regular motor fuel to produce.

$\text{JF} \in \mathbb{R}^+$: Number of barrels of jet fuel to produce.

$\text{FO} \in \mathbb{R}^+$: Number of barrels of fuel oil to produce.

$\text{LBO} \in [\text{LBO_min}, \text{LBO_max}] \subset \mathbb{R}^+$: Number of barrels of lube oil to produce.

### Objective Function

- **Profit:** Maximize the total profit (in hundreds of USD).

\begin{equation}
\text{Max} \quad Z = 7*\text{PMF} + 6*\text{RMF} + 4*\text{JF} + 3.5*\text{FO} + 1.5*\text{LBO}
\tag{0}
\end{equation}

### Constraints

- **Distillation Capacity**: The number of barrels of crude oil to distill cannot exceed the capacity.

\begin{equation}
\sum_{i \in \text{Crudes}}{\text{CR}_i} \leq \text{distill_cap}
\tag{1}
\end{equation}

- **Reforming Capacity**: The number of barrels of naphtha to reform cannot exceed the capacity.

\begin{equation}
\text{LNRG} + \text{MNRG} + \text{HNRG} \leq \text{reform_cap}
\tag{2}
\end{equation}

- **Cracking Capacity**: The number of barrels of oil to crack cannot exceed the capacity.

\begin{equation}
\text{LOCGO} + \text{HOCGO} \leq \text{crack_cap}
\tag{3}
\end{equation}

- **Yield**: The number of barrels produced depends on the quantities of inputs used, as well as their corresponding yields.

\begin{equation}
0.10*\text{CR}_1 + 0.15*\text{CR}_2 = \text{LN}
\tag{4.1}
\end{equation}

\begin{equation}
0.20*\text{CR}_1 + 0.25*\text{CR}_2 = \text{MN}
\tag{4.2}
\end{equation}

\begin{equation}
0.20*\text{CR}_1 + 0.18*\text{CR}_2 = \text{HN}
\tag{4.3}
\end{equation}

\begin{equation}
0.12*\text{CR}_1 + 0.08*\text{CR}_2 = \text{LO}
\tag{4.4}
\end{equation}

\begin{equation}
0.20*\text{CR}_1 + 0.19*\text{CR}_2 = \text{HO}
\tag{4.5}
\end{equation}

\begin{equation}
0.13*\text{CR}_1 + 0.12*\text{CR}_2 = \text{R}
\tag{4.6}
\end{equation}

\begin{equation}
0.60*\text{LNRG} + 0.52*\text{MNRG} + 0.45*\text{HNRG} = \text{RG}
\tag{4.7}
\end{equation}

\begin{equation}
0.68*\text{LOCGO} + 0.75*\text{HOCGO} = \text{CO}
\tag{4.8}
\end{equation}

\begin{equation}
0.28*\text{LOCGO} + 0.20*\text{HOCGO} = \text{CG}
\tag{4.9}
\end{equation}

\begin{equation}
0.50*\text{RLBO} = \text{LBO}
\tag{4.10}
\end{equation}

\begin{equation}
\text{LNPMF} + \text{MNPMF} + \text{HNPMF} + \text{RGPMF} + \text{CGPMF} = \text{PMF}
\tag{4.11}
\end{equation}

\begin{equation}
\text{LNRMF} + \text{MNRMF} + \text{HNRMF} + \text{RGRMF} + \text{CGRMF} = \text{RMF}
\tag{4.12}
\end{equation}

\begin{equation}
\text{LOJF} + \text{HOJF} + \text{COJF} + \text{RJF} = \text{JF}
\tag{4.13}
\end{equation}

- **Mass Conservation:** The number of barrels used must be equal to the number of barrels available.

\begin{equation}
\text{LNRG} + \text{LNPMF} + \text{LNRMF} = \text{LN}
\tag{5.1}
\end{equation}

\begin{equation}
\text{MNRG} + \text{MNPMF} + \text{MNRMF} = \text{MN}
\tag{5.2}
\end{equation}

\begin{equation}
\text{HNRG} + \text{HNPMF} + \text{HNRMF} = \text{HN}
\tag{5.3}
\end{equation}

\begin{equation}
\text{LOCGO} + \text{LOJF} + 0.55*\text{FO} = \text{LO}
\tag{5.4}
\end{equation}

\begin{equation}
\text{HOCGO} + \text{HOJF} + 0.17*\text{FO} = \text{HO}
\tag{5.5}
\end{equation}

\begin{equation}
\text{COJF} + 0.22*\text{FO} = \text{CO}
\tag{5.6}
\end{equation}

\begin{equation}
\text{RLBO} + \text{RJF} + 0.0555*\text{FO} = \text{R}
\tag{5.7}
\end{equation}

\begin{equation}
\text{CGPMF} + \text{CGRMF} = \text{CG}
\tag{5.8}
\end{equation}

\begin{equation}
\text{RGPMF} + \text{RGRMF} = \text{RG}
\tag{5.9}
\end{equation}

- **Premium-to-Regular Proportion:** The production ratio between premium and regular gasoline must satisfy the minimum requirement.

\begin{equation}
\text{PMF} \geq 0.40*\text{RMF}
\tag{6}
\end{equation}

- **Octane Tolerance:** The octane rating of each type of gasoline cannot drop below the lower tolerance.

\begin{equation}
90*\text{LNPMF} + 80*\text{MNPMF} + 70*\text{HNPMF} + 115*\text{RGPMF} + 105*\text{CGPMF} \geq 94*\text{PMF}
\tag{7.1}
\end{equation}

\begin{equation}
90*\text{LNRMF} + 80*\text{MNRMF} + 70*\text{HNRMF} + 115*\text{RGRMF} + 105*\text{CGRMF} \geq 84*\text{PMF}
\tag{7.2}
\end{equation}

- **Vapor-Pressure Tolerance:** The vapor pressure of jet fuel cannot drop below the lower tolerance.

\begin{equation}
1.0*\text{LOJF} + 0.6*\text{HOJF} + 1.5*\text{COJF} + 0.05*\text{RJF} \leq 1.0*\text{JF}
\tag{8}
\end{equation}

---
## Python Implementation

We import the Gurobi Python Module and other Python libraries.


```python
import numpy as np

import gurobipy as gp
from gurobipy import GRB

# tested with Python 3.7.0 & Gurobi 9.0
```

## Input Data
We define all the input data of the model.


```python
# Parameters

crude_numbers = range(1,2+1)
petrols = ["Premium_fuel", "Regular_fuel"]
end_product_names = ["Premium_fuel", "Regular_fuel", "Jet_fuel", "Fuel_oil", "Lube_oil"]
distillation_products_names = ["Light_naphtha", "Medium_naphtha", "Heavy_naphtha",
                               "Light_oil", "Heavy_oil", "Residuum"]
naphthas = ["Light_naphtha", "Medium_naphtha", "Heavy_naphtha"]
intermediate_oils = ["Light_oil", "Heavy_oil"]
cracking_products_names = ["Cracked_gasoline", "Cracked_oil"]
used_for_motor_fuel_names = ["Light_naphtha", "Medium_naphtha", "Heavy_naphtha",
                             "Reformed_gasoline", "Cracked_gasoline"]
used_for_jet_fuel_names = ["Light_oil", "Heavy_oil", "Residuum", "Cracked_oil"]

buy_limit = {1:20000, 2:30000}
lbo_min = 500
lbo_max = 1000

distill_cap = 45000
reform_cap = 10000
crack_cap = 8000

distillation_splitting_coefficients = {"Light_naphtha": (0.1, 0.15),
                          "Medium_naphtha": (0.2, 0.25),
                         "Heavy_naphtha": (0.2, 0.18),
                         "Light_oil": (0.12, 0.08),
                         "Heavy_oil": (0.2, 0.19),
                         "Residuum": (0.13, 0.12)}

cracking_splitting_coefficients = {("Light_oil","Cracked_oil"): 0.68,
                                   ("Heavy_oil","Cracked_oil"): 0.75,
                                   ("Light_oil","Cracked_gasoline"): 0.28,
                                   ("Heavy_oil","Cracked_gasoline"): 0.2}

reforming_splitting_coefficients = {"Light_naphtha": 0.6, "Medium_naphtha":0.52, "Heavy_naphtha":0.45}
end_product_profit = {"Premium_fuel":7, "Regular_fuel":6, "Jet_fuel":4, "Fuel_oil":3.5, "Lube_oil":1.5}
blending_coefficients = {"Light_oil": 0.55, "Heavy_oil": 0.17, "Cracked_oil": 0.22, "Residuum": 0.055}

lube_oil_factor = 0.5
pmf_rmf_ratio = 0.4

octance_number_coefficients = {
    "Light_naphtha":90,
    "Medium_naphtha":80,
    "Heavy_naphtha":70,
    "Reformed_gasoline":115,
    "Cracked_gasoline":105,
}
octance_number_fuel = {"Premium_fuel": 94,"Regular_fuel": 84}

vapor_pressure_constants = [0.6, 1.5, 0.05]
```

## Model Deployment
We create a model and the variables.


```python
refinery = gp.Model('Refinery_Optimization')

# Variables
crudes = refinery.addVars(crude_numbers, ub=buy_limit, name="cr")    
end_products = refinery.addVars(end_product_names, name="end_prod")
end_products["Lube_oil"].lb= lbo_min
end_products["Lube_oil"].ub= lbo_max
distillation_products = refinery.addVars(distillation_products_names, name="dist_prod")
reform_usage = refinery.addVars(naphthas, name="napthas_to_reformed_gasoline")
reformed_gasoline = refinery.addVar(name="reformed_gasoline")
cracking_usage = refinery.addVars(intermediate_oils,name="intermediate_oils_to_cracked_gasoline")
cracking_products = refinery.addVars(cracking_products_names,  name="cracking_prods")
used_for_regular_motor_fuel = refinery.addVars(used_for_motor_fuel_names, name="motor_fuel_to_regular_motor_fuel")
used_for_premium_motor_fuel = refinery.addVars(used_for_motor_fuel_names, name="motot_fuel_to_premium_motor_fuel")
used_for_jet_fuel = refinery.addVars(used_for_jet_fuel_names, name="jet_fuel")
used_for_lube_oil = refinery.addVar(vtype=GRB.CONTINUOUS,name="residuum_used_for_lube_oil")
```

    Using license file c:\gurobi\gurobi.lic
    Set parameter TokenServer to value SANTOS-SURFACE-


Next, we insert the constraints.

The distillation capacity constraint is:


```python
# 1. Distillation capacity
DistillationCap = refinery.addConstr(crudes.sum() <= distill_cap, "Distill_cap")
```

The reforming capacity constraint is:


```python
# 2. Reforming capacity
ReformingCap = refinery.addConstr(reform_usage.sum() <= reform_cap, "Reform_cap")
```

The cracking capacity constraint is:


```python
# 3. Cracking capacity
CrackingCap = refinery.addConstr(cracking_usage.sum() <= crack_cap, "Crack_cap")
```

The quantity of distillation products produced depends on the quantity of crude oil used, taking into account the way in which each crude splits under distillation. This gives:


```python
# 4.1-4.6 Yield (Crude oil products)
YieldCrudeOil = refinery.addConstrs((gp.quicksum(distillation_splitting_coefficients[dpn][crude-1]*crudes[crude] for crude in crudes)
                  == distillation_products[dpn] for dpn in distillation_products_names), "Splitting_distillation")
```

The quantity of reformed gasoline produced depends on the quantities of naphthas used in the reforming process. This gives the constraint:


```python
# 4.7 Yield (Reforming of Naphthas)
YieldNaphthas = refinery.addConstr(reform_usage.prod(reforming_splitting_coefficients) == reformed_gasoline, "Splitting_reforming")
```

The quantities of cracked oil and cracked gasoline produced depend on the quantities of light and heavy oil used. This gives the constraints:


```python
# 4.8-4.9 Yield (Cracking of oils)
YieldCrackingOil = refinery.addConstrs((gp.quicksum(cracking_splitting_coefficients[oil, crack_prod]*cracking_usage[oil]
                           for oil in intermediate_oils) == cracking_products[crack_prod]
                  for crack_prod in cracking_products_names),
                 name="Splitting_cracking")
```

The quantity of lube-oil produced (and sold) is 0.5 times the quantity of residuum used. This gives:


```python
# 4.10 Yield (Lube oil)
YieldLubeOil = refinery.addConstr(lube_oil_factor*used_for_lube_oil == end_products["Lube_oil"],
                "Splitting_lube_oil")
```

The quantity of motor fuels and jet fuel produced is equal to the total quantity of their ingredients. This gives the constraints:


```python
# 4.11 Yield (Premium gasoline)
YieldPremium = refinery.addConstr(used_for_premium_motor_fuel.sum() == end_products["Premium_fuel"], "Blending_premium_fuel")

# 4.12 Yield (Regular gasoline)
YieldRegular = refinery.addConstr(used_for_regular_motor_fuel.sum() == end_products["Regular_fuel"], "Blending_regular_fuel")

# 4.13 Yield (Jet fuel)
YieldJetFuel = refinery.addConstr(used_for_jet_fuel.sum() == end_products["Jet_fuel"], "Continuity_jet_fuel")
```

The quantities of naphthas used for reforming and blending are equal to the quantities available. This gives:


```python
# 5.1-5.3 Mass conservation (Naphthas)
MassBalNaphthas = refinery.addConstrs((reform_usage[naphtha] +
                    used_for_regular_motor_fuel[naphtha] +
                    used_for_premium_motor_fuel[naphtha] ==
                    distillation_products[naphtha] for naphtha in naphthas), "Continuity_napththa")
```

For the blending of fuel oil, the proportion of light oil/heavy oil/ cracked oil/ residuum is fixed. Therefore, separate variables have not been introduced for this proportion as it is determined by the variables. This gives:


```python
# 5.4 Mass Conservation (Light oil)
MassBalLightOil = refinery.addConstr(cracking_usage["Light_oil"]+
                used_for_jet_fuel["Light_oil"]+
                blending_coefficients["Light_oil"]*end_products["Fuel_oil"] ==
                distillation_products["Light_oil"], "Fixed_proportion_light_oil_for_blending")

# 5.5 Mass Conservation (Heavy oil)
MassBalHeavyOil = refinery.addConstr(cracking_usage["Heavy_oil"]+
                used_for_jet_fuel["Heavy_oil"]+
                blending_coefficients["Heavy_oil"]*end_products["Fuel_oil"] ==
                distillation_products["Heavy_oil"], "Fixed_proportion_heavy_oil_for_blending")

# 5.6 Mass Conservation (Cracked oil)
MassBalCrackedOil = refinery.addConstr(used_for_jet_fuel["Cracked_oil"]+
                blending_coefficients["Cracked_oil"]*end_products["Fuel_oil"] ==
                cracking_products["Cracked_oil"], "Fixed_proportion_cracked_oil_for_blending")

# 5.7 Mass Conservation (Residuum)
MassBalResiduum = refinery.addConstr(used_for_lube_oil +
                used_for_jet_fuel["Residuum"]+
                blending_coefficients["Residuum"]*end_products["Fuel_oil"] ==
                distillation_products["Residuum"], "Fixed_proportion_residuum_for_blending")

# 5.8 Mass conservation (Cracked gasoline)
MassBalCrackedGas = refinery.addConstr(used_for_regular_motor_fuel["Cracked_gasoline"] +
                used_for_premium_motor_fuel["Cracked_gasoline"] ==
                cracking_products["Cracked_gasoline"], "Continuity_cracked_gasoline")

# 5.9 Mass conservation (Reformed gasoline)
MassBalReformedGas = refinery.addConstr(used_for_regular_motor_fuel["Reformed_gasoline"] +
                used_for_premium_motor_fuel["Reformed_gasoline"] ==
                reformed_gasoline, "Continuity_reformed_gasoline")
```

Premium motor fuel production must be at least 40% of regular motor fuel production, giving:


```python
# 7. Premium-to-regular proportion
Premium2Regular = refinery.addConstr(end_products["Premium_fuel"] >= pmf_rmf_ratio*end_products["Regular_fuel"],
                "Prem2reg_prop")
```

It is necessary to stipulate that the octane number of premium motor (regular motor) fuel does not drop below 94 (84). This is done by the constraint:


```python
# 8.1-8.2 Octane tolerance
OctaneRegular = refinery.addConstr(used_for_regular_motor_fuel.prod(octance_number_coefficients) >=
                octance_number_fuel["Regular_fuel"] * end_products["Regular_fuel"],
                "Octane_tol_regular_fuel")

OctanePremium = refinery.addConstr(used_for_premium_motor_fuel.prod(octance_number_coefficients) >=
                octance_number_fuel["Premium_fuel"] * end_products["Premium_fuel"],
                "Octane_tol_premium_fuel")
```

For jet fuel, we have the constraint imposed by vapor pressure:


```python
# 9. Vapor-pressure tolerance
VaporPressure = refinery.addConstr(used_for_jet_fuel["Light_oil"] +
                vapor_pressure_constants[0]*used_for_jet_fuel["Heavy_oil"] +
                vapor_pressure_constants[1]*used_for_jet_fuel["Cracked_oil"] +
                vapor_pressure_constants[2]*used_for_jet_fuel["Residuum"] <= end_products["Jet_fuel"],
                "Vapor_pressure_tol")
```

This model has 29 constraints together with simple bounds on three variables.

A note should be made concerning the blending of fuel oil where the ingredients (light, heavy, and cracked oil and residuum) are used in fixed proportions. It might be preferable to think of the production of fuel oil as an activity. It is common in the oil industry to think in terms of activities rather than quantities. In Section 3.4 of the H.P. Williams book, model formulations are discussed where activities represent the extreme modes of operation of a process. In this example, we have a special case of a process with one mode of operation. The level of this activity then fixes the ratios of the ingredients, in a case such as this, automatically.

The only variables involving a profit (or cost) are the final products. This gives an objective (in dollars) to be maximized of:


```python
# 0. Profit
refinery.setObjective(end_products.prod(end_product_profit), GRB.MAXIMIZE)
```

Next, the optimization process starts and Gurobi finds the optimal solution.


```python
refinery.optimize()
```

    Gurobi Optimizer version 9.0.0 build v9.0.0rc2 (win64)
    Optimize a model with 29 rows, 36 columns and 106 nonzeros
    Model fingerprint: 0xe30699e6
    Coefficient statistics:
      Matrix range     [5e-02, 1e+02]
      Objective range  [2e+00, 7e+00]
      Bounds range     [5e+02, 3e+04]
      RHS range        [8e+03, 5e+04]
    Presolve removed 13 rows and 14 columns
    Presolve time: 0.01s
    Presolved: 16 rows, 22 columns, 72 nonzeros
    
    Iteration    Objective       Primal Inf.    Dual Inf.      Time
           0    1.1887574e+06   6.045565e+04   0.000000e+00      0s
          14    2.1136513e+05   0.000000e+00   0.000000e+00      0s
    
    Solved in 14 iterations and 0.01 seconds
    Optimal objective  2.113651348e+05


---
## Analysis

The optimal solution results in a profit of $\$211,365.13$. The optimal values of the variables are given below:


```python
for var in refinery.getVars():
    if abs(var.x) > 1e-6:
        print("{0} = {1}".format(var.varName, np.round(var.x, 2)))
```

    cr[1] = 15000.0
    cr[2] = 30000.0
    end_prod[Premium_fuel] = 6817.78
    end_prod[Regular_fuel] = 17044.45
    end_prod[Jet_fuel] = 15156.0
    end_prod[Lube_oil] = 500.0
    dist_prod[Light_naphtha] = 6000.0
    dist_prod[Medium_naphtha] = 10500.0
    dist_prod[Heavy_naphtha] = 8400.0
    dist_prod[Light_oil] = 4200.0
    dist_prod[Heavy_oil] = 8700.0
    dist_prod[Residuum] = 5550.0
    napthas_to_reformed_gasoline[Heavy_naphtha] = 5406.86
    reformed_gasoline = 2433.09
    intermediate_oils_to_cracked_gasoline[Light_oil] = 4200.0
    intermediate_oils_to_cracked_gasoline[Heavy_oil] = 3800.0
    cracking_prods[Cracked_gasoline] = 1936.0
    cracking_prods[Cracked_oil] = 5706.0
    motor_fuel_to_regular_motor_fuel[Light_naphtha] = 273.07
    motor_fuel_to_regular_motor_fuel[Medium_naphtha] = 10500.0
    motor_fuel_to_regular_motor_fuel[Heavy_naphtha] = 2993.14
    motor_fuel_to_regular_motor_fuel[Reformed_gasoline] = 1342.24
    motor_fuel_to_regular_motor_fuel[Cracked_gasoline] = 1936.0
    motot_fuel_to_premium_motor_fuel[Light_naphtha] = 5726.93
    motot_fuel_to_premium_motor_fuel[Reformed_gasoline] = 1090.84
    jet_fuel[Heavy_oil] = 4900.0
    jet_fuel[Residuum] = 4550.0
    jet_fuel[Cracked_oil] = 5706.0
    residuum_used_for_lube_oil = 1000.0


---
## References

H. Paul Williams, Model Building in Mathematical Programming, fifth edition.

Copyright © 2020 Gurobi Optimization, LLC


```python

```

9af8279d6cb4199ae8338f2eaed4ea5806d263d1
ipynb
Jupyter Notebook
Gurobi/Gurobi-modeling-examples-master/refinery/refinery.ipynb
FlameCiri/operations_research_notebook
ada2b7d5fb6789620f44f3b1500fa91d811597eb
MIT
2021-08-29T08:49:57.000Z
2022-03-13T01:48:58.000Z
Gurobi/Gurobi-modeling-examples-master/refinery/refinery.ipynb
FlameCiri/operations_research_notebook
ada2b7d5fb6789620f44f3b1500fa91d811597eb
MIT
Gurobi/Gurobi-modeling-examples-master/refinery/refinery.ipynb
FlameCiri/operations_research_notebook
ada2b7d5fb6789620f44f3b1500fa91d811597eb
MIT
2021-09-02T09:20:36.000Z
2021-09-02T09:20:36.000Z
Qwen/Qwen-72B
1. YES
2. YES