SupplyChainModeling

Installation

SupplyChainModeling can be installed using the Julia package manager. From the Julia REPL, type ] to enter the Pkg REPL mode and run

pkg> add SupplyChainModeling

API

SupplyChainModeling.ConcreteNodeType
ConcreteNode

The closed set of built-in Node subtypes that participate in the lane-based flow network (Lane, SupplyChain.lanes_in/lanes_out, get_location_index). Hot-path type signatures use this instead of the bare abstract Node so Julia can compile dispatch as a handful of concrete branches (union-splitting) instead of a fully dynamic call - Node itself stays open for extension, but anything typed ConcreteNode needs updating (here) if a new Node subtype is added and should participate in those dispatch-efficient paths.

MaturationSource and QuotaSink are deliberately not part of this union: they connect through direct distance-based costing rather than Lanes (see SupplyChainOptimization.create_maturation_scheduling_model), so they never appear as a Lane origin/destination or in the lane-indexed dictionaries this union exists to speed up.

source
SupplyChainModeling.IndexedCollectionType
IndexedCollection{T}

A stable ordering of a SupplyChain's nodes/products alongside a Dict{T, Int64} mapping each one to its position in that ordering.

Consumers like SupplyChainSimulation.State need to translate a Storage/ Product/location object into an integer index for direct array access on every inventory read/write; this pairs that translation Dict with the Vector it was built from so both can be handed out together, computed once (see get_storage_index/get_product_index/get_location_index below) instead of every State re-deriving its own copy.

source
SupplyChainModeling.LocationType

The geographical location of a node of the supply chain. The location is defined by its latitude and longitude, and an optional name.

country is the ISO 3166-1 alpha-2 country code (e.g. "US", "CN") used to look up tariff rates between locations (see Tariff); it defaults to nothing, meaning the location isn't assigned to a customs territory and never triggers a tariff.

source
SupplyChainModeling.MaturationSourceType

A single-batch production source whose product's value is a function of how long it has been held (see get_maturity_value) - the batch's age-value curve. This generalizes two families of problems that the operations-research literature usually treats separately:

  • Perishable/deteriorating inventory (Nahmias, 1982; Goyal and Giri, 2001): value is typically flat, then drops to zero at a fixed shelf life, or decays continuously from the moment of production.
  • Harvest/maturity scheduling (e.g. sugarcane or wine-grape harvest scheduling: fields/grapes ripen toward a peak, must be harvested within a window, and the mill/winery has a periodic intake capacity): value rises toward an ideal window before falling off if held too long.

Both are the same underlying object - a batch whose eligibility and value for shipment is age_value_curve(duration) - differing only in the curve's shape. add_product! has a convenient linear-growth-with-percentage-bands form for the common (harvest-scheduling-style) case, and a fully custom form accepting arbitrary value/feasible/penalty functions for anything else, including classical shelf-life curves.

A MaturationSource holds at most one batch per planning horizon ("all-in-all-out"): once a batch is started, the source cannot start another until that batch has shipped and, optionally, a changeover_periods-long turnaround has elapsed.

source
SupplyChainModeling.ProductType

A product in the supply chain.

