2
$\begingroup$

I am working on an optimisation problem that involves minimising shipping demurrage. I am struggling to model how to represent the difference, (x-y) between dates where the ship is ready to be loaded and when the ship is actually loaded. This difference, in days, is where demurrage charges are incurred (if applicable).

How can I calculate this difference in pulp and model the 'vessel_ready_to_load' date vs the 'vessel_start_load' date?

Code below:

planning_horizon_dates ['2020-01-01', '2020-01-02', '2020-01-03']

# Port inventory
port_stock_inventory = {
    'RBL': {'current': 200000, 
            'target': 180000, # Note targets are set by separate opt.
            'max': 200000}, 
    'RB2': {'current': 200000, 
            'target': 180000, 
            'max': 200000},
    'PRE': {'current': 200000, 
            'target': 180000, 
            'max': 200000},
    'AFL': {'current': 200000,
            'target': 180000,
            'max': 200000},
    'ACA': {'current': 200000,
            'target': 180000,
            'max': 200000}}

sales_demand_by_vessel ={
    '2020-01-01': {
        'CEYLON': {
            'MAF': 0, 'PRE': 40000, 'ZBL': 0, 'AFE': 10000, 'AAC': 70000
            },
        'KONOS': {
            'MAF': 0, 'PRE': 100000, 'ZBL': 0, 'AFE': 0, 'AAC': 0
            },
        'BULK JAPAN': {
            'MAF': 30000, 'PRE': 0, 'ZBL': 70000, 'AFE': 0, 'AAC': 0
            },
        'XIN FA HAI': {
            'MAF': 0, 'PRE': 0, 'ZBL': 9000, 'AFE': 20000, 'AAC': 0
        }
    },
    '2020-01-02': {
        'PACIFIC MAJOR': {
            'MAF': 50000, 'PRE': 0, 'ZBL': 60000, 'AFE': 10000, 'AAC': 0
            },
        'CCSC YASA JING': {
            'MAF': 10000, 'PRE': 0, 'ZBL': 0, 'AFE': 0, 'AAC': 60000
            },
        'XIAOMING HAO HAI': {
            'MAF': 30000, 'PRE': 0, 'ZBL': 70000, 'AFE': 0, 'AAC': 0
            },
        'ROBUSTA': {
            'MAF': 0, 'PRE': 0, 'ZBL': 0, 'AFE': 50000, 'AAC': 0
        }
    },
    '2020-01-03': {
        'AQUA': {
            'MAF': 0, 'PRE': 0, 'ZBL': 0, 'AFE': 10000, 'AAC': 70000
            },
        'ARUN': {
            'MAF': 0, 'PRE': 0, 'ZBL': 50000, 'AFE': 0, 'AAC': 0
            },
        'HARALL': {
            'MAF': 30000, 'PRE': 0, 'ZBL': 70000, 'AFE': 0, 'AAC': 0
            },
        'MAMBO': {
            'MAF': 0, 'PRE': 0, 'ZBL': 9000, 'AFE': 20000, 'AAC': 0
        }
    },
}


# DECISION VARIABLES

# Binary indicators for all possible vessel load dates after NOR date.
vessel_load_start_date = pulp.LpVariable.dicts(
    'Vessel Load Start Date', 
    ((vessel, date) for vessel, date in load_start_dates.index),
    lowBound=0, 
    cat='Binary')

# Vessel Sales Demand
vessel_sales_demand_vars = pulp.LpVariable.dicts(
    'Vessel Sales Complete',
    ((vessel, product, date) for product in products for vessel, date in load_start_dates.index),
    lowBound=0,
    cat='Continuous'
)

# Vessel grade requirements
vessel_product_requirements = pulp.LpVariable.dicts(
    "Vessel-Grade Requirement Tonnes", 
    ((vessel, product) for vessel in vessels for product in products),
    lowBound=0, 
    cat='Continuous')

# Model
model = pulp.LpProblem('Demurrage Optimisation', pulp.LpMinimize)


# Objective Function
model += pulp.lpSum([
    demurrage_charge_vars[vessel]
    for vessel in demurrage_charge_vars])

# Vessel can ONLY begin loading if there is sufficient supply of each product
for vessel in vessels:
  model += port_inventory[date][product] >= sales_demand_by_vessel[date][vessel][product] == 
  load_start_date[vessel]

# Vessel loading can only on or after notification of readiness
for vessel in vessels:
  model += load_start_date[vessel] >= readiness_date_dict[vessel]
  model += load_start_date_dict[vessel] - readiness_date_dict[vessel]  * daily_dem_rate == demurrage_charge_vars[date][vessel]


# Control vessel loading
for product in products:
  for vessel, date in vessel_load_start_date:
    vessel_sales_demand_vars[(vessel, product, date)] - vessel_product_requirements[vessel, product] * load_start_date[vessel, date] <= 0

Any help very gratefully received!

$\endgroup$
4
$\begingroup$

You don't want to create a variable representing a date in pulp. You want to utilize zero-one indicator variables for each option. In this case your options are the vessel-date combinations. Index the variables by vessel and date.

Suppose the value of the difference between the actually loaded date and the ready to be loaded date is to be used in the objective or a constraint. Then you need to determine the possible set of dates $T_i$ each vessel $i$ can be finally loaded and create indicator variables $y_{it}$ for each possibility. Utilize the constraint set $x_{ijt} - c_{ij}y_{it} <= 0 \space \forall \space i\in V,\space j\in P,\space t\in T_i$ to indicate that a vessel $i$ is finally loaded with $c_{ij}$ quantity of product $j$ on a particular day $t$. This gives you the ability to restrict loading to particular days or to calculate charges based on options for loading through the use of the variables $y_{it}$ and control the port inventory through the variables $x_{ijt}$.

For the total demurrage charge, instead of an expression like $\sum_{i\in V} c_i t_i$ where $c_i$ is the daily demurrage charge and $t_i$ is the number of days of demurrage, you have $\sum_{i\in V, t\in T_i}c_{it}y_{it}$ where $c_{it}$ is the charge if vessel $i$ has $t$ days of demurrage and $y_{it}=1$ indicates the outcome of the finally loaded date.

| improve this answer | |
$\endgroup$
  • $\begingroup$ Many thanks for your help once again Wesley. I am quite new to pulp and LP - could you perhaps add the python logic to the above (I understand code formulations of pulp!) please? $\endgroup$ – cmp Jun 18 at 8:46
  • $\begingroup$ Specifically for the constraint and the demurrage - i have formulated the Binary indicator vars for each vessel, i, and time, t, based on the vessel_ready_to_load_dates. Thank you. $\endgroup$ – cmp Jun 18 at 8:59
  • $\begingroup$ I have attempted your solution however pulp does not allow non-linear expressions i.e. C(ij)Y(it). Is there a way to linearize this constraint? I have added my code to the above problem. Many thanks once again - much appreciated. $\endgroup$ – cmp Jun 19 at 8:59
  • $\begingroup$ $c_{ij}$ is a constant, not a variable, so it is already linear. $\endgroup$ – Wesley Dyk Jun 19 at 17:35
  • 1
    $\begingroup$ Correct. It should be multiplied by the binary variable. It looks like you've used vessel_load_start_date. The constraint would be vessel_sales_demand_vars[vessel][product][date] - sales_demand_by_vessel[date][vessel][product] * vessel_load_start_date[vessel][date] <= 0. $\endgroup$ – Wesley Dyk Jun 21 at 2:51

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.