Skip to content

MEIO — Inventory Optimisation User Guide

Who this guide is for

This guide is written for supply chain planners and inventory managers. It explains what MEIO computes, how to interpret the results, how to configure service-level targets, and how to run MEIO from the pipeline. No advanced mathematics is required — for the underlying theory, see MEIO Theory.


What is MEIO?

MEIO stands for Multi-Echelon Inventory Optimisation. It answers one practical question:

Given our demand forecasts, lead times, and service-level targets, what safety stock should we hold at every location for every item — and where in the network should we hold it?

Figure: the MEIO user workflow from configuration through running to reviewing results.

flowchart TD
  A["Configure scenario (FR target, budget, per-segment overrides)"] --> B["Link scenario to a pipeline"]
  B --> C["Run MEIO from Process Runner or MEIO Scenarios page"]
  C --> D["Rust optimizer computes safety stock per item-site"]
  D --> E["Results: committed buffer, fill rate, inventory value, wait time"]
  E --> F["Review heatmap / KPIs / Segments tab"]
  F --> G["Adjust segment targets or overrides"]
  G --> C

Traditional inventory planning tools compute safety stock location by location, independently. MEIO considers the entire supply network simultaneously: central warehouse → regional warehouse → field/retail sites. It recognises that holding safety stock at the regional warehouse can reduce the amount needed at field sites (because the field can be replenished quickly from a well-stocked regional hub). This network-aware optimisation typically reduces total inventory investment by 15–30 % compared to independent per-location calculations while achieving the same service levels.

What MEIO computes

For every item × site combination in your network, MEIO outputs:

Output What it means
Committed buffer The safety stock quantity to hold at that location
Fill rate The percentage of demand that will be met from stock at that location (target: your service-level goal)
Inventory value Buffer quantity × unit cost — the investment required
Marginal value How much fill rate would improve per currency unit of additional investment at this location — useful for prioritising where to invest next
Wait time The expected delay (weeks) a downstream location experiences if this location is out of stock

The MEIO Scenarios page is at /meio in the left navigation. It lists all configured MEIO scenarios and their latest results.

IO tab on Dashboard

MEIO results are also surfaced in the Dashboard IO tab as a fill-rate heatmap and KPI cards.

MEIO Scenarios list

Each row shows: - Scenario name and whether it is the base (default) scenario - Date of last run - Summary: total inventory value, weighted average fill rate, number of SKUs covered, number of segments

Click a scenario row to open its detail view.

Scenario detail view

IO heatmap

The heatmap plots SKUs by forecast volume and unit cost, coloured by achieved fill rate.

The detail view has three tabs:

Tab Content
Results Per-item table: committed buffer, fill rate, inventory value, wait time
Segments Per-segment achieved fill rate vs. target, budget consumed
Configuration Scenario parameters and per-segment overrides

Understanding the results

Committed buffer

The committed buffer is the safety stock quantity in units. This is the number of units you should have on hand above and beyond what you expect to sell during lead time. For example:

  • Weekly demand rate: 10 units
  • Lead time: 3 weeks
  • Expected lead-time demand: 30 units
  • Committed buffer: 15 units
  • Reorder point = 30 + 15 = 45 units

When on-hand stock falls to 45 units, place an order.

Fill rate vs. service level

MEIO targets fill rate — the percentage of demand units that are immediately available from stock. This is different from the cycle service level (probability of no stockout per cycle):

  • A fill rate of 95 % means 95 out of every 100 units demanded are delivered immediately from stock. The remaining 5 % experience a backorder or delay.
  • This is more intuitive for operational teams than cycle service level and is the standard used by most planners.

Mirabelle uses line fill rate by default: one order line = one unit, regardless of order size.

Marginal value

The marginal value tells you where to invest next if you want to improve service. A high marginal value means that adding one more unit of buffer here gives a large fill-rate improvement per currency spent. The MEIO optimizer has already exploited the highest-MV opportunities; the remaining marginal values represent diminishing returns.

Figure: the trade-off MEIO navigates between service level (fill rate) and inventory cost.

quadrantChart
  title "Service level vs inventory cost"
  x-axis "Low inventory cost" --> "High inventory cost"
  y-axis "Low fill rate" --> "High fill rate"
  quadrant-1 "High service, high cost"
  quadrant-2 "High service, low cost (ideal)"
  quadrant-3 "Low service, low cost"
  quadrant-4 "Low service, high cost"
  "Under-buffered SKU": [0.75, 0.4]
  "Optimised SKU": [0.45, 0.85]
  "Over-buffered SKU": [0.85, 0.95]

Use marginal value to answer: "If my budget increases by €10 000, which items and locations should receive the extra stock?" — prioritise by highest marginal value.