zones are the shipping zone multipliers applied to lane costs for this product (see Lane's unit_cost); it defaults to [1.0], a single zone with no adjustment.

source
SupplyChainModeling.QuotaSinkType

A demand point with a soft periodic quota: deliveries of a product are not capped, but any shortfall or excess relative to the quota is penalized per unit rather than forbidden. This generalizes supply-managed quota systems (e.g. Canadian poultry/egg/dairy production quotas) and any other contractual periodic delivery target that a business would rather miss (at a cost) than treat as a hard constraint.

source
SupplyChainModeling.TariffType

An ad-valorem tariff applied to a product moving from one customs territory to another.

rate is the fraction (e.g. 0.25 for 25%) applied to the product's declared value when a unit crosses from origin_country into destination_country. product is nothing to mean "every product" moving between the two countries.

Countries are matched against Location.country; a Lane whose origin and destination resolve to the same country (or where either side has no country set) never incurs a tariff.

source
SupplyChainModeling.add_demand!Method
add_demand!(supply_chain, customer, product, demand::Array{Float64, 1}; service_level=1.0)

Adds customer demand for a product. The demand is specified for each time period.

The keyword arguments are:

  • service_level: indicates how many lost sales are allowed as a ratio of demand. No demand can be lost if the service level is 1.0 and all demand can be lost if the service level is 0.0.
  • sales_price: the sales price of a unit of product.
  • lost_sales_cost: the cost of losing the sales of a unit of product.
source
SupplyChainModeling.add_product!Method
add_product!(source::MaturationSource, product, value_function, feasible_duration, duration_penalty; initial_inventory=0.0)

Advanced form of add_product!: registers product on source with a fully custom age-value curve, instead of the linear-growth-with-percentage-bands convenience form below. Useful for anything that form can't express - for example, a classical shelf-life curve (constant value, infeasible to ship past a fixed age):

add_product!(source, product,
             duration -> 1.0,               # value_function: constant per-unit value
             duration -> duration <= 5,      # feasible_duration: sellable for 5 periods
             duration -> 0.0)                # duration_penalty: no partial-quality penalty
  • value_function(duration): the batch's value after being held duration periods.
  • feasible_duration(duration): whether a batch may ship after being held duration periods.
  • duration_penalty(duration): the per-unit cost of shipping after duration periods (zero within the curve's ideal range).
  • initial_inventory: if greater than zero, this source already holds a batch of this size at the start of the planning horizon (with current value value_function(0)), which must ship during the horizon rather than being a free scheduling choice.
source
SupplyChainModeling.add_product!Method
add_product!(source::MaturationSource, product; initial_value, maturation_rate, target_value,
                                                acceptable_deviation_under, acceptable_deviation_over,
                                                extended_deviation_under=0.0, extended_deviation_over=0.0,
                                                underrun_unit_penalty=0.0, overrun_unit_penalty=0.0,
                                                initial_inventory=0.0)

Indicates that a MaturationSource can hold a batch of product, whose value grows linearly while held, toward a target with percentage-based tolerance bands - a convenience over the fully custom add_product! method above, for this common case (see MaturationSource's own docstring for the harvest-scheduling problems this shape fits).

The keyword arguments are:

  • initial_value: the value (e.g. weight) of the product when a batch starts, or its current value if initial_inventory is greater than zero (i.e. a batch is already in progress at the start of the planning horizon).
  • maturation_rate: the value gained per period a batch is held.
  • target_value: the ideal value at the time a batch ships.
  • acceptable_deviation_under, acceptable_deviation_over: the maximum deviation below/above target_value (as a fraction of it) still considered on-target.
  • extended_deviation_under, extended_deviation_over: additional deviation below/above the acceptable range (again as a fraction of target_value) that is still sellable, e.g. to an alternative market, subject to underrun_unit_penalty/overrun_unit_penalty. A batch whose value falls outside even this extended range cannot ship.
  • underrun_unit_penalty, overrun_unit_penalty: the cost per unit of value deviation from target_value for a batch that ships within the extended-but-not-acceptable range.
  • initial_inventory: if greater than zero, this source already holds a batch of this size at the start of the planning horizon (with current value initial_value), which must ship during the horizon rather than being a free scheduling choice.
source
SupplyChainModeling.add_product!Method
add_product!(plant::Plant, product::Product; bill_of_material::Dict{Product, Float64}, unit_cost, maximum_throughput)

Indicates that a plant can produce a product.

The keyword arguments are:

  • bill_of_material: the amount of other product needed to produce one unit of the product. This dictionary can be empty if there are no other products needed.
  • unit_cost: the cost of producing one unit of product.
  • maximum_throughput: the maximum amount of product that can be produced in a time period.
  • time: the production lead time.
source
SupplyChainModeling.add_product!Method
add_product!(sink::QuotaSink, product; quota, underproduction_unit_penalty=0.0, overproduction_unit_penalty=0.0)

Indicates that a QuotaSink has a periodic delivery quota for product.

The keyword arguments are:

  • quota: the target quantity of product to be delivered in each period.
  • underproduction_unit_penalty: the cost per unit delivered below quota in a period.
  • overproduction_unit_penalty: the cost per unit delivered above quota in a period.
source
SupplyChainModeling.add_product!Method
add_product!(storage::Storage, product; initial_inventory::Real=0,
                                        unit_handling_cost::Real=0,
                                        unit_holding_cost::Real=0,
                                        maximum_throughput::Float64=Inf,
                                        additional_stock_cover::Real=0.0,
                                        maximum_units::Real=Inf,
                                        overflow_unit_cost::Real=0.0)

Indicates that a storage can store a product.

The keyword arguments are: - initial_inventory: the amount of product initially at the storage location - unit_handling_cost: : the cost of handling a unit of product at the storage location - unit_holding_cost: the cost of holding a unit of product at the storage location per period - maximum_throughput: the maximum number of units of product that can be sent per period - maximum_units: the maximum number of units of product that can be stored at the storage location at once - overflow_unit_cost: the cost per unit per period charged when more inventory arrives than maximum_units allows (e.g. temporary/overflow storage); the excess is delayed rather than lost

source
SupplyChainModeling.add_product!Method
add_product!(supplier::Supplier, product::Product; unit_cost::Float64, maximum_throughput::Float64)

Indicates that a supplier can provide a product.

The keyword arguments are:

  • unit_cost: the cost per unit of the product from this supplier.
  • maximum_throughput: the maximum number of units that can be provided in each time period.
source
SupplyChainModeling.add_tariff!Method
add_tariff!(supply_chain, tariff::Tariff)

Adds a tariff to the supply chain. A second Tariff for the same (origin_country, destination_country, product) replaces the first.

source
SupplyChainModeling.get_lane_indexMethod
get_lane_index(supply_chain::SupplyChain)::IndexedCollection{Lane}

Gets supply_chain.lanes paired with a Dict{Lane, Int64} index into it, computed once and cached (see IndexedCollection).

Unlike get_storage_index/get_product_index/get_location_index, supply_chain.lanes is already a stable, insertion-ordered Array (not a Set), so items below is a copy() of it rather than a collect() of some other backing container - taken so the cached IndexedCollection stays an immutable snapshot decoupled from supply_chain.lanes (which add_lane! mutates in place with push!), same as the other three indices' relationship to the Sets they're built from.

source
SupplyChainModeling.get_leadtimeMethod
get_leadtime(lane::Lane, destination::Int64)

Gets the lead time to reach the destination at the given index in lane.destinations. Prefer the ConcreteNode-based overload below when you have the destination itself rather than its index.

source
SupplyChainModeling.get_location_indexMethod
get_location_index(supply_chain::SupplyChain)::IndexedCollection{ConcreteNode}

Gets every Storage/Customer/Supplier in supply_chain (Plants are not a "location" in this sense - see get_locations in SupplyChainSimulation.jl) as a stable Vector paired with a Dict{ConcreteNode, Int64} index into it, computed once and cached (see IndexedCollection).

source
SupplyChainModeling.get_maturity_valueMethod
get_maturity_value(source::MaturationSource, product, duration)

Gets the expected batch value (e.g. weight) of product at source after being held for duration periods, per the source's registered age-value curve (see add_product!).

source
SupplyChainModeling.get_overflow_costMethod
get_overflow_cost(node, product)

Gets the per-unit-per-period cost charged for inventory that exceeds maximum_units and must be held in temporary overflow storage.

source
SupplyChainModeling.get_product_indexMethod
get_product_index(supply_chain::SupplyChain)::IndexedCollection{Product}

Gets supply_chain.products as a stable Vector paired with a Dict{Product, Int64} index into it, computed once and cached (see IndexedCollection).

source
SupplyChainModeling.get_storage_indexMethod
get_storage_index(supply_chain::SupplyChain)::IndexedCollection{Storage}

Gets supply_chain.storages as a stable Vector paired with a Dict{Storage, Int64} index into it, computed once and cached (see IndexedCollection).

source
SupplyChainModeling.get_tariff_rateMethod
get_tariff_rate(supply_chain, origin_country, destination_country, product)::Float64

Gets the ad-valorem tariff rate (e.g. 0.25 for 25%) applied to product moving from origin_country to destination_country, falling back to a tariff registered for every product (see Tariff) between the two countries, or 0.0 if neither is registered.

source