Wait time

Wait time is the expected delay in weeks that a downstream location experiences when it needs replenishment but its upstream warehouse is out of stock. A wait time of 2 weeks means that, on average, orders that arrive when the upstream is out of stock wait an additional 2 weeks beyond the normal transit time.

A high wait time at a central warehouse cascades to all downstream sites. Investing in buffer at the central warehouse is often the most efficient way to reduce wait times across the network.


Segment targets

Each customer segment (e.g., "Aerospace", "Bicycle Professional", "MRO Critical") can have its own fill-rate target and optional budget cap.

Viewing segment results

In the Segments tab of any scenario, you see:

Column Description
Segment Segment name
Target FR Fill-rate target (%)
Achieved FR Fill rate computed by the optimizer
Budget Inventory value allocated to this segment
Budget Cap Maximum investment (if set)
Met? Whether the target was achieved

If a segment shows "Not met", the optimizer ran out of budget before reaching the fill-rate target. Either increase the budget cap for that segment, or accept the lower fill rate and reduce the target.

Configuring segment targets

Go to the Configuration tab and click Edit segment parameters. For each segment you can set:

Parameter Description
fill_rate_target Target fill rate (0–1, e.g., 0.95 for 95 %)
max_budget Maximum inventory investment in currency units (leave blank for unlimited)
demand_multiplier Scale demand up/down for this segment (default 1.0)
lead_time_multiplier Scale lead times (default 1.0)
sku_min_fill_rate / sku_max_fill_rate Per-SKU fill-rate floor / ceiling
sku_min_sl_qty / sku_max_sl_qty Min / Max safety-quantity bounds
sku_min_sl_slices / sku_max_sl_slices Min / Max safety cover expressed as lead-time slices
use_existing_inventory Credit on-hand inventory against the safety-stock target

Reverse-logistics parameters (return rate, repair yield, repair TAT, WIP) are not set per segment — they come from RETURN/REPAIR supply routes. See MEIO parameters.


Running MEIO

  1. Select your pipeline in the left toolbar.
  2. Go to Processes (/processes).
  3. Find the MEIO Safety Stock step.
  4. Click Run.

The runner will: 1. Refresh demand statistics from the latest actuals and indirect demand. 2. Load all SKU data (item master, routes, BOM, fitted distributions). 3. Apply scenario overrides. 4. Call the Rust optimizer. 5. Save results to meio_results and meio_group_results. 6. Show a summary in the process log.

Expected runtime: 10–120 seconds depending on portfolio size.

From the MEIO Scenarios page (single scenario)

On the MEIO Scenarios list, click the Run button next to a specific scenario. This runs that scenario only, bypassing the full pipeline. Useful for: - Quick what-if scenario testing - Re-running a single scenario after parameter changes - Iterative configuration tuning without re-running forecasting

Checking run status

After triggering a run, the Process Log (at /logs) shows live progress with timing for each step. Look for lines like:

>> END    refresh_item_indirect_stats -- 2.1s
>> END    _load_sku_records -- 4.8s
>> END    meio_optimizer.run_optimization_batch -- 18.3s

When the Rust optimizer finishes, results are immediately available in the MEIO Scenarios page.


Configuring scenarios

Base scenario

Every tenant has exactly one base scenario (is_base = TRUE). This is the default scenario that runs when no specific scenario is configured in the pipeline. The base scenario uses the global MEIO parameters from scenario.parameters.

Creating a new scenario

On the MEIO Scenarios list, click + New Scenario. You can:

  1. Name the scenario (e.g., "High Service FY2026").
  2. Set global demand/lead-time multipliers to test sensitivity.
  3. Set per-segment overrides to give different service levels to different customer groups.
  4. Link the scenario to a pipeline via the ScenarioPlanner.

Scenario parameter overrides

The scenario configuration supports a hierarchy of overrides:

  1. Per-segment overrides (most specific): fill-rate target, budget, demand multiplier, lead-time multiplier, SKU min/max fill rate, SKU min/max safety qty & slices, use-existing-inventory.
  2. Global scenario overrides: applied to all segments not specifically overridden.
  3. Item-level defaults from the item master.
  4. Route-level defaults from replenishment routes (reverse-logistics parameters come exclusively from RETURN/REPAIR routes).

Linking a scenario to a pipeline

In the ScenarioPlanner (/scenarios), select your pipeline and set the MEIO Scenario dropdown to the desired scenario. From that point, every MEIO run triggered by that pipeline will use that scenario.

Figure: lifecycle of a MEIO scenario run, from configuration through review and override.

stateDiagram-v2
  [*] --> Configured: scenario created & linked to pipeline
  Configured --> Running: planner triggers run
  Running --> Completed: optimizer finishes
  Completed --> Reviewed: planner reviews heatmap & segments
  Reviewed --> Overridden: planner sets SKU/segment override
  Overridden --> Configured: re-run with new params
  Reviewed --> [*]: accepted as plan

Interpreting the MEIO output in practice

Example: central warehouse has high wait time

You notice that your central distribution centre (site: "Paris DC") has a wait time of 4 weeks for Item X. This means:

  • When Paris DC is out of stock, downstream regional sites wait 4 weeks for replenishment beyond their normal transit time.
  • The root cause: Paris DC's committed buffer is low relative to its demand variability.
  • Action: Increase Paris DC's committed buffer for Item X, or investigate why the lead time from the supplier is so long.

Example: a segment consistently fails to meet its fill-rate target

Segment "Aerospace Critical" has a fill-rate target of 99 % but achieves only 95 %. Two possible causes:

  1. Budget cap too low: the max_budget for this segment is constraining the optimizer. Check the Segments tab — if the budget is exhausted before the target is met, increase max_budget.
  2. Demand too intermittent: for very slow-moving items with high demand variability, very large buffers are needed to achieve 99 %. Consider whether 99 % is realistic for these items, or configure specific items with a lower target using SKU-level overrides.

Example: marginal value is zero for many items

If many items show marginal value = 0, it means the optimizer has fully satisfied their group targets with no budget constraint. This is a healthy state — it means service-level targets are achievable within budget.

If the marginal value is very high for many items, it means there is a budget shortage — more investment would yield significant service improvements. Use this to quantify the cost of a service-level increase when presenting to management.


What the committed buffer means in practice

The committed buffer is an inventory policy target, not a direct purchase order quantity. It tells your planning system:

  • Minimum on-hand target: always aim to have at least committed_buffer units at this location.
  • Reorder point component: ROP = expected_LT_demand + committed_buffer.
  • Safety stock for supply planning: the supply runner uses the committed buffer when generating planned orders in the supply module.

The buffer does not tell you the order quantity (that is the EOQ/K-Curve output). It tells you when to order and the minimum stock to protect against uncertainty.


K-Curve Order Plan (Optimised Order Plan tab)

The K-Curve workbench in the MEIO screen includes an Optimised Order Plan panel showing the per-item EOQ results from the portfolio-optimised K-Curve run.

New features:

  • Item search filter — type any part of the item name, site name, or unique ID to narrow the list. The count display updates to show filtered / total (e.g. 12 / 600 items). Click ✕ clear to reset.
  • Clickable rows — click any item row to jump directly to its Time Series Viewer with the EOQ tab pre-selected, so you can immediately review the order quantity in context.
  • Order-frequency constraints toggle — an "Apply order-frequency constraints" checkbox controls whether the K-Curve operates on the constrained frontier. When enabled, the exchange curve shows an orange stepped overlay, and feasible k alternatives appear as clickable buttons below the KPI cards.
  • Expandable frequency options — items with frequency constraints show a ▶ indicator; click the row to expand a table of all allowed frequencies (orders/year, Q, cycle stock, total cost, K equivalent) with the selected one highlighted.

K-Curve EOQ vs classic EOQ

The order quantities shown here are portfolio-optimised — computed jointly across all items under a shared capital constraint. They differ from the classic per-item EOQ formula \(\sqrt{2DS/H}\) which treats each item independently.

Supply planning uses K-Curve EOQ (sourced from inventory_scenario.results) when a pipeline is linked to an inventory scenario. The classic EOQ (stored in item.eoq) is used only as a fallback when no scenario is linked.

When order-frequency constraints are active, each item's EOQ is snapped to the nearest allowed replenishment frequency (e.g., monthly, quarterly). The constrained flag in the per-item result indicates whether the snap changed the quantity.


Override mechanism

EOQ and fill-rate overrides

In the IO tab (Detail IO view for any series), planners can override: - The MEIO committed buffer directly (set a fixed buffer regardless of what the optimizer computed) - The fill-rate target for a specific item

These overrides are stored in the exceptions table and take priority over the MEIO optimizer output. An override carries a reason code, author, and expiry date for full auditability.

Absolute stock lower bounds

In the scenario configuration, you can set sku_min_fill_rate or sku_min_sl_qty for critical items. These are enforced before the greedy optimization begins — no matter what the budget situation is, these items will always receive at least enough buffer to meet their minimum. Use this for items where a stockout has severe operational or safety consequences.


Reverse logistics

If your network includes items that are returned (rented equipment, repairable spare parts), MEIO accounts for this automatically when RETURN or REPAIR routes are defined in the route table.

The optimizer reduces the net demand requirement:

If 60 % of units are returned and 80 % of those are repairable, only 52 % of gross demand must be sourced externally.

This significantly reduces required committed buffers for high-return-rate items. If your MEIO buffers seem higher than expected for repairable items, check that the RETURN/REPAIR routes are correctly configured with accurate return rates and repair yields.


Asset mode (MRO high-value items)

For high-value assets managed as a pool (e.g., aircraft engines, MRO equipment), MEIO uses a different calculation based on availability rather than fill rate. Instead of asking "what fraction of demand can we fill?", it asks "what is the probability that at least one spare is available when needed?"

This mode activates automatically for scenarios with service_level_type = 'availability'. The optimizer uses the Erlang-B formula to determine the minimum pool size (committed buffer = number of spare assets) needed to achieve the target availability.

For MRO industries, the MEIO Scenarios page will show availability (not fill rate) as the performance metric for asset-mode items.


Common configuration mistakes

Mistake: no segment assignment

If an item does not belong to any segment, it has no fill-rate target. The MEIO optimizer will not allocate any buffer to it. Check that all items have at least one segment membership. The MEIO Diagnostic endpoint (GET /api/meio/diagnostic) reports the count of "zero-buffer SKUs with no target group" to help identify this issue.

Mistake: budget cap too low

If max_budget is set very low for a segment, the optimizer will exhaust the budget immediately and leave many items with zero or minimal buffer. The resulting achieved fill rate will be far below target. Either increase the budget or reduce the fill-rate target.

Mistake: distribution fitting not run

MEIO requires fitted demand distributions to compute fill rates. If "Distributions" step has not been run for the current pipeline, MEIO falls back to Normal distributions with the Poisson-floor variance. This fallback is functional but less accurate for non-symmetric demand patterns. Always run the full pipeline (ETL → Forecasting → Distributions → MEIO) in order.

Mistake: running MEIO before forecast netting

If the forecast-netting step has not been run, the EOQ calculation uses historical actuals instead of future forecasts. This may cause the MEIO optimizer to size buffers based on past demand patterns rather than anticipated demand. For new product introductions or items with significant demand trend, always run netting before MEIO.


Fill-Rate Heatmap & KPI

The IO tab on the Dashboard (route /) displays the MEIO fill-rate heatmap and KPI cards. The data comes from the /api/meio/fr-heatmap endpoint.

Heatmap Weighting Method

The meio.heatmap_weight_method setting (Settings → Business Config → MEIO) controls how fill rates are averaged across SKUs in each heatmap cell:

Method Formula Notes
demand_rate (default) Σ(fill_rate × direct_demand_rate) / Σ(direct_demand_rate) Matches the optimiser's own group-level computation. SKUs with zero demand rate are excluded.
fcst_12m Σ(fill_rate × fcst_12m) / Σ(fcst_12m) Includes all SKUs weighted by forecast volume. May differ from the optimiser's reported group fill rate.

Fill Rate KPI — Computation & Fallbacks

The Fill Rate KPI value shown at the top of the IO tab is resolved in this priority order:

  1. Optimiser group result — The achieved_fill_rate from PIPE_meio_group_results in ClickHouse, resolved as:
  2. The "Default" group (when no custom segments are configured)
  3. The average of all non-"All" segment groups with achieved_fill_rate > 0 (when custom segments exist)
  4. The "All" group as a last resort
  5. SKU-level backend fallback — When the optimiser group results are unavailable or all-zero, the backend computes a weighted average from the same SKU rows used for the heatmap, using the configured heatmap_weight_method.
  6. SKU-level frontend fallback — As a final safety net, the frontend computes a fcst_12m-weighted average from the SKU-level fill_rate data loaded for the heatmap display.

The target fill rate (tgt) comes from the MEIO scenario's parameter set (fill_rate_target), defaulting to 95 %.


API endpoints reference

For developers integrating with MEIO programmatically:

Endpoint Description
GET /api/meio/scenarios List all MEIO scenarios
POST /api/meio/scenarios Create a new scenario
PUT /api/meio/scenarios/{id} Update scenario configuration
GET /api/meio/scenarios/{id}/results Get SKU and group results
POST /api/meio/scenarios/{id}/run Trigger a single-scenario run
GET /api/meio/scenarios/{id}/segment-params Get per-segment overrides
PUT /api/meio/scenarios/{id}/segment-params Upsert per-segment overrides
GET /api/meio/fr-heatmap Fill-rate heatmap across scenarios and segments
GET /api/meio/diagnostic Diagnostic summary (coverage, zero-buffer count)
POST /api/pipeline/run/meio Run MEIO as part of pipeline

Cross-references: MEIO Theory · Safety Stock · Distribution Fitting · K-Curve · EOQ