Skip to content

Instantly share code, notes, and snippets.

@achal7
Created May 12, 2026 18:28
Show Gist options
  • Select an option

  • Save achal7/62a5ccd69f97fa58c403263ee28cc50b to your computer and use it in GitHub Desktop.

Select an option

Save achal7/62a5ccd69f97fa58c403263ee28cc50b to your computer and use it in GitHub Desktop.
Documents
# Medhāvī ProductionPlanning - Project Description Document (PDD)
**Product**: Medhāvī ProductionPlanning
**Version**: 1.0
**Date**: September 2025
**Author**: Medhāvī Development Team
**Status**: Draft - Architecture Definition Phase
---
## Table of Contents
1. [Introduction](#1-introduction)
1.1 [Objectives](#11-objectives)
1.2 [PDD Structure](#12-pdd-structure)
1.3 [What to Avoid](#13-what-to-avoid)
1.4 [Keep it Simple](#14-keep-it-simple)
2. [Expectations](#2-expectations)
2.1 [Results](#21-results)
2.2 [In Scope](#22-in-scope)
2.3 [Out of Scope](#23-out-of-scope)
2.4 [Performance/Usability](#24-performanceusability)
2.5 [Technology](#25-technology)
2.6 [Miscellaneous](#26-miscellaneous)
3. [Business Goals](#3-business-goals)
3.1 [Goal 1: Optimized Resource Utilization](#31-goal-1-optimized-resource-utilization)
3.2 [Goal 2: Just-in-Time Material Management](#32-goal-2-just-in-time-material-management)
3.3 [Goal 3: Advanced Scheduling Optimization](#33-goal-3-advanced-scheduling-optimization)
4. [Scope and Planning Decisions Overview](#4-scope-and-planning-decisions-overview)
4.1 [Functional Application Architecture](#41-functional-application-architecture)
4.2 [Event Processing Flow](#42-event-processing-flow)
4.3 [Planning Decisions Overview](#43-planning-decisions-overview)
4.4 [KPI Matrix](#44-kpi-matrix)
5. [Scope and Planning Decisions Overview](#5-scope-and-planning-decisions-overview)
5.1 [Application Architecture: Module Overview](#51-application-architecture-module-overview)
5.2 [Core Modules Detailed](#52-core-modules-detailed)
5.3 [Event Processing Flow](#53-event-processing-flow)
6. [Planning Decision Details](#6-planning-decision-details)
6.0 [Material Availability Module](#60-material-availability-module)
6.1 [Order Acceptance (Promise/ATP)](#61-order-acceptance-heuristic-atpctp-lite)
6.2 [Material Requirements Planning (MRP)](#62-material-requirements-planning-mrp)
6.3 [Capacity CTP Module](#63-capacity-ctp-module)
6.4 [Transport ATP Module](#64-transport-atp-module)
6.5 [Capacity Assignment Decision](#65-capacity-assignment-finite-no-detailed-sequencing)
6.6 [Work Order Planning Decision](#66-work-order-planning)
6.7 [Material Reservations Module](#67-material-reservations-module)
6.8 [Material Replenishment Module](#68-material-replenishment-module)
6.9 [Work Order Planning](#69-work-order-planning)
6.10 [Routing & Process Management](#610-routing--process-management)
6.11 [Transport Management](#611-transport-management)
6.12 [Supplier Management](#612-supplier-management)
6.13 [Pegging & Traceability](#613-pegging--traceability)
6.14 [Campaign Management Decision](#614-campaign-management-no-batching)
6.15 [Replanning & What-if](#615-replanning--what-if)
6.16 [Multi-Objective Optimization Module](#616-multi-objective-optimization-module)
6. [Validation & Quality Assurance](#6-validation--quality-assurance)
6.1 [Validation Approach](#61-validation-approach)
6.2 [Development Best Practices](#62-development-best-practices)
7. [Other Functionality](#7-other-functionality)
7.1 [System Health Monitoring](#71-system-health-monitoring)
7.2 [Configuration Management](#72-configuration-management)
7.3 [Audit Logging](#73-audit-logging)
8. [Application/IT Environment](#8-applicationit-environment)
8.1 [Scale and Performance](#81-scale-and-performance)
8.2 [Security Provisioning](#82-security-provisioning)
9. [Integration](#9-integration)
9.1 [General](#91-general)
9.2 [Nexus Integration](#92-nexus-integration)
9.3 [ERP Integration](#93-erp-integration)
9.4 [MES Integration](#94-mes-integration)
10. [Appendix A: Technical Architecture](#10-appendix-a-technical-architecture)
10.1 [System Components](#101-system-components)
10.2 [Data Flow Architecture](#102-data-flow-architecture)
10.3 [Scalability Analysis](#103-scalability-analysis)
11. [Appendix B: Terminology](#11-appendix-b-terminology)
12. [Appendix C: Implementation Roadmap](#12-appendix-c-implementation-roadmap)
---
## 1. Introduction
### 1.1 Objectives
The main aim of this Project Description Document (PDD) is to describe the Medhāvī ProductionPlanning system that serves as the tactical planning engine for Advanced Planning & Scheduling (APS). This document translates our architectural vision into a technical implementation plan for finite capacity scheduling, material requirements planning, and work order optimization.
#### Stakeholder Perspective
Medhāvī ProductionPlanning empowers manufacturers to increase customer satisfaction, delivery performance, and profitability by streamlining complex tactical planning decisions. It balances demand and supply throughout the supply chain, ensuring finite planning of production orders and capacities.
#### Developer Perspective
ProductionPlanning is implemented on a modern event-sourcing foundation using F#/.NET with specialized optimization engines. All state changes are stored in EventStoreDB streams. ProductionPlanning subscribes to Nexus event streams and maintains domain aggregates for production orders, inventory, and resources.
### 1.2 PDD Structure
This PDD is organized around the core planning decisions and architectural components:
**Expectations**: Essential properties and requirements for the final solution
**Business Goals**: Measurable objectives that the system should achieve
**Scope Overview**: Functional architecture and planning decision flows
**Planning Decisions**: Detailed specifications for each decision point
**Technical Architecture**: System design and scalability considerations
### 1.3 What to Avoid
- Over-engineering for future requirements not yet validated
- Complex integrations before proving core planning capabilities
- Premature optimization without performance baselines
- Feature creep that delays the MVP delivery
### 1.4 Keep it Simple
Following the KIS principle:
- Start with core order acceptance and material planning
- Implement essential optimization algorithms
- Focus on reliable planning results before advanced features
- Validate each planning decision before adding complexity
---
## 2. Expectations
### 2.1 Results
The Medhāvī ProductionPlanning should deliver:
- Sub-500ms planning decision response times for 99th percentile
- 99.5% system availability with <2 hours annual downtime
- Zero data loss with guaranteed event persistence
- Optimal resource utilization across all production assets
- Seamless integration with existing ERP and MES systems
### 2.2 In Scope
- Finite capacity scheduling and optimization
- Material requirements planning (MRP) with multi-level BOM support
- Work order creation and sequencing
- Campaign management for batch production
- Real-time replanning based on disruptions
- Integration with Nexus for demand signals
- EventStoreDB for event sourcing and CQRS
- REST APIs for external system integration
- WebSocket support for real-time planning updates
### 2.3 Out of Scope
- Strategic planning (1-5 year horizons)
- Detailed shop floor scheduling and sequencing
- Master data management and quality control
- Advanced AI/ML model training (separate service)
- Multi-tenant isolation (single tenant for MVP)
- Legacy system modernization
- Mobile application development
### 2.4 Performance/Usability
**Computational Performance**
- Planning run execution: <30 minutes for full optimization
- Order acceptance: <5 seconds for individual orders (online heuristic)
- Schedule updates: <2 seconds for real-time adjustments
- What-if analysis: <10 seconds for scenario evaluation
- System startup: <60 seconds
**Scalability Requirements**
- Operations: Support for 50,000+ operations per planning run
- Resources: Handle 1,000+ resources with capacity constraints
- Time horizon: 3-6 month planning horizon
- Concurrent users: Support 50+ concurrent planning users
- BOM processing: Handle 10,000+ BOM levels efficiently
**Data Processing Requirements**
- Event throughput: Process 1,000+ planning events per minute
- Optimization: Solve complex scheduling problems in real-time
- Memory usage: <4GB under normal load
**User Experience**
- Intuitive web-based planning interface with drag-and-drop capabilities
- Real-time planning updates via WebSocket/SignalR
### 2.5 Technology
- **Runtime**: .NET 10.0, F# primary language
- **Optimization**: Custom algorithms with CPLEX/OR-Tools integration
- **Event Storage**: EventStoreDB for event persistence
- **Web Framework**: ASP.NET Core with SignalR
- **Database**: PostgreSQL for read models and optimization data
- **Deployment**: Docker containers with Kubernetes orchestration
### 2.6 Miscellaneous
- Open-source licensing (MIT)
- Comprehensive logging and monitoring
- Automated testing with >95% coverage
- CI/CD pipeline with automated deployment
- Documentation and API specifications
---
## 3. Business Goals
### 3.1 Goal 1: Optimized Resource Utilization
**Definition**: Maximize the utilization of production resources (machines, labor, tooling) while respecting capacity constraints and minimizing bottlenecks.
**Motivation**: Efficient resource utilization directly impacts profitability and delivery performance. Under-utilized resources represent wasted investment, while over-utilization leads to quality issues and maintenance problems.
**Current State**: Manual scheduling with 70-80% average utilization
**Target State**: AI-optimized scheduling with 85-95% utilization
**Success Metric**: 90% average resource utilization with <5% bottleneck occurrences
**Technical Success Metrics**
- Schedule quality: 95%+ constraint satisfaction, 90%+ resource utilization
- Optimization accuracy: 85%+ improvement over manual planning
- System reliability: 99.5% uptime for planning operations
- Replanning speed: <2 seconds for schedule adjustments
### 3.2 Goal 2: Just-in-Time Material Management
**Definition**: Maintain optimal inventory levels to support production while minimizing carrying costs and stockouts.
**Motivation**: Proper inventory management ensures production continuity while reducing working capital requirements. Just-in-time principles eliminate waste and improve cash flow.
**Current State**: Manual inventory planning with 20-30% excess inventory
**Target State**: Automated MRP with 95% on-time material availability
**Success Metric**: 98% material availability with <10% excess inventory
**Business Success Metrics**
- Inventory optimization: 98% material availability with <10% excess inventory
- Planning efficiency: 20% reduction in planning effort and time
- Cost reduction: 25% reduction in production costs through optimization
### 3.3 Goal 3: Advanced Scheduling Optimization
**Definition**: Generate optimal production schedules that balance multiple competing objectives including delivery dates, costs, and resource efficiency.
**Motivation**: Complex scheduling decisions require sophisticated algorithms to consider all constraints and objectives simultaneously. Manual scheduling cannot achieve the same level of optimization.
**Current State**: Manual scheduling with frequent replanning
**Target State**: Automated optimization with real-time adjustments
**Success Metric**: 95% on-time delivery with 20% reduction in planning effort
**Business Success Metrics**
- On-time delivery: 95% on-time delivery achievement
- Resource utilization: 90% average utilization across production resources
- Planning efficiency: 20% reduction in planning effort and time
---
## 4. Core Decision Framework (Shared Building Blocks)
This section captures shared logic across ATP/CTP, material/capacity/transport/supplier services, and optimizer reuse. Keep core policies, scoring, limiters, reservations, and cross-cutting concerns here to avoid duplicating business rules.
### 4.1 What Belongs Here
- Policies & SLA presets (time vs cost vs risk caps, buffers, FullOrder/FullDelivery)
- Scoring primitives for routings, itineraries, supplier options (time/cost/risk/CO2)
- Limiters/reason catalog + ProviderError → Limiter mapping
- Reservations lifecycle contracts (create/confirm/release/expire), deterministic IDs
- Time/FX/cost normalization helpers (UTC, as-of rates, stale-rate guard)
- Routing/alternate selection primitives (policy-driven)
- Transport/supplier feasibility & scoring primitives (cutoff/capacity/MOQ/reliability)
- Telemetry hooks (latency, at-risk, cache hit/miss, provider errors)
- Concurrency/degradation patterns (agent wrapper, error-to-limiter)
- ML hooks (lead-time/reliability predictors with basis/version)
- Tenant scoping and UTC normalization (all timestamps UTC; apply tenant policy presets)
- Cache invalidation events (calendars, legs, reservations, allocations)
- Idempotency guidance (if upstream broker provides idempotency/checkpoints, note boundaries)
### 4.2 Shared Types (Code Structure)
- `Common/PromiseTypes.fs` (exists): limiter domains/codes, policies, provider contracts, request/response, reservations, routing choice, itineraries
- `Common/PromisePolicies.fs` (added): default policy, risk-basis helper, policy merge
- `Common/PromiseScoring.fs` (added): scoring helpers for itineraries, suppliers, routings
- `Common/PromiseLimiter.fs` (added): ProviderError → Limiter mapping
- `Common/CostFx.fs` (added): cost breakdown, FX-as-of with stale guard
- `Common/TimeWindow.fs` (added): window overlap/containment, slack, cutoff helper
- `Common/ValidationHelpers.fs` (added): transport feasibility, supplier MOQ/lead, safety check, apply risk
- `Common/TelemetryContracts.fs` (added): standard telemetry events/contracts
### 4.3 How to Use
- Services (material, capacity, transport, supplier) and the Promise Orchestrator consume the shared types and helpers; inject provider implementations
- Optimizer reuses the same policies, scoring, limiters, and reservations contracts—no duplicated rules in solver code
- Light vs full: inject trivial providers (zero reservations/transport) for heuristic mode; richer providers for full ATP/CTP
### 4.4 Concurrency / Reliability
- Optional Mailbox/agent wrapper to serialize promise requests; reservations are idempotent to avoid race issues
- ProviderError maps to Limiter with retry/alternate suggestions; degrade gracefully, never leave dangling reservations
### 4.5 Telemetry / KPIs
- Use TelemetryProvider (or TelemetryContracts) for latency per provider, errors, cache hit/miss, timeouts, promise accuracy/at-risk
### 4.6 ML Integration (Pattern)
- Providers call ML predictors (lead time/reliability), include basis/version in responses; orchestrator enforces RiskBasis (p50/p95)
- On ML failure, return ProviderError and let limiter surface with retry/heuristic suggestion
### 4.7 Examples
- Policy preset: Gold customer → TimePreference=Fastest, RiskPreference=P95, CostCap=None, CallSupplierOnShortfall=true
- Cache bust: ResourceCalendar updated → emit CalendarChanged event → capacity cache invalidated and rebuilt
- Limiter mapping: Transport provider timeout → Limiter.Domain=Transport, Code=SearchTimeout, Suggestions=[retry,fallbackHeuristic]
---
## 5. Scope and Planning Decisions Overview
### 5.1 Application Architecture: Module Overview
Medhāvī ProductionPlanning consists of **8 core modules** that work together to provide comprehensive Advanced Planning & Scheduling (APS) capabilities.
#### 5.1.1 Module Architecture Diagram
```
┌─────────────────────────────────────────────────────────────────────────┐
│ MEDHĀVĪ PRODUCTIONPLANNING │
│ (APS Engine - 8 Core Modules) │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────────────────────────────────────────────────────────┐ │
│ │ 1. MATERIAL AVAILABILITY (Knowledge/Query Service) │ │
│ │ Purpose: Provide material position information │ │
│ │ Role: Queries projections, calculates net available │ │
│ │ No decisions, just information │ │
│ └──────────────────────────────────────────────────────────────────┘ │
│ │ │
│ │ Used by │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────────────┐ │
│ │ 2. MATERIAL RESERVATION (Hold/Commit Material) │ │
│ │ Purpose: Hold material for orders/promises │ │
│ │ Lifecycle: Tentative → Confirmed → Released/Expired │ │
│ │ Input for: Material Availability, MRP │ │
│ └──────────────────────────────────────────────────────────────────┘ │
│ │ │
│ │ Used by │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────────────┐ │
│ │ 3. CAPACITY CTP (Capacity Checking Service) │ │
│ │ Purpose: Check capacity availability for production │ │
│ │ Owns: Work Routing knowledge (for capacity checking) │ │
│ │ Used by: Promise, MRP, Optimizer │ │
│ └──────────────────────────────────────────────────────────────────┘ │
│ │ │
│ │ Used by │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────────────┐ │
│ │ 4. TRANSPORT ATP (Transport Availability Service) │ │
│ │ Purpose: Check transport/logistics availability │ │
│ │ Owns: Transport Routing knowledge │ │
│ │ Used by: Promise, Transport Planning │ │
│ └──────────────────────────────────────────────────────────────────┘ │
│ │ │
│ │ Used by │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────────────┐ │
│ │ 5. PROMISE/ATP (Order Promising - Real-Time) │ │
│ │ Purpose: Answer "When can I deliver THIS order?" │ │
│ │ Speed: Sub-30s response (real-time, per-order) │ │
│ │ Uses: Material Availability, Reservations, Capacity CTP, │ │
│ │ Transport ATP │ │
│ │ Creates: Tentative reservations │ │
│ └──────────────────────────────────────────────────────────────────┘ │
│ │ │
│ │ Triggers │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────────────┐ │
│ │ 6. MRP (Material Requirements Planning - Batch) │ │
│ │ Purpose: Plan "How to fulfill ALL orders?" │ │
│ │ Speed: 30min-2hr (batch, all orders) │ │
│ │ Uses: Material Availability, Reservations, BOM, │ │
│ │ Capacity CTP (for finite capacity MRP) │ │
│ │ Generates: Supply Orders (PO/WO/TO) │ │
│ │ Sub-Modules: │ │
│ │ - Heuristic MRP (fast, daytime) │ │
│ │ - Material Replenishment (triggers MRP on shortfall) │ │
│ └──────────────────────────────────────────────────────────────────┘ │
│ │ │
│ │ Used by │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────────────┐ │
│ │ 7. MATERIAL REPLENISHMENT (Inventory Management) │ │
│ │ Purpose: Maintain stock levels (min/max/safety) │ │
│ │ Trigger: Stock falls below minimum │ │
│ │ Action: Triggers MRP when shortfall detected │ │
│ │ Supply-driven planning (independent of customer orders) │ │
│ └──────────────────────────────────────────────────────────────────┘ │
│ │ │
│ │ Can use │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────────────┐ │
│ │ 8. OPTIMIZATION (Multi-Objective Optimizer) │ │
│ │ Purpose: Improve planning results (better than heuristics) │ │
│ │ Scope: Material + Capacity + Transport (cross-domain) │ │
│ │ When: Nightly batch run (replaces heuristic results) │ │
│ │ Uses: MRP functions, Capacity CTP, Transport ATP │ │
│ │ Reuses: Same functions as heuristic (no duplication) │ │
│ └──────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
#### 5.1.2 Module Relationships & Data Flow
```
┌─────────────────────────────────────────────────────────────────────────┐
│ MODULE INTERACTIONS │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ Material Availability │
│ ↓ (queries) │
│ Material Reservation ← (subtracts from availability) │
│ ↓ (used by) │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ Promise │ │ MRP │ │
│ │ (Real-Time) │ │ (Batch) │ │
│ └────────┬────────┘ └────────┬────────┘ │
│ │ │ │
│ │ Uses │ Uses │
│ ▼ ▼ │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ Capacity CTP │ │ Capacity CTP │ │
│ │ (Shared) │ │ (Shared) │ │
│ └─────────────────┘ └─────────────────┘ │
│ │ │ │
│ │ Uses │ │
│ ▼ │ │
│ ┌─────────────────┐ │ │
│ │ Transport ATP │ │ │
│ └─────────────────┘ │ │
│ │ │
│ │ Can trigger │
│ ▼ │
│ ┌─────────────────────────────────┐ │
│ │ Material Replenishment │ │
│ │ (Triggers MRP on shortfall) │ │
│ └─────────────────────────────────┘ │
│ │ │
│ │ Can use │
│ ▼ │
│ ┌─────────────────────────────────┐ │
│ │ Optimization │ │
│ │ (Uses MRP functions + others) │ │
│ └─────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
#### 5.1.3 Implementation Order
The modules should be implemented in the following order to respect dependencies:
```
1. Material Availability
└─> No dependencies (foundation)
2. Material Reservation
└─> Depends on: Material Availability
3. Capacity CTP (can be parallel with Transport ATP)
└─> Depends on: Work Routing (master data), Resource Calendars
4. Transport ATP (can be parallel with Capacity CTP)
└─> Depends on: Transport Routing (master data), Transport Legs
5. Promise
└─> Depends on: Material Availability, Material Reservation,
Capacity CTP, Transport ATP
6. MRP
└─> Depends on: Material Availability, Material Reservation,
Capacity CTP, BOM
7. Material Replenishment
└─> Depends on: MRP (triggers MRP on shortfall)
8. Optimization
└─> Depends on: MRP functions, Capacity CTP, Transport ATP
(reuses functions, separate module)
```
#### 5.1.4 Module Responsibilities Summary
| Module | Purpose | Speed | Scope | Creates |
| -------------------------- | -------------------------- | --------- | --------------- | ------------------------ |
| **Material Availability** | Knowledge/Query Service | Instant | Per query | Information only |
| **Material Reservation** | Hold/Commit Material | Instant | Per reservation | Reservations |
| **Capacity CTP** | Capacity Checking Service | Seconds | Per work order | Capacity availability |
| **Transport ATP** | Transport Checking Service | Seconds | Per route | Transport availability |
| **Promise** | Order Promising | Sub-30s | Per order | Tentative reservations |
| **MRP** | Material Planning | 30min-2hr | All orders | Supply orders (PO/WO/TO) |
| **Material Replenishment** | Inventory Management | Minutes | Per product/SP | Triggers MRP |
| **Optimization** | Plan Optimization | Hours | All orders | Optimized plan |
---
### 5.2 Core Modules Detailed
This section provides detailed descriptions of each of the 8 core modules, their responsibilities, interfaces, and how they interact.
#### 5.2.1 Material Availability Module
**Purpose**: Knowledge/Query Service - Provides material position information without making decisions.
**Responsibilities**:
- ✅ Query Inventory projection (on-hand quantities)
- ✅ Query SupplyOrder projection (inbound supply orders)
- ✅ Query MaterialReservation projection (active reservations)
- ✅ Query Safety Policy (safety stock requirements)
- ✅ Calculate net available quantity
- ✅ Return material snapshot
**What It Does NOT Do**:
- ❌ Does NOT decide whether to import or produce
- ❌ Does NOT generate supply orders
- ❌ Does NOT make planning decisions
**Interface**:
```fsharp
type MaterialProvider = {
GetSnapshot: ProductId * StockingPointId * DateTimeOffset
-> Async<Result<MaterialSnapshot, ProviderError>>
}
type MaterialSnapshot = {
OnHand: decimal
Inbound: (DateTimeOffset * decimal) list
Reservations: decimal
Safety: decimal
}
```
**Usage**:
- Used by: Promise, MRP, Material Replenishment
- Called when: Need to know material availability
- Returns: Snapshot of material position (information only)
**Example**:
```fsharp
let snapshot = MaterialProvider.GetSnapshot("ProductX", "SP-1", Monday)
// Returns:
// {
// OnHand = 200.0m
// Inbound = [{ Date = Monday, Qty = 50 }]
// Reservations = 150.0m
// Safety = 20.0m
// }
// Net Available = 200 + 50 - 150 - 20 = 80 units
```
---
#### 5.2.2 Material Reservation Module
**Purpose**: Hold/Commit Material - Prevents double-booking and tracks committed material.
**Responsibilities**:
- ✅ Create tentative reservations (on promise)
- ✅ Confirm reservations (on order acceptance)
- ✅ Release reservations (on cancellation/rejection)
- ✅ Expire reservations (automatic cleanup)
- ✅ Reduce reservations (on quantity decrease)
**Lifecycle**:
```
Tentative → Confirmed → Released/Expired
↘ Reduced ↗
```
**Key Properties**:
- Simple contract: Product + Quantity + Time Window
- Does NOT store full inventory details (MaterialProvider knows this)
- Does NOT specify which order it's for (Pegging does this)
- Subtracted in Material Availability calculations
- Subtracted in MRP netting calculations
**Interface**:
```fsharp
type ReservationProvider = {
CreateTentative: ReservationRequest -> Async<Result<ReservationId, ProviderError>>
Confirm: ReservationId -> Async<Result<unit, ProviderError>>
Release: ReservationId -> Async<Result<unit, ProviderError>>
Expire: ReservationId -> Async<Result<unit, ProviderError>>
Reduce: ReservationId * decimal -> Async<Result<unit, ProviderError>>
}
```
**Usage**:
- Used by: Promise (creates tentative), MRP (subtracts in netting)
- Input for: Material Availability, MRP
- Purpose: Prevent double-booking
---
#### 5.2.3 Capacity CTP Module
**Purpose**: Capacity Checking Service - Answers "When can I produce this product?"
**Responsibilities**:
- ✅ Own Work Routing knowledge (for capacity checking)
- ✅ Check capacity availability for routing steps
- ✅ Find earliest feasible capacity windows
- ✅ Handle alternate routings/resources
- ✅ Return capacity availability results
**Key Design Decision**:
- **Owns Routing Knowledge**: Capacity CTP looks up routing internally
- **MRP Interface**: MRP just asks "can I produce Product X?" (no routing knowledge needed)
- **Better Separation**: MRP doesn't need routing details
**Interface**:
```fsharp
type CapacityCTP = {
CheckCapacity: ProductId * decimal * DateTimeOffset
-> Async<Result<CapacityCheckResult, CapacityError>>
}
type CapacityCheckResult = {
EarliestAvailable: DateTimeOffset
CapacityAvailable: bool
ResourceAssignments: ResourceAssignment list
Limiter: PromiseLimiter option
}
```
**Internal Process**:
```
1. Receive: ProductId, Quantity, Need Date
2. Look up Work Routing from master data (internal)
3. Get routing steps
4. For each routing step:
- Get resource group from step
- Check capacity for that resource
- Find earliest feasible window
5. Calculate earliest capacity-ready date
6. Return result
```
**Usage**:
- Used by: Promise (real-time), MRP (batch), Optimizer
- Shared service: Same Capacity CTP used by all modules
- Purpose: Check if production capacity is available
---
#### 5.2.4 Transport ATP Module
**Purpose**: Transport Availability Service - Answers "Can I ship this product on time?"
**Responsibilities**:
- ✅ Own Transport Routing knowledge (for transport checking)
- ✅ Check transport leg availability
- ✅ Find earliest feasible transport itineraries
- ✅ Check capacity, cutoffs, constraints
- ✅ Return transport availability results
**Key Design Decision**:
- **Owns Transport Routing Knowledge**: Transport ATP looks up transport routing internally
- **Used by**: Promise (for transport ATP), Transport Planning (for logistics)
**Interface**:
```fsharp
type TransportATP = {
GetOptions: StockingPointId * StockingPointId * DateTimeOffset
-> Async<Result<TransportItinerary list, TransportError>>
}
type TransportItinerary = {
Legs: TransportLeg list
Arrival: DateTimeOffset
Cost: decimal
Reliability: float
CO2: decimal option
}
```
**Usage**:
- Used by: Promise (for transport ATP), Transport Planning
- Purpose: Check if transport/logistics is available
---
#### 5.2.5 Promise/ATP Module
**Purpose**: Order Promising - Real-time, per-order, customer-facing (Sub-30s response)
**Responsibilities**:
- ✅ Answer "When can I deliver THIS order?"
- ✅ Check material, capacity, transport availability
- ✅ Create tentative reservations
- ✅ Return promise date with confidence and cost
**Key Characteristics**:
- **Real-Time**: Sub-30s response (customer-facing)
- **Per-Order**: One order at a time
- **Does NOT**: Generate supply orders (that's MRP's job)
- **Does NOT**: Call MRP (too slow, different purpose)
**Process Flow** (Detailed):
```
┌─────────────────────────────────────────────────────────────┐
│ PROMISE ORCHESTRATOR │
│ (Real-Time, Per-Order, Sub-30s) │
│ │
│ Input: Customer Order (Product, Quantity, Due Date) │
│ │
│ Step 1: ROUTING SELECTION ← FIRST! │
│ └─> Call RoutingProvider.Select(ProductId, SP, Policy) │
│ └─> Returns: Primary Routing + Alternates │
│ └─> Routing contains: │
│ - Work Routing Steps (for capacity checking) │
│ - Transport Routing (for shipping) │
│ - Qty-dependent durations │
│ │
│ Step 2: MATERIAL ATP │
│ └─> Call MaterialProvider.GetSnapshot() │
│ └─> Calculate: netAvailable = onHand + inbound - │
│ reservations - safety │
│ └─> If shortfall and policy allows: │
│ - Call SupplierProvider.GetSupplierOptions() │
│ └─> Result: Earliest material-ready date │
│ │
│ Step 3: CAPACITY ATP (Uses Work Routing from Step 1) │
│ └─> For each routing step: │
│ - Get resource group from routing step │
│ - Call CapacityCTP.CheckCapacity() │
│ - Find earliest feasible window │
│ └─> Result: Earliest capacity-ready date │
│ │
│ Step 4: TRANSPORT ATP (Uses Transport Routing from Step 1) │
│ └─> Get origin/destination from routing │
│ └─> Call TransportATP.GetOptions() │
│ └─> Check capacity, cutoffs, constraints │
│ └─> Result: Earliest transport arrival date │
│ │
│ Step 5: CALCULATE PROMISE DATE │
│ └─> PromiseDate = MAX( │
│ materialReady, │
│ capacityReady, │
│ transportArrival │
│ ) │
│ └─> Bottleneck determines promise date │
│ │
│ Step 6: CREATE TENTATIVE RESERVATIONS │
│ └─> Material Reservation (if material available) │
│ └─> Capacity Reservation (if capacity available) │
│ └─> Transport Reservation (if transport available) │
│ │
│ Step 7: RETURN PROMISE RESPONSE │
│ └─> Decision: Accepted/Rejected │
│ └─> Promise Date (if accepted) │
│ └─> Limiter (if rejected, explains why) │
│ └─> Cost, Confidence, Reservations │
└─────────────────────────────────────────────────────────────┘
```
**Why Routing Selection First?**
- Capacity ATP needs routing steps to know which machines to check
- Transport ATP needs routing to know which transport route to check
- Routing provides context for all subsequent checks
**Integration Points**:
- Uses: Material Availability, Material Reservation, Capacity CTP, Transport ATP
- Creates: Tentative Reservations
- Does NOT: Call MRP (different purpose, too slow)
---
#### 5.2.6 MRP Module
**Purpose**: Material Requirements Planning - Batch planning for all orders (30min-2hr)
**Responsibilities**:
- ✅ BOM Explosion (multi-level material requirements)
- ✅ Material Netting (gross - available = net requirements)
- ✅ Finite Capacity Checking (via Capacity CTP)
- ✅ Generate Supply Orders (PO/WO/TO)
- ✅ Create Pegging Links (demand → supply traceability)
**Key Characteristics**:
- **Batch**: Plans all orders together (30 minutes to 2 hours)
- **Finite Capacity**: Checks capacity via Capacity CTP (doesn't assume infinite)
- **No Routing Knowledge**: Just asks Capacity CTP "can I produce?" (Capacity CTP owns routing)
- **Planning-Facing**: Internal planning process (not customer-facing)
**Process Flow** (Detailed):
```
┌─────────────────────────────────────────────────────────────┐
│ MATERIAL REQUIREMENTS PLANNING (MRP) │
│ (Batch, All Orders, 30min-2hr) │
│ │
│ Input: All Customer Orders, Forecasts │
│ │
│ Step 1: BOM EXPLOSION │
│ └─> For each order: │
│ - Explode BOM recursively (multi-level) │
│ - Calculate gross requirements per component │
│ - Example: Product A (100 units) requires: │
│ * 200 units Component X │
│ * 100 units Component Y │
│ │
│ Step 2: MATERIAL NETTING │
│ └─> For each component: │
│ - Query Material Availability (on-hand + inbound) │
│ - Subtract Material Reservations (active) │
│ - Subtract Safety Stock │
│ - Calculate: NetReq = GrossReq - Available │
│ - Example: Component X │
│ * Gross: 200 units │
│ * Available: 50 units │
│ * Net: 150 units (need to order/produce) │
│ │
│ Step 3: GENERATE WORK ORDER PROPOSALS │
│ └─> For each net requirement: │
│ - Create Work Order Proposal │
│ - Product: Product A │
│ - Quantity: 100 units │
│ - Need Date: Jan 15 │
│ - NO routing knowledge needed! ✅ │
│ │
│ Step 4: CAPACITY CHECKING (Via Capacity CTP) │
│ └─> For each work order proposal: │
│ - Call CapacityCTP.CheckCapacity( │
│ productId: "ProductA", │
│ quantity: 100, │
│ needDate: Jan 15 │
│ ) │
│ - Capacity CTP internally: │
│ * Looks up Work Routing (its responsibility) │
│ * Gets routing steps │
│ * Checks capacity for each step │
│ * Returns: Earliest available date │
│ - Result: Jan 16 (capacity not available on Jan 15) │
│ │
│ Step 5: ADJUST WORK ORDER DATES │
│ └─> If capacity not available on need date: │
│ - Delay to earliest available date │
│ - Or try alternate routing (if policy allows) │
│ - Or reject if too late (per policy) │
│ │
│ Step 6: GENERATE SUPPLY ORDERS │
│ └─> Purchase Orders (for materials from BOM) │
│ - Component X: 150 units, need by Jan 10 │
│ └─> Work Orders (with capacity-checked dates) │
│ - Product A: 100 units, start Jan 16 │
│ └─> Transport Orders (if needed) │
│ │
│ Step 7: CREATE PEGGING LINKS │
│ └─> Link demand to supply (traceability) │
│ - Customer Order → Work Order │
│ - Work Order → Purchase Order │
│ │
│ Output: Supply Orders (PO/WO/TO) with realistic dates │
└─────────────────────────────────────────────────────────────┘
```
**Sub-Modules**:
- **Heuristic MRP**: Fast path (minutes per order, daytime)
- **Material Replenishment**: Triggers MRP when stock falls below minimum
**Integration Points**:
- Uses: Material Availability, Material Reservation, Capacity CTP, BOM
- Generates: Supply Orders (PO/WO/TO)
- Creates: Pegging Links
- Does NOT: Create reservations (that's Promise's job)
---
#### 5.2.7 Material Replenishment Module
**Purpose**: Inventory Management - Maintain stock levels (supply-driven planning)
**Responsibilities**:
- ✅ Monitor stock levels (current vs. targets)
- ✅ Detect shortfalls (below min/safety)
- ✅ Trigger MRP when shortfall detected
- ✅ Maintain stock levels independently of customer orders
**Key Characteristics**:
- **Supply-Driven**: Works on stock levels (not customer orders)
- **Triggers MRP**: When shortfall detected, calls MRP to arrange supply
- **Independent**: Can run alongside MRP (different planning approach)
**Process Flow**:
```
┌─────────────────────────────────────────────────────────────┐
│ MATERIAL REPLENISHMENT │
│ (Inventory Management - Supply-Driven) │
│ │
│ Step 1: MONITOR STOCK LEVELS │
│ └─> Check current stock: 50 units Product X │
│ │
│ Step 2: COMPARE TO TARGETS │
│ └─> Min Level: 100 units │
│ └─> Safety Stock: 20 units │
│ └─> Target: 100 units │
│ │
│ Step 3: CALCULATE SHORTFALL │
│ └─> Shortfall = 100 - 50 = 50 units │
│ └─> Need Date: Jan 15 (based on cover days) │
│ │
│ Step 4: TRIGGER MRP ← KEY ACTION! │
│ └─> Call MRP with: │
│ - Product: Product X │
│ - Quantity: 50 units │
│ - Need Date: Jan 15 │
│ - Source: Replenishment (not customer order) │
│ │
│ Step 5: MRP TAKES OVER │
│ └─> MRP does BOM explosion (if needed) │
│ └─> MRP checks Material Availability │
│ └─> MRP calculates net requirements │
│ └─> MRP checks Capacity (via Capacity CTP) │
│ └─> MRP generates Supply Orders │
│ │
│ Result: Supply orders generated to maintain stock levels │
└─────────────────────────────────────────────────────────────┘
```
**Integration Points**:
- Triggers: MRP (when shortfall detected)
- Uses: Material Availability (to check current stock)
- Purpose: Maintain stock levels (supply-driven)
---
#### 5.2.8 Optimization Module
**Purpose**: Multi-Objective Optimization - Improve planning results (better than heuristics)
**Responsibilities**:
- ✅ Optimize across material + capacity + transport together
- ✅ Multi-objective optimization (cost, time, CO2, utilization)
- ✅ Replace heuristic results with optimized plan
- ✅ Reuse MRP functions (no duplication)
**Key Characteristics**:
- **Cross-Domain**: Optimizes material + capacity + transport
- **Separate Module**: Not sub-module of MRP (optimizes more than just material)
- **Reuses Functions**: Calls MRP functions, Capacity CTP, Transport ATP
- **Nightly Batch**: Runs overnight to optimize all orders
**When to Run**:
- **Daytime**: Heuristic MRP (fast, per-order)
- **Nighttime**: Full Optimizer (better results, all orders)
**Process Flow**:
```
┌─────────────────────────────────────────────────────────────┐
│ OPTIMIZATION MODULE │
│ (Cross-Domain, Multi-Objective) │
│ │
│ Input: All Demands (from all orders) │
│ │
│ Step 1: REUSE MRP FUNCTIONS │
│ └─> Call MRP.bomExplosion() (reuse, no duplication) │
│ └─> Call MRP.materialNetting() (reuse) │
│ │
│ Step 2: REUSE CAPACITY CTP │
│ └─> Call CapacityCTP.CheckCapacity() (reuse) │
│ │
│ Step 3: REUSE TRANSPORT ATP │
│ └─> Call TransportATP.GetOptions() (reuse) │
│ │
│ Step 4: BUILD OPTIMIZATION MODEL │
│ └─> Decision Variables: │
│ - Production quantities per routing/step/time │
│ - Purchase quantities per supplier/time │
│ - Transport quantities per leg/time │
│ └─> Constraints: │
│ - Material balance │
│ - Capacity limits │
│ - Transport capacity │
│ - BOM relationships │
│ └─> Objectives: │
│ - Minimize lateness │
│ - Minimize cost │
│ - Maximize utilization │
│ - Minimize CO2 │
│ │
│ Step 5: SOLVE (MILP Solver) │
│ └─> Run CPLEX/OR-Tools solver │
│ └─> Find optimal solution │
│ └─> Fallback to heuristic if timeout │
│ │
│ Step 6: REPLACE HEURISTIC RESULTS │
│ └─> Replace heuristic supply orders with optimized │
│ └─> Update work order dates │
│ └─> Ready for next day execution │
│ │
│ Output: Optimized plan (better than heuristic) │
└─────────────────────────────────────────────────────────────┘
```
**Integration Points**:
- Reuses: MRP functions, Capacity CTP, Transport ATP
- Replaces: Heuristic MRP results (with optimized plan)
- Purpose: Improve overall plan quality
---
### 5.3 Complete Planning Flow: Promise → MRP → Optimization
This section shows how all modules work together in a complete planning cycle.
#### 5.3.1 Daytime Flow: Real-Time Promise
```
┌─────────────────────────────────────────────────────────────┐
│ DAYTIME: REAL-TIME PROMISE │
│ (Customer-Facing, Per-Order, Sub-30s) │
│ │
│ Customer Order Arrives: │
│ "I need 100 units Product A by Jan 15" │
│ │
│ Step 1: Promise Orchestrator │
│ └─> Routing Selection (Work + Transport) │
│ └─> Material ATP (via Material Availability) │
│ └─> Capacity ATP (via Capacity CTP) │
│ └─> Transport ATP (via Transport ATP) │
│ └─> Calculate Promise Date │
│ └─> Create Tentative Reservations │
│ │
│ Result: "Yes, Jan 15 is feasible" (30 seconds) │
│ → Customer accepts │
│ → Reservations confirmed │
└─────────────────────────────────────────────────────────────┘
```
#### 5.3.2 Nighttime Flow: Batch MRP
```
┌─────────────────────────────────────────────────────────────┐
│ NIGHTTIME: BATCH MRP │
│ (Planning-Facing, All Orders, 30min-2hr) │
│ │
│ MRP Runs (e.g., at midnight): │
│ └─> Takes ALL customer orders (from Promise) │
│ └─> Takes ALL forecasts │
│ │
│ Step 1: BOM Explosion (all orders) │
│ Step 2: Material Netting (all orders) │
│ Step 3: Generate Work Order Proposals (all orders) │
│ Step 4: Capacity Checking (via Capacity CTP, all orders) │
│ Step 5: Generate Supply Orders (PO/WO/TO) │
│ Step 6: Create Pegging Links │
│ │
│ Result: Complete supply plan for all orders │
│ → Supply orders ready for execution │
└─────────────────────────────────────────────────────────────┘
```
#### 5.3.3 Nighttime Flow: Full Optimization (Optional)
```
┌─────────────────────────────────────────────────────────────┐
│ NIGHTTIME: FULL OPTIMIZATION │
│ (Better Results, All Orders, Hours) │
│ │
│ Optimizer Runs (after MRP, e.g., 2am): │
│ └─> Takes ALL demands │
│ └─> Reuses MRP functions (BOM, netting) │
│ └─> Reuses Capacity CTP │
│ └─> Reuses Transport ATP │
│ └─> Optimizes across all domains together │
│ └─> Replaces heuristic results with optimized plan │
│ │
│ Result: Optimized plan (better than heuristic) │
│ → Ready for next day execution │
└─────────────────────────────────────────────────────────────┘
```
#### 5.3.4 Material Replenishment Flow (Independent)
```
┌─────────────────────────────────────────────────────────────┐
│ MATERIAL REPLENISHMENT (Independent) │
│ (Inventory Management, Supply-Driven) │
│ │
│ Replenishment Runs (e.g., hourly): │
│ └─> Monitors stock levels │
│ └─> Detects shortfall (below min/safety) │
│ └─> Triggers MRP (when shortfall detected) │
│ │
│ Result: Stock levels maintained │
│ → MRP generates supply orders to maintain stock │
└─────────────────────────────────────────────────────────────┘
```
#### 5.3.5 Key Distinctions
**Promise vs MRP**:
- **Promise**: Real-time (30s), per-order, customer-facing, creates reservations
- **MRP**: Batch (30min-2hr), all orders, planning-facing, generates supply orders
- **Both use**: Material Availability, Material Reservation, Capacity CTP
- **Promise does NOT call MRP** (too slow, different purpose)
**Heuristic MRP vs Optimizer**:
- **Heuristic MRP**: Fast (minutes), good enough, daytime
- **Optimizer**: Slower (hours), better results, nighttime
- **Optimizer replaces** heuristic results (doesn't run after MRP)
**Material Replenishment vs MRP**:
- **Material Replenishment**: Supply-driven, maintains stock levels, triggers MRP
- **MRP**: Demand-driven, fulfills specific orders, generates supply orders
---
### 5.4 Event Processing Flow
1. **Demand Ingestion**: Receive demand signals from Nexus (forecasts, orders)
2. **Real-Time Promise**: Answer "When can I deliver?" (Promise module, sub-30s)
3. **Material Planning**: Calculate material requirements and generate purchase orders (MRP module, batch)
4. **Capacity Planning**: Assign production operations to resources with finite constraints (Capacity CTP, used by Promise and MRP)
5. **Schedule Optimization**: Balance competing objectives using advanced algorithms (Optimization module, nightly)
6. **Work Order Generation**: Create executable work orders for shop floor (MRP module)
7. **Real-time Adjustments**: Handle disruptions with automated replanning (Replanning module)
### 5.4 Planning Decisions Overview (detailed)
| Decision | Description | Automation Level | Frequency |
| ---------------------- | ------------------------------------------------------------------- | --------------------- | ------------ |
| Order Acceptance | Heuristic ATP/CTP-lite combining material and capacity checks | Semi-automated (fast) | Per order |
| Material Replenishment | Netting, pegging, safety/min-max/cover days, PO recommendations | Fully automated | Daily |
| Capacity Assignment | Finite allocation to periods/resources (no detailed sequencing) | Fully automated | Daily |
| Work Order Planning | Generate/release WOs, track progress, reconcile MES feedback | Semi-automated | Per order/WO |
| Campaign Management | Group similar ops to reduce setups (no batching) | Semi-automated | Weekly |
| Replanning | Incremental adjustments on disruptions; scenario/what-if evaluation | Semi-automated | On event |
| Optimization | Multi-objective solver run with heuristic fallback | Automated/opt-in | On demand |
### 5.5 KPI Matrix (with meaning)
| Business Goal | Order Acceptance | Material Replenishment | Capacity Assignment | Work Order Planning | Campaign Management |
| ----------------------- | ---------------- | ---------------------- | ------------------- | ------------------- | ------------------- |
| Resource Utilization | ✅ Medium | ✅ Low | ✅ High | ✅ High | ✅ High |
| Material Management | ✅ High | ✅ High | ✅ Medium | ✅ Medium | ✅ Low |
| Scheduling Optimization | ✅ High | ✅ Medium | ✅ High | ✅ High | ✅ Medium |
**KPI Definitions, equations, and decisions they drive**
- On-Time Delivery = delivered_on_or_before_promise ÷ delivered. If low, improve capacity/material/lead times.
- Promise Accuracy = accepted_on_time ÷ accepted. If low, tighten acceptance heuristics or add buffers.
- Acceptance Rate = accepted ÷ requested. If low, expand capacity/material or relax rules.
- Response Time (P95/P99) = latency of acceptance decision. Over SLA → optimize heuristics/data access.
- Resource Utilization = used_capacity ÷ available_capacity. High+lateness → add capacity; low → rebalance.
- Inventory Turnover = COGS ÷ average_inventory. Low → reduce buffers/lot sizes; high with stockouts → raise safety.
- Planning Cycle Time = order_to_schedule_release duration. Long → streamline data/algorithms.
- Schedule Stability = unchanged_ops_after_replan ÷ total_ops. Low → reduce churn and increase locks/fixed.
---
## 6. Planning Decision Details
### 6.0 Material Availability Module
**Description**
Material Availability is a **knowledge/query service** that provides material position information without making planning decisions. It serves as the foundation for all material-related planning operations.
**Purpose**
Answer the question: "What material is available?" (Information only, no decisions)
**Responsibilities**:
- ✅ Query Inventory projection (on-hand quantities per product/stocking point)
- ✅ Query SupplyOrder projection (inbound supply orders with dates/quantities)
- ✅ Query MaterialReservation projection (active reservations to subtract)
- ✅ Query Safety Policy (safety stock requirements to subtract)
- ✅ Calculate net available quantity
- ✅ Return material snapshot (aggregated information)
**What It Does NOT Do**:
- ❌ Does NOT decide whether to import or produce
- ❌ Does NOT generate supply orders
- ❌ Does NOT make planning decisions
- ❌ Does NOT store full inventory details (just queries and calculates)
**Interface**:
```fsharp
type MaterialProvider = {
GetSnapshot: ProductId * StockingPointId * DateTimeOffset
-> Async<Result<MaterialSnapshot, ProviderError>>
}
type MaterialSnapshot = {
OnHand: decimal // Current inventory
Inbound: (DateTimeOffset * decimal) list // Inbound supply orders
Reservations: decimal // Active reservations (subtract)
Safety: decimal // Safety stock (subtract)
}
```
**Calculation**:
```
Net Available = OnHand + Inbound - Reservations - Safety
```
**Usage**:
- **Used by**: Promise, MRP, Material Replenishment
- **Called when**: Need to know material availability
- **Returns**: Snapshot of material position (information only)
**Example**:
```fsharp
let snapshot = MaterialProvider.GetSnapshot("ProductX", "SP-1", Monday)
// MaterialProvider queries:
// 1. Inventory Projection → 200 units on-hand
// 2. SupplyOrder Projection → 50 units arriving Monday
// 3. MaterialReservation Projection → 150 units reserved
// 4. Safety Policy → 20 units safety stock
// Returns:
{
OnHand = 200.0m
Inbound = [{ Date = Monday, Qty = 50 }]
Reservations = 150.0m
Safety = 20.0m
}
// Net Available = 200 + 50 - 150 - 20 = 80 units
// (Just information, no decision about what to do)
```
**Integration Points**:
- **Promise**: Queries Material Availability to check if material is available
- **MRP**: Queries Material Availability for netting calculations
- **Material Replenishment**: Queries Material Availability to check stock levels
**Key Principle**:
Material Availability = "What do I have?" (Information)
MRP = "What should I do?" (Decision)
**Architectural Note**:
Material Availability is a pure query/knowledge service. It does NOT make decisions about importing or producing. It simply provides information about material position. Decision-making (what to import, what to produce) is the responsibility of MRP.
---
### 5.6 Module Architecture Summary
#### 5.6.1 Module Responsibilities Matrix
| Module | Purpose | Speed | Scope | Creates | Owns Knowledge |
| -------------------------- | ---------------------------- | --------- | --------------- | ---------------------- | -------------------------- |
| **Material Availability** | Query material position | Instant | Per query | Information | None (queries projections) |
| **Material Reservation** | Hold/commit material | Instant | Per reservation | Reservations | None (simple contract) |
| **Capacity CTP** | Check capacity availability | Seconds | Per work order | Capacity availability | **Work Routing** |
| **Transport ATP** | Check transport availability | Seconds | Per route | Transport availability | **Transport Routing** |
| **Promise** | Order promising | Sub-30s | Per order | Tentative reservations | None (orchestrates) |
| **MRP** | Material planning | 30min-2hr | All orders | Supply orders | BOM (for explosion) |
| **Material Replenishment** | Inventory management | Minutes | Per product/SP | Triggers MRP | None (monitors stock) |
| **Optimization** | Plan optimization | Hours | All orders | Optimized plan | None (reuses functions) |
#### 5.6.2 Key Architectural Decisions
**1. Routing Knowledge Ownership**
- ✅ **Capacity CTP owns Work Routing knowledge** (for capacity checking)
- ✅ **Transport ATP owns Transport Routing knowledge** (for transport checking)
- ✅ **MRP does NOT need routing knowledge** (just asks "can I produce?")
- ✅ **Promise does NOT need routing knowledge** (uses Capacity CTP and Transport ATP)
**2. Finite Capacity MRP**
- ✅ **MRP checks capacity** via Capacity CTP (doesn't assume infinite capacity)
- ✅ **Realistic plans**: MRP creates feasible plans (not over-committed)
- ✅ **Early detection**: Catch capacity issues during planning (not execution)
**3. Promise vs MRP Separation**
- ✅ **Promise**: Real-time (30s), per-order, customer-facing, creates reservations
- ✅ **MRP**: Batch (30min-2hr), all orders, planning-facing, generates supply orders
- ✅ **Promise does NOT call MRP** (too slow, different purpose)
- ✅ **Both use**: Material Availability, Material Reservation, Capacity CTP
**4. Material Replenishment → MRP**
- ✅ **Material Replenishment triggers MRP** (when shortfall detected)
- ✅ **MRP generates supply orders** (single source of truth)
- ✅ **No duplication**: Replenishment doesn't generate orders directly
**5. Optimization as Separate Module**
- ✅ **Optimization is separate** (not sub-module of MRP)
- ✅ **Cross-domain**: Optimizes material + capacity + transport
- ✅ **Reuses functions**: Calls MRP functions, Capacity CTP, Transport ATP
- ✅ **No duplication**: Same functions used by heuristic and optimizer
#### 5.6.3 Data Flow Summary
```
Material Availability
↓ (queries)
Material Reservation ← (subtracts from availability)
↓ (used by)
┌─────────────┐ ┌─────────────┐
│ Promise │ │ MRP │
│ (Real-Time) │ │ (Batch) │
└──────┬──────┘ └──────┬──────┘
│ │
│ Uses │ Uses
▼ ▼
┌─────────────┐ ┌─────────────┐
│ Capacity CTP│ │ Capacity CTP│
│ (Shared) │ │ (Shared) │
└─────────────┘ └─────────────┘
│ │
│ Uses │
▼ │
┌─────────────┐ │
│Transport ATP│ │
└─────────────┘ │
│ Can trigger
┌───────────────────────┐
│ Material Replenishment │
│ (Triggers MRP) │
└───────────────────────┘
│ Can use
┌───────────────────────┐
│ Optimization │
│ (Uses all functions) │
└───────────────────────┘
```
---
### 6.1 Order Acceptance (Promise/ATP - Heuristic ATP/CTP-lite)
**Description**
Commit orders by checking material, capacity, transport, and supplier feasibility with a fast heuristic (sub-30s); no detailed sequencing. The order promising framework provides reliable order promising across all availability domains with event-sourced core, projection-backed queries, deterministic IDs, idempotency, and pluggable policies (time-first, cost-aware, risk-aware) with alternates/fallbacks.
**Goals**
- Reliable order promising across material, capacity, transport, and supplier options
- Event-sourced core, projection-backed queries, deterministic IDs, idempotency
- Pluggable policies (time-first, cost-aware, risk-aware), alternates/fallbacks
- Reservations lifecycle (tentative → confirmed → release/expire) across domains
- Caching with clear invalidation for performance; optional background rebuild
**High-Level Flow (Data & Decisions)**
```
┌─────────────────────────────────────────────────────────────┐
│ PROMISE WORKFLOW (Complete Flow) │
│ │
│ Input: Customer Order (Product, Quantity, Due Date) │
│ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ Step 1: ROUTING SELECTION ← FIRST! │ │
│ │ └─> RoutingProvider.Select(ProductId, SP, Policy) │ │
│ │ └─> Returns: Primary + Alternates │ │
│ │ └─> Contains: Work Routing + Transport Routing │ │
│ └──────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ Step 2: MATERIAL ATP │ │
│ │ └─> MaterialProvider.GetSnapshot() │ │
│ │ └─> Calculate: onHand + inbound - reservations │ │
│ │ └─> If shortfall: SupplierProvider (optional) │ │
│ │ └─> Result: Material ready date │ │
│ └──────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ Step 3: CAPACITY ATP (Uses Work Routing) │ │
│ │ └─> For each routing step: │ │
│ │ - Get resource group from step │ │
│ │ - CapacityCTP.CheckCapacity() │ │
│ │ - Find earliest feasible window │ │
│ │ └─> Result: Capacity ready date │ │
│ └──────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ Step 4: TRANSPORT ATP (Uses Transport Routing) │ │
│ │ └─> Get origin/destination from routing │ │
│ │ └─> TransportATP.GetOptions() │ │
│ │ └─> Check capacity, cutoffs, constraints │ │
│ │ └─> Result: Transport arrival date │ │
│ └──────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ Step 5: CALCULATE PROMISE DATE │ │
│ │ └─> PromiseDate = MAX( │ │
│ │ materialReady, │ │
│ │ capacityReady, │ │
│ │ transportArrival │ │
│ │ ) │ │
│ │ └─> Bottleneck determines promise date │ │
│ └──────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ Step 6: CREATE TENTATIVE RESERVATIONS │ │
│ │ └─> Material Reservation (if material available) │ │
│ │ └─> Capacity Reservation (if capacity available) │ │
│ │ └─> Transport Reservation (if transport available) │ │
│ └──────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ Output: Promise Response │ │
│ │ - Decision: Accepted/Rejected │ │
│ │ - Promise Date (if accepted) │ │
│ │ - Limiter (if rejected) │ │
│ │ - Cost, Confidence, Reservations │ │
│ └──────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
Key Dependencies:
Material Availability ← Queries projections
Material Reservation ← Subtracts from availability
Capacity CTP ← Owns Work Routing knowledge
Transport ATP ← Owns Transport Routing knowledge
Routing Master Data ← Used by Capacity CTP and Transport ATP
```
**Core Domain Pieces (Aggregates / Projections)**
- CustomerOrder / Promise: decision, limiter, rationale
- Routing: primary + alternates, qty-duration, validity, resource groups, cost/risk prefs
- ResourceCalendar (proj): capacity windows per resource ref
- Operation: allocations/locks against capacity (scheduled/started/completed)
- Inventory (proj): on-hand per product/SP
- SupplyOrder (proj): firm inbound with required dates/state
- MaterialRequirement / MaterialReservation: pegged demand + holds; safety policies
- TransportLeg aggregate + TransportCalendar proj: schedules, capacity, lead time, cost, constraints, reliability
- SupplierOffer / Supplier ATP: lead times (p50/p95), MOQs, capacities, price, incoterms, reliability
- Reservation aggregate: scope (material/capacity/transport), qty/time window, state (Tentative/Confirmed/Released/Expired/Reduced), TTL
**Inputs**
- **Order**: product, qty, due date, priority, full-order/full-delivery flags, stocking point, expedite flag, customer
- **Supply**: on-hand, WIP, in-transit, firm inbound POs/WOs with dates, reservations, safety/targets
- **Routing/capacity**: routing steps, qty-dependent durations, resource calendars, fixed/locked operations, allowed slack, routing alternates
- **Transport**: transport legs, calendars, capacities, cutoffs, lead times, constraints, reliability, CO2
- **Supplier**: supplier offers, MOQs, lead times (p50/p95), price tiers, reliability, incoterms
- **Policies**: priority ordering, full-order/full-delivery enforcement, expedite limits, time vs cost vs risk preferences, cost/risk caps, search budget
**Process (online fast path <30s heuristic; no detailed sequencing)**
1) **Routing Selection**: Call RoutingProvider.Select with policy; get primary/alternate set. Try primary first, then alternates on failure or better score per policy (fastest/cheapest/balanced)
2) **Material ATP-lite**:
- Call MaterialProvider.GetSnapshot (on-hand + inbound - reservations - safety)
- Compute earliest material-ready date: net_available(t) = on_hand + inbound_by(t) – reservations_by(t) – safety; earliest_material_date = min t where net_available ≥ qty
- If shortfall and policy allows, call SupplierProvider.GetSupplierOptions; consider supplier option if earlier/cheaper per policy
3) **Capacity window**:
- For each routing step, call CapacityProvider.GetBuckets (calendars − allocations/locks − reservations − safety)
- Apply qty-duration from CapacityProvider.GetQtyDuration (qty-dependent duration calculation)
- Find earliest feasible window per routing step; apply slack/buffer per policy
- Earliest_capacity_date = end of last step
4) **Transport ATP**:
- Call TransportProvider.GetOptions for origin→destination (multi-hop pathfinding with k-shortest paths)
- Check capacity, cutoffs, constraints (regulatory/hazmat), reliability
- Pick earliest/score by policy (time/cost/risk/CO2/green preference)
- Earliest transport arrival date
5) **Promise date calculation**: PromiseDate = max(material_ready, capacity_ready, transport_arrival)
6) **Limiter selection**: Limiter = argmax contributor (material/capacity/transport/supplier); attach rationale with reason code from catalog
7) **Risk/confidence calculation**: Use p50/p95 or buffers per policy/SLA; compute confidence from material/capacity/transport/supplier reliability
8) **Cost calculation**: Compute time + cost score (material + production + transport + holding + lateness penalties); policy chooses fastest/cheapest/balanced
9) **Constraint application**: Apply FullOrder/FullDelivery (requires single promise per order/line), priority (may preempt within policy), expedite (only if policy allows)
10) **Decision emission**:
- If feasible: Create tentative reservations (material/capacity/transport) via ReservationProvider.CreateTentative; emit Promise/Acceptance event with limiter, rationale, routing/itinerary, cost, confidence, reservations
- If infeasible: Emit Rejection with limiter (domain, code, message, suggestions) and rationale
**Outputs**
- **Decision**: accepted/rejected, PromiseDate, limiter, rationale
- **Routing**: selected routing (primary/alternate), steps used
- **Itinerary**: transport itinerary (legs, arrival, cost, reliability, CO2) if transport used
- **Material**: material snapshot (earliest available, net available qty)
- **Capacity**: capacity availability per routing step (earliest end, is sufficient)
- **Cost**: total cost breakdown (material, production, transport, holding, lateness penalties), currency
- **Risk**: confidence level, lead-time basis (p50/p95), reliability notes
- **Reservations**: tentative reservation IDs (material/capacity/transport) created
- **Meta**: generated timestamp, search budget used, search mode, provider latencies
- **Events**: PromiseCreated/AcceptanceCreated events to read models/audit
**Rules**
- Single UoM; no batching; no detailed sequencing
- Online SLA <30s heuristic; day-over-day full run can refine
- Honor locks/fixed operations; enforce full-order/full-delivery; priority strictly ordered
- Reservations are idempotent (deterministic IDs, idempotency keys); retry-safe
- Provider-based extensibility: light mode (no-op providers) vs full mode (real providers) without code changes
- Degradation: ProviderError maps to Limiter with retry/alternate suggestions; never leave dangling reservations on failure
- Cache invalidation: subscribe to calendar/leg/reservation/allocation change events; rebuild caches on invalidation
**Provider-Based Architecture**
The promise orchestrator uses injected providers to enable light vs full modes:
- **MaterialProvider**: GetSnapshot (on-hand + inbound - reservations - safety), optional GetSupplierOptions
- **CapacityProvider**: GetBuckets (calendars - allocations - reservations - safety), GetQtyDuration (qty-dependent duration)
- **TransportProvider**: GetOptions (k-shortest paths, capacity/cutoff/constraint checks, scoring)
- **SupplierProvider**: GetSupplierOptions (MOQ/lead-time validation, scoring by time/cost/reliability)
- **RoutingProvider**: Select (primary/alternates, policy-driven selection)
- **ReservationProvider**: CreateTentative, Confirm, Release (idempotent, deterministic IDs)
- **PolicyProvider**: GetPolicy (customer/SKU-specific policy presets: Gold/Silver/Bronze)
- **TelemetryProvider**: RecordKpi, RecordError, RecordLatency (per provider, limiter frequency, cache hit/miss)
- **FxProvider**: GetRate (currency conversion with as-of timestamp, stale guard)
- **TenantProvider**: GetTenant (tenant ID + timezone, UTC normalization)
**Light vs Full Mode**
- **Light mode**: Inject defaults (reservations=0, safety=0, GetOptions returns empty transport, GetSupplierOptions empty, allocations not subtracted, qty-duration None). Orchestrator still runs and returns heuristic results
- **Full mode**: Inject real providers (reservations/safety filled, allocations/locks subtracted, transport itineraries, supplier options, qty-duration present). No code duplication, just richer data and policies
**Reservations Lifecycle (Cross-Domain)**
- States: Tentative → Confirmed → Released/Expired → Reduced
- Events: ReservationCreated, Confirmed, Released, Reduced, Expired
- Created on promise (tentative), confirmed on release/firming; released on cancel/qty decrease/expiry
- TTL/expiry sweeper frees resources; projections feed availability subtraction
- Deterministic IDs: hash(orderId, lineId, scope, step/leg, window)
- Idempotency keys: prevent duplicates on retry
**Multi-Hop Material Flow**
- Graph: stocking points + transport legs
- Material readiness can be sourced from multiple origins; k-shortest paths optional
- Stage chaining: material ready → production finish → transport to next node → … → delivery
- Promise driven by slowest stage; limiter names the bottleneck
**Transport Modeling**
- Leg: origin, destination, mode, schedule (departures), capacity per departure, cutoff, lead time, cost, constraints, reliability, CO2
- Availability: earliest feasible itinerary with capacity/cutoff/constraints; add safety slack or p95 lead times for high-SLA
- Pathfinding: k-shortest path algorithm (Yen's algorithm) over transport graph with capacity/cutoff/constraint checks
- Scoring: time/cost/risk/reliability/CO2 per policy preference
**Supplier ATP**
- SupplierOffer events: capacity offered/consumed, lead time updates, price tiers, MOQs, reliability
- Query when local short or policy says compare; score by date/cost/reliability
- Fallback policy configurable (block vs accept-with-risk vs counteroffer)
- MOQ validation, lead-time validation (reject if exceeds SLA)
**Cost & Risk**
- Cost model: material (buy), production (resource), transport (lane/mode), holding, lateness penalties; currency normalized via FX snapshot
- Risk: lead-time distributions, reliability, buffers (p95 or p50+buffer%); RiskAssessment returns confidence + sensitivity
- Promise payload: date, limiter, cost, confidence
**Quality & Rework**
- Yield per step; effective required material/capacity = demand / yield
- Rework/inspection steps; add capacity/transport hops where needed
**Regulatory & Compliance**
- Constraints on legs/routings (export controls, hazmat, country pairs)
- Customs/clearance as extra lead time/cost nodes
- Sustainability: CO2 per leg; "green" preference in scoring if required
**Real-Time Updates & Alerting**
- On projection deltas (resource down, transport outage, supplier delay): re-eval impacted promises, emit AtRisk, notify customers; optional reroute/replan per policy
- SLA monitoring: promised vs predicted; alert on breach risk
**Caching & Invalidation**
- Caches: Capacity buckets (post allocations/reservations/safety) per resource key; Material snapshots (on-hand + inbound − reservations − safety) per product/SP; Transport next departures/itineraries per lane
- Invalidation triggers: calendar/leg changes, reservations changes, allocations changes, safety policy changes
- Background agents: rebuild caches on events/interval; keep last-good on failure. Reservation expiry sweeper
- Timeouts: per-request budget; fallback to heuristic if optimal search exceeds budget
- Scale: stateless services over shared projections/caches; shardable caches if high QPS
**Reason-Code Catalog**
- **Material**: MaterialShortfall, MaterialReservationConflict, SafetyViolation, SupplierMOQ, SupplierLeadtimeExceeded
- **Capacity**: CapacityShortfall, CapacityLocked, CapacitySafetyBuffer, QtyDurationUnsupported
- **Transport**: NoTransportLeg, NoTransportCapacity, CutoffMissed, RegulatoryBlocked
- **Routing/Alternates**: RoutingInvalid, RoutingCapacityFail, AlternateExhausted
- **Policy/Constraints**: FullOrderViolation, FullDeliveryViolation, CostCapExceeded, RiskCapExceeded
- **System**: SearchTimeout, DataStale
**Concurrency / Race Handling**
- For app-driven ATP, wrap the orchestrator in a MailboxProcessor (PromiseAgent) to serialize promise computations if needed and avoid concurrent state mutations
- Providers themselves should be side-effect free or concurrency-safe; reservations remain the critical shared mutation and should be idempotent
**ML Integration Workflow**
- Where ML fits: Providers can call ML models to predict lead times, reliability, or risk for material inbound, capacity availability (e.g., outage risk), transport itineraries, and supplier performance
- Pattern: Provider returns Result<'a, ProviderError> with fields carrying both predicted values and the basis (model/version, p50/p95)
- On ML failure, return ProviderError and let limiter surface with retry/heuristic suggestion
**UI**
- Promise date, limiter reason, priority/flags, response timer; hot-spot alerts; override with explanation
- Show routing/itinerary chosen, cost breakdown, confidence level, reservation status
- Display suggestions from limiter (later date, alternate routing/mode, reduce qty, supplier option, override buffer, retry)
**Use Cases**
- Standard acceptance; rejection with reason; priority preemption; expedite attempt; full-order enforcement; bulk check
- Multi-hop material flow; supplier ATP on shortfall; transport pathfinding with constraints; alternate routing selection
- Real-time promise re-evaluation on disruption; cache invalidation and rebuild
**KPIs (see 4.4 definitions)**
- Promise accuracy, acceptance rate, response time (P95/P99), lateness split (material vs capacity vs transport vs supplier)
- Provider latency per domain (material/capacity/transport/supplier); limiter frequency distribution; cache hit/miss rates
- Promise at-risk tracking (promises that may breach due to projection deltas)
### 6.2 Material Requirements Planning (MRP)
**Description**
Multi-level netting, pegging, and PO recommendations with safety/min-max/cover-day policies. The material planning system computes net requirements per product/stocking point to drive supply orders and reservations, supporting pegging/traceability, safety/min/max/cover policies, and integration with ATP/Promise. **Includes finite capacity checking** to create realistic, feasible plans.
**Goals**
- Compute net requirements per product/stocking point; drive supply orders and reservations
- Support pegging/traceability, safety/min/max/cover policies, and integration with ATP/Promise
- **Check capacity constraints** (finite capacity MRP) to ensure realistic plans
- Remain deterministic/idempotent; reuse shared core (policies, scoring, limiters, reservations)
**Scope**
- Netting logic (gross → net) using on-hand, inbound (firm/planned), reservations, safety targets
- **Finite capacity checking** via Capacity CTP (ensures realistic work order dates)
- Supply order proposals (PO/WO/TO) generation and updates
- Pegging map (demand → supply/reservations)
- Time-phased visibility (optional buckets)
- **Sub-Modules**:
- **Heuristic MRP**: Fast path (minutes per order, daytime)
- **Material Replenishment**: Triggers MRP when stock falls below minimum
**Inputs**
- **Demands**: Customer Orders, Forecasts (time-phased), safety stock, inventory targets
- **Supply**: Inventory (on-hand), SupplyOrders (firm & planned), inbound lead times, WIP, in-transit
- **Reservations**: Material reservations (tentative/confirmed/reduced) - active totals per product/SP
- **Policies**: safety/min/max/cover days per product/SP, lot sizes (fixed lot, min lot, EOQ), priority/expedite flags, violation handling (allow vs reject vs flag)
- **Master data**: Products, BOMs (for multi-level expansion with alternates), Stocking Points, UoM, lead times
**Core Logic**
1) **BOM Explosion**: Multi-level BOM explosion with cycle detection; choose alternates per policy (preferred, availability)
2) **Gross Requirements**: Sum demand per period (customer orders + forecasts + safety stock)
3) **Netting**:
- NetReq = max(0, GrossReq – (OnHand + InTransit + FirmSupply + PlannedSupply – Reservations – Safety))
- Subtract active material reservations (Tentative + Confirmed + Reduced) from availability
- Honor safety/min/max/cover days; never violate safety unless policy permits
4) **Lot Sizing**: Apply lot size/EOQ/min/max to net requirement with rounding rules (up, nearest lot)
5) **Pegging**: Create/update pegging map linking demand to supply/reservations (optional but recommended)
6) **Planned Supply**: Emit SupplyOrder recommendations (PO/WO/TO) with dates/qty, priority/expedite, pegging references
**Process (Detailed Steps with Capacity Checking)**
```
┌─────────────────────────────────────────────────────────────┐
│ MRP PROCESS FLOW │
│ (Batch, All Orders, Finite Capacity) │
│ │
│ Step 1: BOM EXPLOSION │
│ └─> For each order: │
│ - Explode BOM recursively (multi-level) │
│ - Detect circular BOM references │
│ - Calculate gross requirements per component │
│ - Example: Product A (100 units) requires: │
│ * 200 units Component X │
│ * 100 units Component Y │
│ │
│ Step 2: MATERIAL NETTING │
│ └─> For each component: │
│ - Query Material Availability: │
│ * OnHand + Inbound - Reservations - Safety │
│ - Calculate: NetReq = GrossReq - Available │
│ - Example: Component X │
│ * Gross: 200 units │
│ * Available: 50 units │
│ * Net: 150 units (need to order/produce) │
│ │
│ Step 3: GENERATE WORK ORDER PROPOSALS │
│ └─> For each net requirement: │
│ - Create Work Order Proposal │
│ - Product: Product A │
│ - Quantity: 100 units │
│ - Need Date: Jan 15 │
│ - NO routing knowledge needed! ✅ │
│ (Capacity CTP will handle routing lookup) │
│ │
│ Step 4: CAPACITY CHECKING (Via Capacity CTP) ← KEY STEP! │
│ └─> For each work order proposal: │
│ - Call CapacityCTP.CheckCapacity( │
│ productId: "ProductA", │
│ quantity: 100, │
│ needDate: Jan 15 │
│ ) │
│ - Capacity CTP internally: │
│ * Looks up Work Routing (its responsibility) │
│ * Gets routing steps │
│ * Checks capacity for each step │
│ * Returns: Earliest available date │
│ - Example Result: │
│ * Need Date: Jan 15 │
│ * Capacity Available: Jan 16 (1 day delay) │
│ │
│ Step 5: ADJUST WORK ORDER DATES │
│ └─> If capacity not available on need date: │
│ - Option A: Delay to earliest available │
│ * Work Order: Jan 16 (delayed by 1 day) │
│ - Option B: Try alternate routing (if policy allows) │
│ - Option C: Reject if too late (per policy) │
│ │
│ Step 6: GENERATE SUPPLY ORDERS │
│ └─> Purchase Orders (for materials from BOM) │
│ - Component X: 150 units, need by Jan 10 │
│ └─> Work Orders (with capacity-checked dates) │
│ - Product A: 100 units, start Jan 16 │
│ └─> Transport Orders (if needed) │
│ │
│ Step 7: CREATE PEGGING LINKS │
│ └─> Link demand to supply (traceability) │
│ - Customer Order → Work Order │
│ - Work Order → Purchase Order │
│ │
│ Output: Supply Orders (PO/WO/TO) with realistic dates │
│ (capacity-checked, not assuming infinite capacity) │
└─────────────────────────────────────────────────────────────┘
```
**Key Architectural Points**:
- ✅ **MRP does NOT own routing knowledge** (Capacity CTP owns it)
- ✅ **MRP queries Capacity CTP** (just asks "can I produce?")
- ✅ **Finite Capacity MRP** (checks capacity, creates realistic plans)
- ✅ **Capacity CTP handles routing lookup** (better separation of concerns)
**Example: Capacity Constraint Detection**:
```
Scenario: 100 customer orders, all due Jan 15
MRP Process:
Step 1-3: BOM Explosion, Netting, Generate 100 Work Orders
Step 4: Capacity Checking
└─> For each of 100 work orders:
- CapacityCTP.CheckCapacity()
- Capacity CTP finds: Only 50 can be produced on Jan 15
- Returns: Jan 16 for remaining 50 orders
Step 5: Adjust Dates
└─> Work Orders 1-50: Jan 15 ✅
└─> Work Orders 51-100: Jan 16-20 (spread across capacity) ⚠️
Result: Realistic plan (not over-committed) ✅
```
**States & Events**
- MaterialRequirement aggregate: exists with net requirement, pegging, safety context
- SupplyOrder aggregate: planning creates/updates planned orders (Planned → Confirmed/Firm → Released)
- MaterialReservation aggregate: provides holds; subtracted in availability/netting
- Events: MaterialRequirementCreated/Updated, SupplyOrderRecommended, PeggingLinkCreated/Updated
**Projections / Queries**
- Inventory projection (on-hand per product/SP)
- SupplyOrder projection (firm/planned with dates/states)
- MaterialReservation projection (active totals per product/SP)
- Optional time-phased netting buckets (gross/net/on-hand/inbound/reservations per period)
**Integration Points**
- **Promise/ATP**: Uses reservations and inbound; may create tentative reservations on accept
- **Capacity CTP**: MRP queries Capacity CTP to check capacity (finite capacity MRP)
- **Netting loop**: After net calc, propose/update planned supply orders; produce pegging map
- **MRP output**: SupplyOrder recommendations + pegging events
**Key Architectural Decision: Finite Capacity MRP**
- ✅ **MRP checks capacity** via Capacity CTP (doesn't assume infinite capacity)
- ✅ **MRP does NOT own routing knowledge** (Capacity CTP owns it)
- ✅ **MRP interface**: Just asks "can I produce Product X?" (Capacity CTP handles routing lookup)
- ✅ **Realistic plans**: MRP creates feasible plans (not over-committed to capacity)
**Policies**
- Safety/min/max/cover days per product/SP
- Lot sizing (fixed lot, min lot, EOQ) with rounding rules
- Priority/expedite influence on dates/selection
- Violation handling: allow vs reject vs flag (ConstraintPolicy)
- Forecast consumption: finished strong, intermediate limited to preferred routing
**Telemetry / KPIs**
- Netting run latency; number of planned orders; safety violations; pegging completeness
- Promise accuracy impact (optional)
- Material availability, netting accuracy, pegging completeness, plan stability
**ML Hooks (Optional)**
- Predict lead times for inbound; predict demand variability for safety; adjust netting buffers
- Include model basis/version in predictions; fallback to defaults on ML failure
**Outputs**
- Material requirements (gross/net per product/SP/period)
- Pegged reservations (demand ↔ supply/reservations links)
- PO recommendations (PurchaseOrderRecommendation with qty, dates, priority, expedite, pegging)
- Projected inventory (on-hand + inbound – reservations – safety over time)
**Rules**
- Single UoM; detect circular/invalid BOM; honor safety/min/max/cover days; alternates allowed per policy
- Forecast consumption rules: finished strong, intermediate limited to preferred routing
- Reservations subtraction: subtract active material reservations (Tentative + Confirmed + Reduced) in netting
- Idempotent proposal generation: key proposals by (demandId, period, type) to avoid duplicates on rerun
**UI**
- Pegging graph (demand ↔ supply visualization)
- Inventory projections (on-hand + inbound – reservations – safety over time)
- PO suggestion list (recommendations with qty, dates, priority, expedite)
- Shortfall alerts (when net requirement cannot be met)
- Policy tuning interface (safety/min/max/cover days per product/SP)
**Use Cases**
- Automatic daily netting run; forecast consumption; alternate BOM selection; shortfall detection; expedite PO
- Multi-level BOM explosion with cycle detection; time-phased netting visibility; pegging traceability
**KPIs**
- **Material Availability** = demands met on/before need date ÷ total demands. Drives procurement/target tuning
- **Netting Accuracy** = 1 – |planned_net – actual_net| ÷ planned_net (per period). Signals BOM/lead-time/forecast issues
- **Pegging Completeness** = pegged demand lines ÷ total demand lines. Low → improve pegging/traceability
- **Plan Stability** = unchanged requirements/reservations after replan ÷ total. Low → tune policies to reduce churn
### 5.3 Capacity Assignment (Finite, no detailed sequencing)
**Description**
Allocate operations to periods/resources with finite capacity, honoring locks/fixed ops; no detailed shop-floor sequencing. The capacity planning system provides configurable finite and infinite capacity planning modes (switchable by policy/config without code changes), plans operations against resource calendars/allocations/locks with buffers/safety, and supports routing steps, qty-dependent durations, alternates, and bottleneck handling.
**Goals**
- Provide configurable finite and infinite capacity planning modes (switchable by policy/config without code changes)
- Plan operations against resource calendars/allocations/locks with buffers/safety
- Support routing steps, qty-dependent durations, alternates, and bottleneck handling
- Integrate with reservations (capacity) and respect material/transport constraints via upstream services
**Modes (Configurable)**
- **Infinite planning**: Ignore capacity limits; schedule to requested/target dates; useful for what-if or rough-cut
- **Finite planning**: Honor capacity calendars, allocations/locks, reservations, safety buffers; find earliest feasible windows
- **Switch via configuration/policy**: CapacityPlanningParameters.EarliestDesiredInfiniteStrategy, bottleneck thresholds, overload thresholds
**Scope**
- Operations planning (Operation aggregate) over resource groups/resources
- Capacity buckets from ResourceCalendars minus allocations/locks/reservations/safety
- Scheduling heuristics: earliest feasible (no sequencing detail here), bottleneck identification, slack/buffer application
- Alternates: routing alternates or resource alternates if available
- Optional: time-phased capacity visibility
**Inputs**
- **Operations**: From SupplyOrder routing steps (Operation aggregate with routing step references)
- **ResourceCalendars**: Availability per resource/resource group (from ResourceCalendar projection)
- **Allocations/locks**: From scheduled ops (Operation allocations projection: scheduled/started operations)
- **Capacity reservations**: Future extension; aligned to material reservations pattern (CapacityReservation projection)
- **Policies**: Overload thresholds, bottleneck %, buffers, early/late thresholds (CapacityPlanningParameters)
- **Routings**: Precedence, validity windows, qty-dependent durations, resource groups, alternates
**Core Logic (Heuristic Scheduler)**
1) **Build Capacity Buckets**:
- Per resource group/resource: calendar × factor − allocations/locks − reservations − safety
- Buckets = available capacity windows (start time, end time, available duration)
- Cache buckets; invalidate on calendar/alloc/reservation change events
2) **For Each Operation Step**:
- Determine required duration (qty-dependent from routing step qty-duration function)
- **Finite mode**: Find earliest bucket(s) meeting duration; apply buffers; if none, flag overload/limiter
- **Infinite mode**: Place on target date (e.g., requested/confirmed) ignoring limits; optionally set overload flag if exceeding thresholds
3) **Alternates**: Try alternate routings or resource groups if primary fails (policy-driven)
4) **Buffers**: Apply safety/withheld capacity per resource; slack for reliability if configured
**Process (Detailed Steps)**
1) **Build Capacity Buckets**:
- For each resource/resource group:
- Base capacity = calendar availability (duration × factor) – downtime
- Subtract allocations (from scheduled/started operations)
- Subtract locks (from fixed operations)
- Subtract reservations (from CapacityReservation projection, if available)
- Subtract safety buffers (per resource policy)
- Clamp to minimum 0
- Create capacity buckets (start time, end time, available duration)
- Cache buckets; invalidate on calendar/alloc/reservation change events
2) **Interpret Routing**:
- For each routing step:
- Get qty-duration function from routing (qty → duration mapping)
- Calculate required duration = qty-duration(operation_qty) + slack per policy
- Enforce precedence (step k+1 after step k completion)
- Identify resource groups required for step
3) **Allocate Operations**:
- **Finite mode**:
- For each operation step:
- Find earliest feasible bucket(s) meeting duration requirement
- Apply buffers (safety, reliability, bottleneck protection)
- If no feasible bucket, flag CapacityShortfall limiter
- Assign operation to bucket (start time, end time, resource)
- **Infinite mode**:
- Place operation on target date (requested/confirmed) ignoring capacity limits
- Optionally set overload flag if exceeding thresholds (for visibility)
- **Alternates**: If primary routing/resource fails, try alternates per policy
4) **Produce Schedule Snapshot**:
- Create schedule snapshot with period/resource assignments
- Calculate utilization metrics (used ÷ available capacity per resource/period)
- Identify bottlenecks (resources with high utilization or overload)
**Projections / Queries**
- ResourceCalendar projection (existing): availability per resource/resource group
- Operation allocations projection: scheduled ops (scheduled/started operations consuming capacity)
- Capacity reservation projection: future extension, similar to material reservations
- Capacity bucket read model: optional cache for performance
**Integration Points**
- **Promise/ATP (capacity side)**: Can reuse buckets/reservations
- **Optimizer**: Can reuse policies and buckets; switch finite/infinite by policy
- **Material/transport constraints**: Handled upstream; this module focuses on capacity
**Key Configurations (CapacityPlanningParameters)**
- BottleneckPercentage: threshold for bottleneck identification (e.g., 85%)
- OverloadCapacityThreshold: threshold for overload flagging (e.g., 0.5%)
- EarliestDesiredInfiniteStrategy: infinite mode behavior (place on target date, flag overload)
- MaxDistanceToBottleneck: maximum distance from bottleneck for alternate selection
- PlanEarlyThreshold, PlanLateThreshold: early/late scheduling thresholds
- OnTimeDeliveryBuffer: buffer for on-time delivery protection
**Outputs**
- **Planned operation windows**: Start/End times per operation step, resource assignment
- **Overload/bottleneck flags**: If finite mode cannot satisfy within target
- **Optional planned capacity reservations**: Future extension for capacity holds
- **Schedule snapshot**: Period/resource assignments, utilization view
- **Utilization metrics**: Used ÷ available capacity per resource/period
**Rules**
- No detailed sequencing; no setup optimization (handled via campaigns later)
- Honor locks/fixed; support quantity-dependent lead times
- Finite/infinite toggle via policy; no code changes required
- Alternates: try alternate routings or resource groups if primary fails
- Buffers: apply safety/withheld capacity per resource; slack for reliability
**Telemetry / KPIs**
- Latency per planning run; overloads flagged; bottleneck detection counts
- Finite vs infinite usage; fallback rates
- Optional KPI weighting (if optimizer consumes)
**ML Hooks (Optional)**
- Predict downtime/reliability; adjust buffers or bucket availability
- Include model basis/version in predictions
**UI**
- Gantt-like capacity view (operations on timeline per resource)
- Utilization dashboard (used ÷ available per resource/period)
- Constraint violation alerts (overload, bottleneck, capacity shortfall)
- Finite/infinite mode indicator
**Use Cases**
- Initial schedule; reschedule with locks; expedite placement; view bottlenecks
- What-if analysis (finite vs infinite mode comparison)
- Bottleneck identification and resolution
**KPIs**
- **Utilization** = used capacity ÷ available capacity (per resource/period). High with lateness → need more capacity; low → balance loading
- **Lateness** = avg/max (scheduled end – required date if >0). High → improve capacity or adjust targets
- **Lock Adherence** = unchanged locked ops ÷ locked ops. Low → scheduler violating constraints
- **Churn per Replan** = ops moved ÷ total ops. High churn erodes shop-floor stability
- **Overload Count** = number of operations flagged as overload in finite mode
- **Bottleneck Detection** = number of resources identified as bottlenecks (utilization > threshold)
### 6.6 Work Order Planning
**Description**
Generate and release executable WOs from planned supply orders; track execution and reconcile MES feedback. The work order planning system turns net requirements into supply proposals (PO/WO/TO) deterministically and idempotently, supports planned vs firm states, priority/expedite, lead times, and confirmation workflows, and keeps proposals policy-driven (lot sizing, buffers) so behavior can be tuned without code changes.
**Goals**
- Turn net requirements into supply proposals (PO/WO/TO) deterministically and idempotently
- Support planned vs firm states, priority/expedite, lead times, and confirmation workflows
- Keep proposals policy-driven (lot sizing, buffers) so behavior can be tuned without code changes
**Scope**
- Proposal generation for SupplyOrders: PurchaseOrder, WorkOrder, TransportOrder
- States: Planned → Confirmed/Firm → Released → InProgress → Completed/Cancelled (align with existing SupplyOrder aggregate)
- Priority/expedite and dates (required/earliest/latest) derived from netting output and policies
- Pegging (optional): maintain linkage from demand to supply proposals for traceability
- Out of scope: capacity scheduling (separate), transport pathfinding (separate), optimization solver specifics
**Inputs**
- **Netting results**: From Material Planning - net qty, need date, pegging context
- **Policies**: Lot sizing (fixed/min/max/EOQ), lead time buffers, priority/expedite rules, min/cover days, supplier vs internal preference
- **Master data**: Products, StockingPoints, Routing (for WO), Suppliers (for PO), Transport legs (for TO)
- **Reservations**: Material reservations (subtracting active reservations already handled in netting)
**Process (Detailed Steps)**
1) **Proposal Record Creation**:
- Define proposal record shape: type (PO/WO/TO), qty (lot-sized), requested/need date, planned/available date, priority/expedite, routingId (WO), supplierId (PO), origin/destination (TO), state=Planned, pegging refs, idempotency key
2) **Lot Sizing**:
- Apply lot-sizing policy: fixed lot, min/max, EOQ
- Rounding rules (up, nearest lot)
- Example: net=120, lot=50 → propose 3 lots (150) or policy to cap at 2 lots (100) with residual rule
3) **Lead-Time Buffers**:
- Derive planned date = need date – (lead time p50/p95 or buffer%)
- Lead-time source selection: internal (routing) vs supplier (supplier offer)
- Example: need T0, lead p50=5d, buffer=20% → planned date T0-6d
4) **Priority/Expedite Mapping**:
- Map from demand priority or SLA tier
- Promote when late vs need date
- Example: Critical demand sets expedite=true and higher priority
5) **Firming Workflow**:
- Define rules to auto-confirm/firm proposals (e.g., when inside firming window) vs manual
- Specify emitting Confirm/Plan commands to SupplyOrder aggregate
- Example: firm POs 5 days before need if supplier lead time is short/locked
6) **Pegging Storage**:
- Store demand→proposal links (optional) for traceability
- Update on replan/churn
**Outputs**
- **Planned SupplyOrder recommendations** with:
- Type: PO/WO/TO
- Qty (lot-sized), RequiredDeliveryDate/PlannedDeliveryDate
- Priority/Expedite flag
- RoutingId (for WO), SupplierId (for PO), Origin/Destination (for TO)
- State: Planned (until confirmed)
- Pegging references (optional)
- Optionally emit events or populate a planning read model (recommendations)
**Policies (Configurable)**
- Lot sizing: fixed lot, min/max, EOQ; rounding rules
- Lead time buffers: add p50/p95 or buffer% to planned dates
- Priority/expedite: map from demand priority or SLA tier; promote when late vs need date
- Supplier vs internal: prefer internal WO unless policy says compare supplier ATP; fall back to supplier on shortfall
- Firming rules: when to auto-confirm/firm; when to require approval
- Date choice: end-of-period vs start-of-period suggestions
**Integration Points**
- Material Planning: consumes netting results to propose supply; feeds back into SupplyOrder aggregate or a recommendations projection
- Promise/ATP: firm SupplyOrders appear as inbound; planned orders may be excluded or marked as non-firm unless policy allows
- Transport: for TO proposals, defer to transport planning/pathfinding for dates/routes
- Reservations: no direct creation here; material reservations are upstream; capacity reservations are downstream (capacity module)
**Telemetry / KPIs**
- Proposal counts, lot-size adjustments, lead-time buffer usage, firming rate, replan churn (changes to planned orders), lateness risk
**ML Hooks (Optional)**
- Predict lead times (supplier/internal) to set planned dates; predict expedite need; predict churn to minimize replan
**Rules**
- Partial completions allowed; guarded state transitions; record variance/scrap; respect locks from schedule
- Idempotent proposal generation: key by (demandId, period, type) to avoid duplicates on rerun
- Replan/churn policy: define how to replace/update planned proposals vs leave as-is; versioning or supersede rules
**UI**
- WO list/detail, progress tracking, variance/rework alerts
- Supply order proposal list (recommendations with qty, dates, priority, expedite, supplier/routing)
- Firming workflow interface (approve/reject proposals)
**Use Cases**
- Release WO; record partial completion; close WO; register scrap/rework; reconcile MES update
- Generate supply proposals from netting; firm proposals; update proposals on replan
**KPIs**
- WO cycle time, throughput, redo/partial rate, variance vs plan
- Proposal counts, lot-size adjustments, lead-time buffer usage, firming rate, replan churn
### 5.5 Material Reservations
**Description**
Hold material for demand/promises in an idempotent, deterministic way. Material reservations support lifecycle: Tentative → Confirmed → Released/Expired/Reduced, subtract reservations in material availability/ATP/netting, and provide clear reason codes on conflicts.
**Goals**
- Hold material for demand/promises in an idempotent, deterministic way
- Support lifecycle: Tentative → Confirmed → Released/Expired/Reduced
- Subtract reservations in material availability/ATP/netting
- Provide clear reason codes on conflicts; avoid race conditions; support TTL sweeps
**Scope**
- Material reservations per ProductId + StockingPointId
- Cross-cutting: used by Promise/ATP, planning/netting, and operations that consume material
- Out of scope (for now): capacity/transport reservations (handled separately)
**States & Events**
- States: Tentative | Confirmed | Released | Expired | Reduced
- Commands: CreateTentative (idempotent key, ttl optional), Confirm, Release, Reduce (lower qty/window), Expire (sweeper)
- Events: ReservationCreated, ReservationConfirmed, ReservationReleased, ReservationReduced, ReservationExpired
**Fields (Aggregate)**
- Id (deterministic; e.g., hash of orderId/line/scope/window)
- IdempotencyKey (for retry-safe create/confirm)
- ProductId, StockingPointId
- Quantity (decimal, non-negative)
- WindowStart, WindowEnd (DateTimeOffset)
- Origin (e.g., PromiseId/OrderId)
- State, TTL (optional), Created, Modified
**Projection / Query**
- Projection keyed by (ProductId, StockingPointId), holding totals by state and list of active reservations
- Query service: getAll(), getByProductSp(product, sp), getActiveTotal(product, sp) → decimal
- MaterialAvailabilityService subtracts active (Tentative+Confirmed+Reduced) quantities from on-hand+inbound
**Integration Points**
- Promise/ATP: on Accept → CreateTentative; on cancel/timeout → Release; on order release/firming → Confirm
- Netting/planning: subtract active reservations when computing net requirements
- Expiry: background sweeper posts Expire for reservations past TTL or window end
**Validation**
- Non-negative qty; end >= start
- Reduce cannot increase qty; Confirm only from Tentative; Release from Tentative/Confirmed/Reduced
- Expire only if past TTL window
**Concurrency / Races**
- Idempotent Create with IdempotencyKey
- Deterministic IDs to avoid duplicates
- Sweeper should be idempotent; Release safe to call multiple times
**Telemetry**
- Emit events/KPIs for created/confirmed/released/expired; track failures and idempotent replays
---
### 6.10 Routing & Process Management
**Description**
Model routings and steps (process and transport) with alternates and validity windows. Capture qty-dependent durations, resource requirements, and preferences. Provide selection logic (primary/alternate) driven by policy (time/cost/risk).
**Goals**
- Model routings and steps (process and transport) with alternates and validity windows
- Capture qty-dependent durations, resource requirements, and preferences
- Provide selection logic (primary/alternate) driven by policy (time/cost/risk)
**Scope**
- Routing aggregate: steps, precedence, validity (effectivity), preferences/weights
- Routing steps: resource group/skill, setup/processing/yield, optional transport step
- Alternates: whole-routing alternates or per-step alternates
- Qty-duration functions; yields and rework/inspection steps
- **Note**: Routing is master data (not owned by Capacity Planning or MRP)
- **Note**: Capacity CTP owns Work Routing knowledge (for capacity checking)
- **Note**: Transport ATP owns Transport Routing knowledge (for transport checking)
**Inputs**
- Master data: products, stocking points, resource groups, calendars
- Policies: alternate selection (fastest/cheapest/balanced), validity enforcement, yield handling
**Outputs**
- Selected routing/steps for a demand (or alternates set)
- Duration estimates per step (qty-adjusted) and aggregate lead estimate
**Integration Points**
- Promise/ATP: routing selection feeds capacity/transport checks
- Capacity Planning: uses routing steps for resource buckets and qty-duration
- Material Planning: for BOM explosion alignment (not covered here)
**Telemetry / KPIs**
- Alternate usage rate; invalid/expired routing incidence; selection latency
**ML Hooks (Optional)**
- Predict step duration/yield; choose alternates based on predicted reliability/cost
---
### 5.7 Transport Management
**Description**
Model transport legs, calendars, capacities, cutoffs, constraints; provide transport ATP (earliest feasible arrival). Support alternates/modes and k-shortest path search; policy-driven scoring (time/cost/risk/CO2).
**Goals**
- Model transport legs, calendars, capacities, cutoffs, constraints; provide transport ATP (earliest feasible arrival)
- Support alternates/modes and k-shortest path search; policy-driven scoring (time/cost/risk/CO2)
**Scope**
- TransportLeg aggregate: origin/destination SP, mode, schedule, capacity, cutoff, constraints (regulatory/hazmat), reliability, CO2
- Transport calendars/itineraries: departures/arrivals
- Transport ATP service: feasibility and earliest arrival; limiter on failures
- Alternates: multiple legs/modes; pathfinding
**Inputs**
- Legs/calendars, capacity per departure, cutoff rules
- Constraints: regulatory/hazmat/country, blackout periods
- Policies: time vs cost vs risk/CO2, supplier/green preference (if any)
**Outputs**
- Itineraries with arrival, cost, reliability, CO2; limiter code if infeasible
**Integration Points**
- **Promise/ATP**: Calls Transport ATP for transport availability checking
- **Transport Planning**: Uses Transport ATP for logistics planning
- **Optimizer**: Calls Transport ATP for optimization transport constraints
- **Reservations (future)**: Seat/slot holds per departure
**Key Architectural Decision: Transport Routing Knowledge Ownership**
- ✅ **Transport ATP owns Transport Routing knowledge** (for transport checking)
- ✅ **Promise uses Transport ATP** (doesn't need transport routing details)
- ✅ **Better separation**: Promise = order promising, Transport ATP = transport checking
**Telemetry / KPIs**
- Pathfinding latency, success/fail rate, cutoff misses, capacity shortfalls, limiter distribution
**ML Hooks (Optional)**
- Predict lead times/delays/reliability per leg; adjust itineraries; include model basis/version
---
### 6.12 Supplier Management
**Description**
Model suppliers and offers; provide supplier ATP (earliest feasible supply) with MOQs/lead times/reliability/cost. Allow policy-driven use (always compare vs only on shortfall) without code changes.
**Goals**
- Model suppliers and offers; provide supplier ATP (earliest feasible supply) with MOQs/lead times/reliability/cost
- Allow policy-driven use (always compare vs only on shortfall) without code changes
**Scope**
- Supplier aggregate/offer: lead times (p50/p95), MOQs, lot sizes, capacities/windows, price tiers, incoterms, reliability
- Supplier ATP service: feasibility and scoring (time/cost/reliability)
- Policies: when to query supplier (shortfall vs always), fallback behavior (block vs accept-with-risk vs counteroffer)
**Inputs**
- Supplier master/offers, capacities, MOQs, lead times, price tiers, reliability scores
- Policies from PromisePolicy/presets
**Outputs**
- Supplier options with earliest date, qty, cost, reliability; limiter if infeasible (SupplierMOQ/LeadtimeExceeded)
**Integration Points**
- Promise/ATP: invoked on shortfall or per policy
- Material Planning: can generate PurchaseOrder recommendations from supplier options
- Optimizer: reuses scoring/policies to choose supplier vs internal
**Telemetry / KPIs**
- Supplier ATP latency; hit rate; limiter distribution; cost deltas vs internal
**ML Hooks (Optional)**
- Predict lead times/reliability from historical performance; include model basis/version
---
### 5.9 Pegging & Traceability
**Description**
Maintain end-to-end traceability between demand (customer orders/forecasts) and supply (planned/firm POs/WOs/TOs) plus reservations. Enable clear visibility for ATP/CTP, replanning, and audit (what demand is covered by which supply/reservation).
**Goals**
- Maintain end-to-end traceability between demand (customer orders/forecasts) and supply (planned/firm POs/WOs/TOs) plus reservations
- Enable clear visibility for ATP/CTP, replanning, and audit (what demand is covered by which supply/reservation)
- Keep peg updates deterministic and idempotent across replans
**Scope**
- Pegs between demand lines and supply artifacts (SupplyOrders) and/or reservations (material/capacity/transport)
- Supports partial pegging (multiple supplies cover one demand; one supply covers multiple demands)
- Applies to both planned and firm supply; preserves links across replans/churn
- Out of scope: sequencing/scheduling specifics (covered in capacity/transport designs)
**Pegging Model**
- Entities:
- DemandRef: { DemandId, LineId, ProductId, StockingPointId, NeedDate, Quantity }
- SupplyRef: { SupplyOrderId, Type (PO/WO/TO), ProductId, StockingPointId, DeliveryDate, Quantity, State (Planned/Firm/…)}
- ReservationRef: { MaterialReservationId | CapacityReservationId | TransportReservationId }
- PeggingLink:
- Id (deterministic from DemandRef + SupplyRef + quantity slice)
- DemandRef
- SupplyRef or ReservationRef
- PeggedQty (decimal)
- Status: Active | Superseded | Released
- Created, Modified
- Supports partials: multiple PeggingLinks can exist for one demand; a supply can have multiple links
**Events**
- PegCreated (demand↔supply/reservation, qty)
- PegUpdated (qty/date changes)
- PegSuperseded (old peg replaced)
- PegReleased (on cancel/replan)
**Lifecycle / Replan**
- Deterministic generation: key off demand+period and supply proposal identity to avoid duplicates on rerun
- Replan rules:
- If supply proposal changes, supersede old pegs and create new ones
- If demand decreases, reduce/release pegs accordingly
- If demand cancels, release pegs
- Firm supply: pegs persist; replans should not break firm pegs unless explicitly allowed by policy
**Integration Points**
- Material Planning: when proposing SupplyOrders, create/update pegs to those proposals; subtract reservations separately
- ATP/Promise: can surface pegged supply to show coverage; reservations are already tracked separately
- Capacity/Transport: pegs can reference reservations rather than supply if holding capacity/transport slots (future)
- Replanning: pegging rebuild runs with netting/replan; ensure idempotent keys to avoid duplicates
**Policies**
- Firm peg protection: disallow changing pegs to firm supply unless policy permits
- Partial allocation rules: fill firm/earliest supplies first, then planned; policy-driven (FIFO/priority)
- Peg visibility: include peg info in promise/plan responses (optional)
- Over-peg handling: cap vs allow, with limiter
**Data / Storage**
- Pegging projection/read model:
- Keyed by DemandRef and by SupplyRef for fast lookup
- Stores active pegs and history (Superseded/Released) if audit needed
- Idempotent key: hash of DemandId+LineId+SupplyId(+slice) to prevent duplicate pegs on rerun
**Telemetry / KPIs**
- Pegging completeness (% demand pegged), peg churn (changes per replan), peg age, firm peg breakage count
**Examples**
- One demand, multiple supplies: Demand CO#1 line1 qty 100; Supply WO#A qty 60 (firm), WO#B qty 50 (planned) → pegs: CO1→WOA 60 (Active), CO1→WOB 40 (Active), net pegged 100 (10 over, policy decides to cap at 100)
- Partial reduction: demand drops to 80 → reduce/release CO1→WOB from 40 to 20 or release per policy
- Cancellation: demand cancels → release CO1→WOA and CO1→WOB pegs; supply stays available for other demand if policy allows
---
### 6.14 Campaign Management (No batching)
**Description**
Group similar operations to reduce setups; no true batching; sequencing still out of scope. Campaign management groups similar operations to reduce setups, supports sequence-dependent changeover modeling, min run and campaign length constraints, family batching and cleaning windows, and campaign reduction factor modeling.
**Goals**
- Group similar operations to reduce setups; no true batching
- Support sequence-dependent changeover modeling, min run and campaign length constraints
- Family batching and cleaning windows; campaign reduction factor modeling
**Scope**
- Operations with similarity attributes, campaign types, setup matrices, resources/calendars, priorities
- Sequence-dependent changeover: setup time depends on previous product
- Min run and campaign length constraints
- Family batching and cleaning windows (CIP - Cleaning In Place)
- Campaign reduction factor (efficiency loss for longer campaigns)
- Out of scope: true batching; detailed sequencing (handled separately)
**Inputs**
- Operations with similarity attributes (product family, setup matrix entries)
- Campaign types: product family, setup matrix, min run, campaign length
- Setup matrices: from product → to product: setup time, cleaning required
- Resources/calendars: resource groups, availability, cleaning windows (CIP)
- Priorities: operation priorities for campaign assignment
**Process (Detailed Steps)**
1) **Campaign Types & Setup Matrices**:
- Define campaign types (product family, setup matrix, min run, campaign length)
- Create setup matrix structure (from product → to product: setup time, cleaning required)
- Example: Product A → Product B: 2h setup, cleaning=yes
- Example: Family=SKU-100-*, MinRun=100, CampaignLength=500
2) **Sequence-Dependent Changeover Modeling**:
- Implement sequence-dependent changeover logic (setup time depends on previous product)
- Add changeover time calculation (from previous product to next product)
- Example: A→B: 2h, A→C: 3h, B→C: 1h
- Implement changeover optimization (minimize total changeover time)
3) **Min Run & Campaign Length Constraints**:
- Implement min run constraint (campaign must produce at least min run qty)
- Add campaign length constraint (campaign can produce up to campaign length qty)
- Example: min run=100, campaign length=500 → campaign must be 100-500 qty
- Add constraint enforcement (reject campaigns that violate min run or exceed campaign length)
4) **Family Batching & Cleaning Windows**:
- Implement family batching (group products by family for campaigns)
- Add cleaning window logic (require cleaning between families)
- Example: Family A: SKU-100-*, Family B: SKU-200-*
- Implement cleaning window enforcement (block family B after family A without cleaning)
5) **Campaign Reduction Factor Modeling**:
- Implement campaign reduction factor (efficiency loss for longer campaigns)
- Add reduction factor calculation (apply reduction factor to capacity)
- Example: campaign length >1000 → 5% efficiency loss
- Add reduction factor to capacity calculations
6) **Campaign Assignment**:
- Score similarity and setup savings; propose campaign groups
- Assign campaigns to resources/time buckets; enforce capacity/locks
- Example: assign 3 operations to 1 campaign (same family)
- Implement campaign sequencing (optimize campaign order to minimize changeover)
7) **Adherence Tracking**:
- Track campaign adherence (actual vs planned campaign execution)
- Add adherence metrics (campaign start/end variance, qty variance, setup time reduction)
- Example: planned start T+5, actual start T+6 (1 day late)
**Outputs**
- Campaign definitions/assignments, setup-saving estimates, adherence metrics
- Campaign assignments to resources/time buckets
- Setup time reduction estimates (baseline setup – campaign setup)
- Adherence reports (campaign performance dashboard)
**Rules**
- No true batching; campaigns approximate grouping; sequencing remains out of scope; respect capacity/locks
- Sequence-dependent changeover: setup time depends on previous product
- Min run and campaign length: enforce constraints per campaign type
- Family batching: group by family; require cleaning between families
- Campaign reduction factor: apply efficiency loss for longer campaigns
**UI**
- Campaign planner (create/adjust campaigns, assign to resources)
- Setup-saving indicators (time saved by campaigns vs individual setups)
- Compliance view (campaign adherence, lateness impact)
- Setup matrix editor (from product → to product: setup time, cleaning)
**Use Cases**
- Create/adjust campaign; assign to resource; assess lateness impact; monitor adherence
- Sequence-dependent changeover optimization; family batching with cleaning windows
**KPIs**
- **Setup Reduction** = (baseline setup – campaign setup) ÷ baseline setup. Measures time saved by campaigns
- **Campaign Adherence** = ops executed within campaign ÷ ops assigned to campaign. Measures compliance
- **Lateness Impact** = lateness delta with/without campaign (simulated). Evaluates whether campaigns help without hurting lateness
### 5.11 Replanning & What-if
**Description**
React to disruptions and evaluate scenarios while preserving locks and minimizing churn. The replanning system detects disruptions, classifies impact, computes affected operations/orders, proposes alternatives respecting locks, applies incremental replan or generates scenarios, scores them, and applies the chosen plan.
**Goals**
- React to disruptions and evaluate scenarios while preserving locks and minimizing churn
- Detect disruption; classify impact (material vs capacity)
- Compute affected ops/orders; propose alternatives respecting locks
- Apply incremental replan or generate scenarios; score them; apply chosen plan
**Inputs**
- **Disruption events**: Resource down, material delay, quality flags, MES variance
- **Current plan**: Existing schedule with operations, assignments, reservations
- **Locks/fixed ops**: Fixed operations that should not be moved
- **Priorities/SLAs**: Order priorities, SLA tiers, full-order/full-delivery constraints
**Process (Detailed Steps)**
1) **Disruption Listeners**:
- Create disruption event listeners (resource down, material delay, quality issues, MES variance)
- Detect disruption; classify impact (material vs capacity vs transport)
- Example: resource down → capacity impact; material delay → material impact
2) **Impact Assessment**:
- Implement impact assessment logic (assess impact of disruptions on plan)
- Add impact calculation (affected operations, promises, supply orders)
- Example: resource down → 5 operations affected, 3 promises at risk
3) **Delta Planner (Preserving Locks/Fixed)**:
- Create delta planner (minimal changes to existing plan)
- Implement lock preservation (preserve fixed operations, firm supply orders)
- Add fixed protection (do not change fixed/firm items unless policy permits)
- Example: change only affected operations, preserve fixed items
4) **Minimal-Move Strategy**:
- Implement minimal-move strategy (minimize changes to existing plan)
- Add churn minimization (minimize operation/supply order changes)
- Example: move only 2 operations instead of 10
5) **Rollback/Fallback**:
- Implement rollback logic (revert to previous plan if new plan worse)
- Add fallback strategy (fallback to heuristic if solver fails)
- Example: new plan has more lateness → rollback to previous
6) **Scenario Runner (Snapshot/Apply/Compare)**:
- Create scenario snapshot logic (snapshot current plan state)
- Implement scenario application (apply what-if changes to snapshot)
- Add scenario comparison (compare scenarios: finite vs infinite, buffer variations)
- Example: snapshot → apply 10% buffer increase → compare outcomes
7) **What-If Config Structure**:
- Define what-if config structure (policy overrides: buffers, finite/infinite, supplier usage, green preference)
- Implement policy override application (apply overrides to base policy)
- Example: what-if config = { buffer: +10%, finite: true, supplierUsage: always }
8) **Scenario Diffing/Reporting**:
- Implement scenario diffing (compare scenario outcomes: promise dates, overloads, cost)
- Add diff reporting (report deltas between scenarios)
- Example: scenario A vs B → 3 promises later, 2 overloads reduced, cost +$100
9) **Plan Delta Emission**:
- Implement plan delta emission (emit changes from old plan to new plan)
- Add delta structure (operations added/removed/changed, supply orders added/removed/changed)
- Example: Operation-1 moved from T+5 to T+7, SupplyOrder-2 qty increased 100→120
**Outputs**
- Replan proposals (alternative plans with minimal changes)
- Applied plan updates (operations moved, supply orders adjusted)
- Scenario scores/diffs (comparison of scenarios with metrics)
- Plan delta (changes from old plan to new plan)
**Rules**
- Preserve locks/fixed; minimize churn; respect priorities and full-order/full-delivery
- Deterministic replan: same disruption → same replan result (idempotent)
- Firm peg protection: do not break firm pegs unless policy permits
**UI**
- Disruption alerts (resource down, material delay, quality issues)
- Proposed moves (operations to move, new assignments)
- Scenario comparison with scores (finite vs infinite, buffer variations)
- Plan delta view (changes from old plan to new plan)
**Use Cases**
- Machine down; material delay; due-date move what-if; choose lowest-lateness scenario
- What-if analysis: compare scenarios with different policies (buffers, finite/infinite, supplier usage)
**KPIs**
- **Replan Latency** = time from disruption to plan update. Low → faster response to disruptions
- **Stability** = unchanged ops after replan ÷ total ops. Low → tune policies to reduce churn. Target: 80%+ for stable schedules
- **Service Preservation** = orders still on-time after replan ÷ total accepted. High → replan maintains service levels
### 6.16 Multi-Objective Optimization Module
**Description**
Optimization is a **separate, cross-domain module** that improves planning results beyond heuristics. It optimizes across material + capacity + transport together using mathematical optimization (MILP). The optimizer **reuses functions** from MRP, Capacity CTP, and Transport ATP (no duplication) but remains a separate module because it optimizes more than just material.
**Purpose**
Improve planning results through multi-objective optimization (better than heuristics)
**Responsibilities**:
- ✅ Optimize across material + capacity + transport together (cross-domain)
- ✅ Multi-objective optimization (cost, time, CO2, utilization)
- ✅ Replace heuristic results with optimized plan
- ✅ Reuse MRP functions (BOM explosion, netting) - no duplication
- ✅ Reuse Capacity CTP and Transport ATP - no duplication
**Key Characteristics**:
- **Separate Module**: Not sub-module of MRP (optimizes more than just material)
- **Cross-Domain**: Optimizes material + capacity + transport together
- **Reuses Functions**: Calls MRP functions, Capacity CTP, Transport ATP
- **Nightly Batch**: Runs overnight to optimize all orders
**When to Run**:
- **Daytime**: Heuristic MRP (fast, per-order, minutes)
- **Nighttime**: Full Optimizer (better results, all orders, hours)
**Process Flow**:
```
┌─────────────────────────────────────────────────────────────┐
│ OPTIMIZATION MODULE │
│ (Cross-Domain, Multi-Objective) │
│ │
│ Input: All Demands (from all orders) │
│ │
│ Step 1: REUSE MRP FUNCTIONS (No Duplication) │
│ └─> Call MRP.bomExplosion() (reuse) │
│ └─> Call MRP.materialNetting() (reuse) │
│ │
│ Step 2: REUSE CAPACITY CTP │
│ └─> Call CapacityCTP.CheckCapacity() (reuse) │
│ │
│ Step 3: REUSE TRANSPORT ATP │
│ └─> Call TransportATP.GetOptions() (reuse) │
│ │
│ Step 4: BUILD OPTIMIZATION MODEL │
│ └─> Decision Variables: │
│ - Production quantities per routing/step/time │
│ - Purchase quantities per supplier/time │
│ - Transport quantities per leg/time │
│ └─> Constraints: │
│ - Material balance │
│ - Capacity limits │
│ - Transport capacity │
│ - BOM relationships │
│ └─> Objectives: │
│ - Minimize lateness │
│ - Minimize cost │
│ - Maximize utilization │
│ - Minimize CO2 │
│ │
│ Step 5: SOLVE (MILP Solver) │
│ └─> Run CPLEX/OR-Tools solver │
│ └─> Find optimal solution │
│ └─> Fallback to heuristic if timeout │
│ │
│ Step 6: REPLACE HEURISTIC RESULTS │
│ └─> Replace heuristic supply orders with optimized │
│ └─> Update work order dates │
│ └─> Ready for next day execution │
│ │
│ Output: Optimized plan (better than heuristic) │
└─────────────────────────────────────────────────────────────┘
```
**Why Separate Module?**
- ✅ **Cross-Domain**: Optimizes material + capacity + transport (not just material)
- ✅ **Reuses Functions**: Calls MRP functions, Capacity CTP, Transport ATP (no duplication)
- ✅ **Separation of Concerns**: MRP = planning logic, Optimizer = optimization logic
- ✅ **Flexibility**: Optimizer can evolve independently
**Integration Points**:
- **Reuses**: MRP functions (BOM explosion, netting), Capacity CTP, Transport ATP
- **Replaces**: Heuristic MRP results (with optimized plan)
- **Purpose**: Improve overall plan quality
**Goals**
- Optional solver run to improve objectives (delivery, utilization, inventory/cost/carbon) with heuristic fallback
- Mathematical optimization (MILP) for batch planning with comprehensive constraints
- Support CPG/manufacturing considerations: sequencing, changeovers, campaigns, shelf-life
**When to Run What**
- **Daytime Heuristic MRP**: Fast path (minutes per order); use cached availability; good enough results for immediate execution
- **Nighttime Full Optimizer**: Many demands, multi-echelon, cost/CO2/churn minimization, pegging. Run on cadence (hourly/shift/daily) or when major deltas occur (calendar/leg outage, large demand spike). Uses full MILP with broader horizon and richer constraints. **Replaces heuristic results** with optimized plan
- **Hybrid Approach**: Heuristic during day (fast), Optimizer at night (better results)
**Modeling Principles**
- Policy-driven toggles: finite vs infinite capacity, emissions/cost caps, acceptance policy, churn weight
- Idempotent, deterministic keys outside the solver (reservations/pegs) to avoid double-allocation
- Tight bounds and small big-M; prefer caps/slacks with penalties over infeasibility
- Piecewise-linear where needed; bucketed time (hours/days) aligned to operations cutoffs
**Sets and Indices**
- \(p \in P\): products/SKUs
- \(s \in S\): stocking points
- \(d \in D\): demand lines
- \(t \in T\): time buckets (ordered)
- \(r \in R_p\): routings/alternates for product \(p\)
- \(k \in K_{p,r}\): steps of routing \(r\)
- \(g \in G\): resource groups
- \(m \in M_{s \to s'}\): transport legs/modes
- \(v \in V\): suppliers
- \(c \in C\): components
- \(b \in B_p\): BOM entries for product \(p\)
- \(h \in H_d\): candidate itineraries (precomputed K-shortest)
- \(l \in L\): production lines (subset/specialization of resource groups) for sequencing/changeovers
**Parameters (Nonnegative Unless Noted)**
- Demand: \(D_d\), due bucket \(\tau^{due}_d\), priority weight \(w^{prio}_d\), earliness/lateness weights \(w^{early}_d, w^{late}_d\)
- Inventory/supply: on-hand \(Inv_{p,s}\); firm inbound \(Inb_{p,s,t}\); reservations to subtract \(Resv^{mat}_{p,s,t}\); safety floor \(Safety_{p,s}\)
- BOM: \(BOM_{c,p}\) (units of component \(c\) per unit of \(p\))
- Production: lead \(LT^{prod}_{p,r,k}\); duration rate \(Dur_{p,r,k}(\cdot)\); step yield \(Yield_{p,r,k}\); cost \(Cost^{prod}_{p,r,k}\); feasible resources \(AltRes_{p,r,k}\)
- Capacity: available \(Cap_{g,t}\); reserved \(Resv^{cap}_{g,t}\); mode toggle \(Mode^{cap} \in \{\text{finite}, \text{infinite}\}\)
- Transport: capacity \(Cap^{trans}_{m,t}\); reserved \(Resv^{trans}_{m,t}\); lead \(LT^{trans}_m\); cutoff mask \(Cutoff_{m,t} \in \{0,1\}\); cost \(Cost^{trans}_{m,t}\); emissions \(CO2^{trans}_m\); reliability \(Rel^{trans}_m\)
- Supplier: \(MOQ_{v,p,s}\), \(Lot_{v,p,s}\), lead \(LT^{sup}_{v,p,s}\), cost \(Cost^{sup}_{v,p,s}\), reliability \(Rel^{sup}_{v,p,s}\)
- Sequencing & changeover: setup time \(SetupTime_{i,j,l}\) and cost \(SetupCost_{i,j,l}\) for switching from item \(i\) to \(j\) on line \(l\); family map \(Family_{p}\); min run \(MinRun_{p,l}\); min campaign length \(MinCamp_{p,l}\); max campaign length \(MaxCamp_{p,l}\); cleaning windows \(CIP_{l,t} \in \{0,1\}\) (unavailable if 0)
- Transport preference & cost detail: fixed depart cost \(FixCost^{trans}_{m,t}\); variable cost \(VarCost^{trans}_{m,t}\); lane preference penalty \(PrefPen_{m}\); min fill \(MinFill_{m}\)
- Shelf-life / freshness (CPG): shelf-life \(Life_{p}\) (buckets), minimum remaining life at ship \(MinLife_{p}\)
- Replenishment policies: min/max \(MinInv_{p,s}, MaxInv_{p,s}\); cover-days target \(Cover_{p,s}\)
- Policy weights: \(w^{cost}, w^{late}, w^{early}, w^{util}, w^{co2}, w^{churn}, w^{risk}\)
- Churn reference: \(PlanPrev^{prod}_{p,r,k,t}\), \(PlanPrev^{trans}_{m,t}\)
- Caps (optional): emissions \(CO2^{max}\), cost \(Budget^{cost}\), reliability floor \(Rel^{min}\)
- Big-M: tight upper bounds from demand/throughput; avoid loose values
**Decision Variables**
- \(x^{prod}_{p,r,k,t} \ge 0\): qty completing step \(k\) of routing \(r\) at time \(t\)
- \(x^{sup}_{v,p,s,t} \ge 0\): qty from supplier \(v\) arriving at \(s,t\)
- \(x^{trans}_{m,t} \ge 0\): qty shipped on leg \(m\) departing \(t\)
- \(Inv^{end}_{p,s,t} \ge 0\): end-of-bucket inventory
- \(y_{d,t} \ge 0\): demand \(d\) fulfilled at \(t\)
- \(Back_d, Early_d \ge 0\): lateness / earliness slack
- \(z^{route}_{p,r} \in \{0,1\}\): routing selection (optional exclusivity)
- \(z^{res}_{p,r,k,g} \in \{0,1\}\): resource-group selection (optional)
- \(u^{sup}_{v,p,s,t} \in \mathbb{Z}_{\ge 0},\; b^{sup}_{v,p,s,t} \in \{0,1\}\): MOQ/lot binaries
- \(OverUtil_{g,t} \ge 0\): capacity overload slack (finite mode)
- Reservations (if created by optimizer): \(resv^{mat}_{p,s,t}, resv^{cap}_{g,t}, resv^{trans}_{m,t} \ge 0\)
- Churn: \(dev^{prod}_{p,r,k,t} \ge 0\), \(dev^{trans}_{m,t} \ge 0\)
- Acceptance (optional): \(acc_d \in \{0,1\}\)
- Sequencing/changeover: \(start_{p,l,t} \in \{0,1\}\) (line \(l\) starts product \(p\) at \(t\)); \(seq_{i,j,l,t} \in \{0,1\}\) (changeover from \(i\) to \(j\)); \(run_{p,l,t} \ge 0\) time used by \(p\) on line \(l\) at \(t\)
- Campaign/family: \(camp_{p,l} \in \{0,1\}\) (campaign active); \(fam_{f,l,t} \in \{0,1\}\) for family \(f\) if batching by family
- Shelf-life: \(Inv^{age}_{p,s,t,a}\) (inventory of age \(a\)); \(ship^{age}_{p,s,t,a}\) shipped of age \(a\) (age-bucketed freshness)
**Objective**
Weighted composite (can be turned into lexicographic by staged solves):
\[
\min \;
w^{late}\!\sum_d w^{prio}_d Back_d
+ w^{early}\!\sum_d w^{prio}_d Early_d
+ w^{cost}(\text{Prod}+\text{Sup}+\text{Trans}+\text{Hold}+\text{Setup})
+ w^{util}\!\sum_{g,t} OverUtil_{g,t}
+ w^{co2}\!\sum_{m,t} CO2^{trans}_m x^{trans}_{m,t}
+ w^{churn}(\sum dev^{prod}+\sum dev^{trans})
- w^{risk}(\sum Rel^{trans}_m x^{trans}_{m,t} + \sum Rel^{sup}_{v,p,s} x^{sup}_{v,p,s,t})
+ \text{LanePrefPenalty} + \text{MinFillPenalty}
\]
Caps (optional hard):
\[
\sum_{m,t} CO2^{trans}_m x^{trans}_{m,t} \le CO2^{max}, \quad
\text{TotalCost} \le Budget^{cost}
\]
**Constraints (Key Categories)**
1. **Demand fulfillment & tardiness**: \(\sum_t y_{d,t} = D_d\); \(Back_d \ge \sum_{t > \tau^{due}_d} y_{d,t}\); \(Early_d \ge \sum_{t < \tau^{due}_d} y_{d,t}\)
2. **Inventory balance + safety**: \(Inv^{end}_{p,s,t} = Inv^{end}_{p,s,t-1} + Inflow_{p,s,t} - Outflow_{p,s,t}\); \(Inv^{end}_{p,s,t} \ge Safety_{p,s}\)
3. **BOM consumption**: \(\sum_{r,k} x^{prod}_{p,r,k,t'} \cdot BOM_{c,p} \le \text{AvailComponent}_{c,t'}\)
4. **Material reservations subtraction**: \(Outflow_{p,s,t} \le Inv^{end}_{p,s,t-1} + Inb_{p,s,t} - Resv^{mat}_{p,s,t}\)
5. **Routing validity & precedence with yield**: \(x^{prod}_{p,r,k,t} \le M \cdot Valid_{p,r,t} \cdot z^{route}_{p,r}\); \(x^{prod}_{p,r,k+1,t} \le \sum_{t' \le t - LT^{prod}_{p,r,k+1}} x^{prod}_{p,r,k,t'} \cdot Yield_{p,r,k}\)
6. **Capacity (finite/infinite toggle)**: \(\sum_{p,r,k:g\in AltRes} timeReq_{p,r,k,g} \cdot x^{prod}_{p,r,k,t} \le Cap_{g,t} - Resv^{cap}_{g,t} + OverUtil_{g,t}\)
7. **Transport capacity, cutoff, lead**: \(x^{trans}_{m,t} \le Cap^{trans}_{m,t} - Resv^{trans}_{m,t}\); \(x^{trans}_{m,t} \le M \cdot Cutoff_{m,t}\)
8. **Supplier MOQ / lot sizing**: \(x^{sup}_{v,p,s,t} = Lot_{v,p,s} \cdot u^{sup}_{v,p,s,t}\); \(x^{sup}_{v,p,s,t} \ge MOQ_{v,p,s} \cdot b^{sup}_{v,p,s,t}\)
9. **Churn (plan stability)**: \(dev^{prod}_{p,r,k,t} \ge |x^{prod}_{p,r,k,t} - PlanPrev^{prod}_{p,r,k,t}|\)
10. **Sequence-dependent setup / changeover**: \(\sum_{p} run_{p,l,t} + \sum_{i,j} SetupTime_{i,j,l} \cdot seq_{i,j,l,t} \le Cap_{l,t}\)
11. **Minimum run and campaign length**: \(run_{p,l,t} \ge MinRun_{p,l} \cdot start_{p,l,t}\); \(MinCamp_{p,l} \cdot camp_{p,l} \le \sum_t run_{p,l,t} \le MaxCamp_{p,l} \cdot camp_{p,l}\)
12. **Family batching / cleaning (CIP) windows**: \(run_{p,l,t} \le M \cdot fam_{Family_p,l,t}\); \(run_{p,l,t} \le M \cdot CIP_{l,t}\)
13. **Shelf-life / freshness (CPG)**: \(Inv^{age}_{p,s,t+1,a+1} = Inv^{age}_{p,s,t,a} - ship^{age}_{p,s,t,a}\); \(ship^{age}_{p,s,t,a} = 0\) if \(a + Transit > Life_p - MinLife_p\)
14. **Replenishment policy (min/max, cover days)**: \(Inv^{end}_{p,s,t} \ge MinInv_{p,s}\); \(Inv^{end}_{p,s,t} \le MaxInv_{p,s}\); \(Inv^{end}_{p,s,t} \ge Cover_{p,s} \cdot \text{AvgDemand}_{p,s}\)
**Inputs**
- Problem: operations, resources, constraints, precedence, calendars, locks
- Objectives: lateness/tardiness, utilization, inventory/expedite cost, carbon, churn
- Parameters: weights/epsilon, time limits, gap targets
**Process**
1) Build model via abstraction (solver abstraction layer)
2) Run solver (CPLEX/OR-Tools or custom heuristic)
3) Fallback to heuristic on timeout/gap
4) Generate solutions/Pareto frontier; compute objective scores
5) Explain trade-offs vs baseline
**Outputs**
- Optimized schedules, Pareto set, objective scorecards, explanations
- Plan delta (changes from baseline to optimized)
**Rules**
- Same constraints as core scheduler; respect locks; fallback required
- Optimizer reuses PromiseOrchestrator/providers with different policy/search budget
- No duplicated business rules; optimizer calls same orchestrator
**UI**
- Run summary (solver runtime, gap, objective scores)
- Pareto chart (trade-offs between objectives)
- Solution picker (choose from Pareto set)
- Objective breakdown (lateness, cost, utilization, CO2, churn)
**Use Cases**
- Full optimization run; compare heuristic vs optimized; select Pareto candidate
- What-if analysis: optimize with different policy weights
**KPIs**
- **Objective Attainment** = achieved objective value ÷ target. High → optimization effective
- **Solver Runtime** = time to solve. Over limit → fallback to heuristic
- **Gap** = (best bound – solution) ÷ best bound. Low → solution quality high
- **Pareto Diversity** = spread of solutions in objective space. High → good trade-off exploration
---
## 7. Validation & Quality Assurance
### 7.1 Validation Approach
**Feature-Level Validation:**
1. **Algorithm Testing**: Mathematical validation of optimization algorithms
2. **Integration Testing**: Cross-system data flow and communication validation
3. **Performance Testing**: Computational performance and scalability validation
4. **Business Validation**: Real-world scenario testing with domain experts
**System-Level Validation:**
1. **End-to-End Testing**: Complete planning workflow validation
2. **Load Testing**: High-volume planning scenario validation
3. **Stress Testing**: Peak load and failure scenario validation
4. **User Acceptance Testing**: Business user validation of planning results
**Quality Assurance Standards:**
- **Algorithm Validation**: Mathematical proof of algorithm correctness where possible
- **Performance Testing**: Comprehensive performance testing for all planning operations
- **Integration Testing**: End-to-end testing of planning workflows
- **Business Validation**: Domain expert validation of planning results
- **Code Coverage**: Automated testing with >95% coverage
### 7.2 Development Best Practices
**Optimization Algorithm Standards:**
- **Proven Algorithms**: Use industry-standard optimization techniques
- **Performance Benchmarking**: Establish performance baselines for all algorithms
- **Scalability Testing**: Validate algorithm performance at scale
- **Result Validation**: Comprehensive validation of optimization results
**Planning System Patterns:**
- **Event-Driven Updates**: All planning triggered by business events
- **Incremental Planning**: Support for partial schedule updates
- **Constraint Management**: Robust constraint satisfaction and validation
- **Result Explanation**: Clear explanation of planning decisions and trade-offs
---
## 8. Other Functionality
### 8.1 System Health Monitoring
**Monitoring Requirements:**
- Real-time monitoring of planning performance
- Algorithm performance tracking and benchmarking
- System reliability monitoring (99.5% uptime target)
- Planning result quality validation
- Disruption detection and alerting
**Health Checks:**
- Planning engine availability
- Optimization solver health
- Event processing latency
- Database connectivity
- Integration endpoint health
### 6.2 Configuration Management
#### Description and Relevancy
System health monitoring ensures the reliability and availability of the ProductionPlanning platform.
#### Inputs, Process and Outputs
**Inputs:** system metrics (CPU/mem), service liveness/readiness, ingest lag, queue depth, error rates.
**Process:** automated health checks; SLO/SLA alerting; synthetic probes for critical APIs; circuit breakers/back-pressure on ingest.
**Outputs:** health dashboards, alerts (pager/email), maintenance notifications, SLO burn-rate reports.
#### Knowledge
Health check rules and thresholds for API, scheduler, MRP, ingestion, EventStoreDB, database, cache, and message queues.
#### Graphical User Interface
Health dashboard with component status, SLO gauges, ingest lag charts, and recent incidents.
#### Use Cases
1. Startup health verification
2. Continuous health monitoring with alerts
3. Automated recovery (restart, failover) when checks fail
4. Operator drill-down into failing component metrics/logs
#### Automation
Fully automated checks and alerting; automated remediation where safe (restart, scale-up), manual approval for higher risk actions.
#### Assumptions
Monitoring/alerting stack deployed; runbooks defined for critical alerts; SLOs defined per flow (ingest, acceptance, run, replan).
### 6.2 Configuration Management
#### Description and Relevancy
Configuration management enables dynamic system configuration without code changes.
#### Inputs, Process and Outputs
**Inputs:** configuration files/values (alg parameters, SLOs, feature flags), environment overrides, secrets.
**Process:** schema validation; safe reload; audit trail of changes; drift detection across environments.
**Outputs:** applied configuration state, change history, validation errors.
#### Knowledge
Configuration schemas, validation rules, defaults, and precedence (env > file > default).
#### Graphical User Interface
Configuration editor with validation feedback, diff to previous, and approval workflow for risky changes.
#### Use Cases
1. Tune planning parameters (e.g., slack, priorities) without redeploy.
2. Environment-specific overrides (dev/test/prod).
3. Enable/disable features (heuristic acceptance path, solver runs).
4. Secrets rotation (handled via secure store).
#### Automation
Semi-automated with validation and role-based approval; automatic propagation after approval.
#### Assumptions
Configs are versioned; rollbacks possible; changes validated in lower envs before prod.
### 6.3 Audit Logging
#### Description and Relevancy
Audit logging provides complete traceability of planning decisions and system activities.
#### Inputs, Process and Outputs
**Inputs:** domain events (decisions, assignments, replans), user actions (overrides, approvals), config changes.
**Process:** write-once event logs (EventStoreDB), structured audit stream; retention and access controls.
**Outputs:** audit read model (for UI later), compliance exports, change history per entity.
#### Knowledge
Audit requirements, retention policies, access controls, PII stance.
#### Graphical User Interface
Deferred UI: planned audit viewer (filter/search by order, resource, user, time, action).
#### Use Cases
1. Trace why/when an order was promised/replanned.
2. Track who changed config/overrode a decision.
3. Export audit for compliance.
#### Automation
Fully automated event capture; retention configurable; access logged.
#### Assumptions
EventStoreDB is durable; retention storage sized; audit UI deferred to later phase.
---
## 9. Application/IT Environment
### 9.1 Scale and Performance
**Scale**
- Number of products: < 10,000
- Number of plants: < 20
- Number of resources: < 1,000
- Number of concurrent users: < 50
- Planning horizon: 3-6 months
- Operations per full run: target 50,000+
**Performance**
- Planning run: < 30 minutes (full MRP + capacity)
- Order acceptance: < 5 seconds (online heuristic)
- Replan application: < 2 seconds for schedule updates
- Real-time updates: < 2 seconds
- What-if analysis: < 10 seconds for scenario evaluation
- Startup: < 60 seconds
- Memory usage: < 4GB under load
**Data Processing Requirements**
- Event throughput: Process 1,000+ planning events per minute
- BOM processing: Handle 10,000+ BOM levels efficiently
- Optimization: Solve complex scheduling problems in real-time
**Success Metrics**
- **Technical Success Metrics**:
- Planning performance: <30 minute planning cycles, <5 second order acceptance
- Schedule quality: 95%+ constraint satisfaction, 90%+ resource utilization
- Optimization accuracy: 85%+ improvement over manual planning
- System reliability: 99.5% uptime for planning operations
- Replanning speed: <2 seconds for schedule adjustments
- **Business Success Metrics**:
- Resource utilization: 90% average utilization across production resources
- On-time delivery: 95% on-time delivery achievement
- Inventory optimization: 98% material availability with <10% excess inventory
- Planning efficiency: 20% reduction in planning effort and time
- Cost reduction: 25% reduction in production costs through optimization
### 9.2 Security Provisioning
**Authentication**
- Integration with enterprise identity providers
- Role-based access control
- API key management for external systems
**Authorization**
- Planner roles with least privilege
- Data-level security for multi-plant operations
- Audit trails for all planning changes
**Communication Security**
- TLS 1.3 encryption for all communications
- Secure API gateways (rate limits, WAF)
- Database encryption at rest and in transit
- Secrets managed via secure store
---
## 10. Integration
### 10.1 General
ProductionPlanning natively supports integration using standard data structures including:
- Production orders and operations
- Inventory levels and movements
- Resource availability and utilization
- Material requirements and allocations
**Integration Architecture:**
- Event-driven integration with Nexus for demand signals and master data enrichment
- REST and GraphQL APIs for external system communication
- WebSocket/SignalR for real-time updates
- Message queues for async communication
- API gateway for external access with rate limiting and security
**Critical Integration Points:**
- **Nexus**: Demand signals and master data enrichment
- **ERP Systems**: Product, BOM, and supplier master data
- **MES Systems**: Shop floor execution and progress data
- **Resource Management**: Capacity and availability data
- **Quality Systems**: Quality specifications and results
- Work order status and progress
- Calendars, routings (with alternates), BOMs, reservations, forecasts
### 9.2 Nexus Integration
**Event-Driven Communication:**
- Subscribes to Nexus event streams for demand signals
- Publishes planning results as events to Nexus
- Real-time synchronization of production schedules
**API Integration:**
- REST APIs for demand signal consumption
- WebSocket connections for real-time updates
- GraphQL endpoints for complex queries
- Contracts: idempotent ingest (by message ID), schema validation, lag and error metrics, back-pressure handling.
### 10.3 ERP Integration
**Data Exchange:**
- Standard integration interfaces for order data
- Material master data synchronization
- Production order status updates
- Inventory level reconciliation
- BOM/routing/alternate updates; supplier/lead-time updates; reservations and POs back to ERP.
- Idempotent upsert to EventStore; validation and missing-reference reporting; retries with DLQ.
**Supported Systems:**
- SAP ERP
- Oracle ERP
- Microsoft Dynamics
- Custom ERP systems via APIs
### 8.4 MES Integration
**Shop Floor Integration:**
- Work order release and tracking
- Operation status updates
- Resource utilization feedback
- Quality control data exchange
- Partial completions, scrap, rework triggers.
**Real-Time Synchronization:**
- Event-driven updates from shop floor
- Automated replanning based on actual progress
- Performance metric collection
- Contracts: at-least-once event ingestion with idempotency; reconciliation rules for quantity/time variance; latency/lag metrics.
---
## 11. Appendix A: Technical Architecture
### 11.1 System Components
#### Planning Engine
- Finite capacity scheduling algorithms
- Material requirements planning (MRP)
- Advanced optimization solvers (CPLEX/OR-Tools integration)
- Real-time replanning capabilities
- Campaign optimization for setup minimization
- Multi-objective optimization (cost, delivery, carbon, quality trade-offs)
#### Optimization Algorithm Architecture
- **CPLEX/OR-Tools Integration**: Industry-standard optimization solvers for complex scheduling problems
- **Custom Algorithms**:
- Finite capacity constraint-based scheduling
- Material optimization (lot sizing, inventory algorithms)
- Campaign optimization (setup minimization algorithms)
- Multi-objective Pareto optimization for trade-off analysis
- **Real-Time Planning Pipeline**: Event-driven planning updates triggered by business events
- **Incremental Planning**: Support for partial schedule updates without full replan
- **Constraint Management**: Robust constraint satisfaction and validation
- **Result Explanation**: Clear explanation of planning decisions and trade-offs
#### Data Management
- EventStoreDB for event sourcing
- PostgreSQL for read models
- In-memory caching for performance
- Distributed data synchronization
#### Integration Layer
- REST and GraphQL APIs
- WebSocket for real-time updates
- Message queues for async communication
- API gateway for external access
#### User Interface
- Web-based planning dashboard
- Real-time visualization components
- Drag-and-drop scheduling interface
- Mobile-responsive design
### 11.2 Data Flow Architecture
```
Nexus (Demand Events)
ProductionPlanning Engine
↓ (Planning Decisions)
EventStoreDB (Schedule Events)
MES/Shop Floor Systems
```
### 11.3 Scalability Analysis
**Current Load:** 1000 operations/day
**Target Load:** 50,000 operations/day
**Scaling Strategy:** Horizontal scaling with Kubernetes
**Performance Baseline:** Sub-30 minute planning cycles
**Scalability Requirements:**
- **Operations**: Support for 50,000+ operations per planning run
- **Resources**: Handle 1,000+ resources with capacity constraints
- **Time Horizon**: 3-6 month planning horizon
- **Concurrent Users**: Support 50+ concurrent planning users
- **BOM Processing**: Handle 10,000+ BOM levels efficiently
- **Event Throughput**: Process 1,000+ planning events per minute
**Performance Targets:**
- **Planning Run Time**: <30 minutes for full optimization
- **Order Acceptance**: <5 seconds for individual orders (online heuristic)
- **Schedule Updates**: <2 seconds for real-time adjustments
- **What-If Analysis**: <10 seconds for scenario evaluation
- **Memory Usage**: <4GB under normal load
---
## 12. Appendix B: Terminology
| Term | Description |
| --------------- | --------------------------------------------------- |
| APS | Advanced Planning & Scheduling system |
| MRP | Material Requirements Planning |
| Finite Capacity | Resource scheduling with capacity constraints |
| Campaign | Grouped production operations for efficiency |
| Work Order | Executable production order for shop floor |
| Event Sourcing | Persistence pattern storing state changes as events |
| CQRS | Command Query Responsibility Segregation pattern |
| BOM | Bill of Materials defining product structure |
---
## 12. Appendix C: Implementation Roadmap
### Phase 1: Core Planning Engine (Weeks 1-8)
- [ ] Implement basic order acceptance logic
- [ ] Set up EventStoreDB integration
- [ ] Create material requirements planning
- [ ] Build finite capacity scheduling foundation
### Phase 2: Advanced Optimization (Weeks 9-16)
- [ ] Integrate optimization solvers (CPLEX/OR-Tools)
- [ ] Implement campaign management
- [ ] Add real-time replanning capabilities
- [ ] Create work order generation logic
### Phase 3: Integration & UI (Weeks 17-24)
- [ ] Build Nexus integration layer
- [ ] Implement ERP/MES connectors
- [ ] Create web-based planning interface
- [ ] Add real-time dashboard capabilities
### Phase 4: Enterprise Features (Weeks 25-32)
- [ ] Implement advanced security and audit logging
- [ ] Add configuration management
- [ ] Create monitoring and alerting
- [ ] Performance optimization and scaling
---
**Document Control:**
- **Version**: 1.0
- **Status**: Draft
- **Review Date**: Monthly
- **Owner**: Medhāvī Development Team
- **Architecture**: Nexus → ProductionPlanning → MES
- **Integration**: Event-driven with REST APIs
---
# Medhāvī ProductionPlanning — Optimizer (Mathematical Model)
**Status**: ✅ Stage 2 Complete — Full Mathematical Specification with Equations
**Version**: 2.0
**Last Updated**: 2026-01-04
**Scope**: Cross-domain planning optimization (material + capacity + transport + campaigns) per `documents/Planning.md`
**References**: Industry best practices from MPS optimization patterns
**Non-goal**: Business logic inside optimizer (policies/eligibility live in upstream providers/services; optimizer consumes normalized inputs).
---
## Document Overview
This document provides a complete mathematical specification of the Medhāvī ProductionPlanning optimizer. It includes:
| Section | Content | Purpose |
| ------- | -------------------- | ----------------------------------- |
| §0 | Purpose & Principles | Design philosophy |
| §1 | Inputs/Outputs | Data contracts (F# types) |
| §2 | Modeling Scope | Time representation, modes |
| §3 | Sets & Indices | Mathematical domains |
| §4 | Parameters | Input data specification |
| §5 | Decision Variables | Optimizer choices |
| §6 | Support Variables | Slacks for soft constraints |
| §7 | Constraints | Mathematical equations |
| §8 | Objective Function | Weighted sum formulation |
| §9 | Explainability | Limiter framework |
| §10-14 | Implementation | Post-processing, testing, decisions |
---
## 0. Purpose & Principles
### 0.1 Purpose
The Optimizer solves the **Master Production Scheduling (MPS)** problem: given customer demands, available resources, materials, and transport options, determine an optimal production and fulfillment plan that minimizes costs and delays while respecting all physical and business constraints.
**When the Optimizer Runs:**
| Mode | Trigger | Horizon | Time Limit | Purpose |
| ----------------- | ------------------------------- | ----------------- | ------------- | -------------------------------- |
| **Nightly Batch** | Scheduled (e.g., 2 AM) | Full (3-6 months) | 30 min - 4 hr | Comprehensive plan for next day |
| **On-Demand** | User request / major disruption | Full or partial | 5-30 min | Replan after significant changes |
| **What-If** | Scenario analysis | Full | 10-30 min | Compare alternative policies |
**Core Question Answered:**
> "How should we fulfill all demands by deciding: (1) what to produce, when, and where; (2) what to purchase and from whom; (3) what to transport and via which routes; while minimizing lateness, cost, and other KPIs?"
### 0.2 Design Principles
#### Principle 1: Abstraction-First
The optimizer consumes **normalized, pre-validated sets and parameters**. It does not:
- Validate data (ACL does this)
- Compute eligibility rules (providers do this)
- Apply customer-specific policies (encoded in weights/parameters)
```
┌─────────────────────────────────────────────────────────────┐
│ UPSTREAM (Pre-Optimizer) │
│ ┌─────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ ACL │→ │ Material │→ │ Capacity │→ │Transport │ │
│ │(Validate)│ │ Provider │ │ Provider │ │ Provider │ │
│ └─────────┘ └──────────┘ └──────────┘ └──────────┘ │
│ ↓ Normalized Data │
├─────────────────────────────────────────────────────────────┤
│ OPTIMIZER (This Document) │
│ - Consumes: Sets, Parameters (no business logic) │
│ - Decides: Variables (production, transport, assignment) │
│ - Outputs: Optimal plan + explainability │
├─────────────────────────────────────────────────────────────┤
│ DOWNSTREAM (Post-Optimizer) │
│ - Convert variables → domain events (WO, PO, TO) │
│ - Generate pegging links │
│ - Emit KPI scorecard │
└─────────────────────────────────────────────────────────────┘
```
#### Principle 2: No Duplicated Business Rules
All business rules are encoded in **sets and parameters**, not constraint logic:
- Routing eligibility → `Valid_{r,t}` mask (precomputed)
- Customer priority → `w^{prio}_d` weight (parameter)
- Supplier preferences → `Pref^{sup}_v` score (parameter)
#### Principle 3: Deterministic/Idempotent Outputs
Given the same inputs, the optimizer produces the same outputs:
- Stable variable ordering
- Deterministic ID generation for artifacts
- No reliance on solver-internal randomness (seed fixed)
#### Principle 4: KPI-Aligned
Every slack/penalty variable maps directly to a measurable KPI:
```
Slack Variable → KPI → Business Metric
─────────────────────────────────────────
Back_d → DeliveryPerformance → OTIF%
Short_d → DeliveryFulfillment → Fill Rate%
OverUtil_{g,t} → CapacityOverload → Overload Hours
```
### 0.3 Optimizer vs Provider Boundaries
| Decision | Who Decides | How |
| --------------------------------- | ------------------ | -------------------------------------------------- |
| **Which routings are valid?** | Routing Provider | Sets `R_{p,s}` and `Valid_{r,t}` |
| **Which routing to use?** | Optimizer | Variables `z^{route}_{d,r}` |
| **What's the material position?** | Material Provider | Parameters `Inv^{init}`, `Inb^{firm}` |
| **How much to produce?** | Optimizer | Variables `x^{prod}_{r,k,t}` |
| **Capacity availability?** | Capacity Provider | Parameters `Cap_{g,t}`, `Alloc^{firm}` |
| **Transport options?** | Transport Provider | Sets `M`, parameters `Cap^{trans}`, `Cost^{trans}` |
| **Which transport to use?** | Optimizer | Variables `x^{trans}_{m,p,t}` |
### 0.4 KPI Taxonomy
```
KPI HIERARCHY
├── DELIVERY
│ ├── DeliveryPerformanceScore (lateness)
│ └── DeliveryFulfillmentScore (shortfall)
├── INVENTORY
│ ├── InventoryAdherenceScore (vs targets)
│ ├── SupplyHoldingScore (finished goods)
│ └── InprocessHoldingScore (WIP)
├── CAPACITY
│ ├── ResourceUtilizationScore
│ ├── ResourceCapacityOverload
│ ├── CampaignCapacityOverload
│ ├── MissingCapacityConsumption
│ └── SpreadCapacityConsumption (leveling)
├── SUPPLY CHAIN
│ ├── SupplyPreferenceScore
│ ├── SupplyQualityScore
│ ├── MissingUpstreamSupply
│ ├── InvalidUpstreamSupply
│ └── LateUpstreamSupply
├── FEASIBILITY
│ ├── OverAssignedDemand
│ ├── OverUtilizedSupply
│ ├── PartialAssignedPiece
│ └── StorageCapacityExcess
└── SUSTAINABILITY
└── CO2Emissions
```
---
## 1. Inputs, Outputs, and Abstractions (CS View)
### 1.1 Input Contracts
#### 1.1.1 Core Input Types (F# Signatures)
```fsharp
/// Time bucket index (0 = start of horizon)
type Bucket = int
/// Unique identifiers
type ProductId = string
type StockingPointId = string
type DemandId = string
type RoutingId = string
type StepIndex = int
type ResourceGroupId = string
type TransportLegId = string
type SupplierId = string
type FamilyId = string
/// Product-in-Stocking-Point tuple
type PISP = ProductId * StockingPointId
/// Demand line input
type DemandInput = {
Id: DemandId
Product: ProductId
StockingPoint: StockingPointId
Quantity: decimal
DueBucket: Bucket
Priority: decimal // Higher = more important
FullOrder: bool // Must fulfill entirely or not at all
FullDelivery: bool // All lines of order together
Expedite: bool
Characteristics: Map<string, string> // For CBP matching
}
/// Inventory position input
type InventoryInput = {
Product: ProductId
StockingPoint: StockingPointId
OnHand: decimal
FirmInbound: (Bucket * decimal) list // (arrival bucket, quantity)
Reservations: (Bucket * decimal) list
SafetyStock: decimal
MinTarget: decimal
MaxTarget: decimal
TargetLevel: decimal // For adherence scoring
}
/// Routing step input
type RoutingStepInput = {
RoutingId: RoutingId
StepIndex: StepIndex
ResourceGroup: ResourceGroupId
LeadTimeBuckets: int
ProcessingTimePerUnit: decimal // Or piecewise function
Yield: decimal // Output/Input ratio
InputComponents: (ProductId * decimal) list // (component, qty per unit)
OutputProduct: ProductId
OutputStockingPoint: StockingPointId
}
/// Routing input
type RoutingInput = {
Id: RoutingId
Product: ProductId
StockingPoint: StockingPointId
Steps: RoutingStepInput list
ValidityMask: Set<Bucket> // Buckets where routing is valid
ProductionCost: decimal
PreferenceScore: decimal // Lower = preferred
QualityScore: decimal
ProducibleCharacteristics: Map<string, string> // For CBP
}
/// Capacity bucket input
type CapacityInput = {
ResourceGroup: ResourceGroupId
Bucket: Bucket
AvailableCapacity: decimal
FirmAllocations: decimal // Already committed
Reservations: decimal
Buffer: decimal // Safety withheld
}
/// Transport leg input
type TransportLegInput = {
Id: TransportLegId
Origin: StockingPointId
Destination: StockingPointId
LeadTimeBuckets: int
CapacityByBucket: Map<Bucket, decimal>
CutoffMask: Set<Bucket> // Buckets with departures
CostPerUnit: decimal
Reliability: decimal // 0-1
CO2PerUnit: decimal
PreferenceScore: decimal
}
/// Supplier offer input
type SupplierOfferInput = {
Supplier: SupplierId
Product: ProductId
StockingPoint: StockingPointId
MOQ: decimal
LotSize: decimal
LeadTimeBuckets: int
CapacityByBucket: Map<Bucket, decimal>
CostPerUnit: decimal
Reliability: decimal
PreferenceScore: decimal
}
/// Campaign parameters input
type CampaignInput = {
ProductionLine: ResourceGroupId
SetupMatrix: Map<ProductId * ProductId, decimal> // (from, to) -> setup time
SetupCostMatrix: Map<ProductId * ProductId, decimal>
MinRunByProduct: Map<ProductId, decimal>
MinCampaignLength: Map<ProductId, decimal>
MaxCampaignLength: Map<ProductId, decimal>
FamilyMapping: Map<ProductId, FamilyId>
CleaningRequired: Set<FamilyId * FamilyId> // Family pairs requiring cleaning
CleaningWindows: Set<Bucket> // Buckets unavailable (CIP)
}
/// Shelf-life input (for perishables)
type ShelfLifeInput = {
Product: ProductId
ShelfLifeBuckets: int
MinRemainingLifeAtShip: int
InitialInventoryByAge: Map<int, decimal> // age -> quantity
}
/// Policy/weight parameters
type PolicyInput = {
// Objective weights
WeightLateness: decimal
WeightEarliness: decimal
WeightFulfillment: decimal
WeightCost: decimal
WeightHolding: decimal
WeightOverload: decimal
WeightCO2: decimal
WeightChurn: decimal
WeightPreference: decimal
WeightQuality: decimal
WeightSpread: decimal
// Hard caps
CostCap: decimal option
CO2Cap: decimal option
// Mode toggles
FiniteCapacityMode: bool
IncludeCampaigns: bool
IncludeShelfLife: bool
IncludeSuppliers: bool
}
/// Complete optimizer input
type OptimizerInput = {
Horizon: int // Number of time buckets
Demands: DemandInput list
Inventory: InventoryInput list
Routings: RoutingInput list
Capacity: CapacityInput list
TransportLegs: TransportLegInput list
Suppliers: SupplierOfferInput list
Campaigns: CampaignInput list
ShelfLife: ShelfLifeInput list
Storage: Map<StockingPointId * Bucket, decimal> // Storage capacity
Policy: PolicyInput
Baseline: BaselinePlan option // Previous plan for churn calculation
}
```
### 1.2 Output Contracts
```fsharp
/// Fulfillment decision for a demand
type FulfillmentOutput = {
DemandId: DemandId
FulfilledByBucket: Map<Bucket, decimal> // bucket -> quantity
TotalFulfilled: decimal
Shortfall: decimal
Lateness: decimal // Bucket-weighted lateness
AssignedSources: AssignmentOutput list
}
/// Assignment of demand to supply source
type AssignmentOutput = {
DemandId: DemandId
SourceType: SupplySourceType // INV, FIRM, PROD, TRANS, SUP
SourceId: string // Routing, Leg, or Supplier ID
Bucket: Bucket
Quantity: decimal
}
type SupplySourceType = INV | FIRM | PROD | TRANS | SUP
/// Production schedule output
type ProductionOutput = {
RoutingId: RoutingId
StepIndex: StepIndex
Bucket: Bucket
Quantity: decimal
ResourceGroup: ResourceGroupId
}
/// Transport schedule output
type TransportOutput = {
LegId: TransportLegId
Product: ProductId
DepartBucket: Bucket
ArrivalBucket: Bucket
Quantity: decimal
}
/// Purchase order output
type PurchaseOutput = {
Supplier: SupplierId
Product: ProductId
StockingPoint: StockingPointId
OrderBucket: Bucket
ArrivalBucket: Bucket
Quantity: decimal
NumLots: int
}
/// Inventory trajectory output
type InventoryTrajectoryOutput = {
Product: ProductId
StockingPoint: StockingPointId
EndOfBucketInventory: Map<Bucket, decimal>
SafetyViolations: Map<Bucket, decimal>
TargetDeviations: Map<Bucket, decimal>
}
/// KPI scorecard output
type KPIScorecard = {
DeliveryPerformance: decimal // Total lateness
DeliveryFulfillment: decimal // Total shortfall
InventoryAdherence: decimal // Target deviation
HoldingCost: decimal
ProductionCost: decimal
TransportCost: decimal
PurchaseCost: decimal
CapacityOverload: decimal
CO2Emissions: decimal
PlanChurn: decimal
ObjectiveValue: decimal // Weighted sum
}
/// Limiter explanation for a demand
type LimiterOutput = {
DemandId: DemandId
Domain: LimiterDomain
Code: string
Message: string
AffectedBuckets: Bucket list
Suggestions: string list
}
type LimiterDomain = Material | Capacity | Transport | Supplier | Storage | Quality | System
/// Complete optimizer output
type OptimizerOutput = {
Status: SolverStatus
ObjectiveValue: decimal
OptimalityGap: decimal
SolveTimeSeconds: decimal
Fulfillments: FulfillmentOutput list
Production: ProductionOutput list
Transport: TransportOutput list
Purchases: PurchaseOutput list
InventoryTrajectory: InventoryTrajectoryOutput list
KPIs: KPIScorecard
Limiters: LimiterOutput list
PeggingLinks: AssignmentOutput list
}
type SolverStatus = Optimal | Feasible | Infeasible | Timeout | Error of string
```
### 1.3 Solver Abstraction Layer
```fsharp
/// Abstract interface for solver backends
type ISolver =
abstract member AddVariable: name:string * lb:decimal * ub:decimal * varType:VarType -> VarRef
abstract member AddConstraint: name:string * expr:LinearExpr * sense:Sense * rhs:decimal -> unit
abstract member SetObjective: expr:LinearExpr * minimize:bool -> unit
abstract member Solve: config:SolverConfig -> SolveResult
type VarType = Continuous | Integer | Binary
type Sense = LessOrEqual | Equal | GreaterOrEqual
type VarRef = int // Internal variable reference
type LinearExpr =
| Const of decimal
| Var of VarRef
| Add of LinearExpr * LinearExpr
| Mul of decimal * LinearExpr
type SolverConfig = {
TimeLimit: TimeSpan
GapTolerance: float
Threads: int
Seed: int // For determinism
Presolve: bool
MIPStart: Map<VarRef, decimal> option
}
type SolveResult = {
Status: SolverStatus
ObjectiveValue: decimal option
Gap: float option
Solution: Map<VarRef, decimal>
DualValues: Map<string, decimal> option // For shadow prices
}
/// Solver factory
type SolverBackend = CPLEX | ORTools | Gurobi
let createSolver (backend: SolverBackend) : ISolver =
match backend with
| CPLEX -> CplexSolver() :> ISolver
| ORTools -> ORToolsSolver() :> ISolver
| Gurobi -> GurobiSolver() :> ISolver
```
### 1.4 Data Validation & Normalization
```fsharp
/// Validation errors
type ValidationError =
| NegativeQuantity of entity:string * id:string * field:string * value:decimal
| MissingReference of entity:string * id:string * refType:string * refId:string
| CyclicBOM of products:string list
| InvalidDateRange of entity:string * id:string * start:Bucket * end_:Bucket
| DuplicateId of entity:string * id:string
/// Validate inputs before optimization
let validateInput (input: OptimizerInput) : Result<unit, ValidationError list> =
let errors = ResizeArray<ValidationError>()
// Check non-negative quantities
for d in input.Demands do
if d.Quantity < 0m then
errors.Add(NegativeQuantity("Demand", d.Id, "Quantity", d.Quantity))
// Check references exist
let productIds = input.Routings |> List.map (_.Product) |> Set.ofList
for d in input.Demands do
if not (productIds.Contains d.Product) then
errors.Add(MissingReference("Demand", d.Id, "Product", d.Product))
// Check for cyclic BOMs
let bomGraph = buildBOMGraph input.Routings
match detectCycles bomGraph with
| Some cycle -> errors.Add(CyclicBOM(cycle))
| None -> ()
if errors.Count = 0 then Ok ()
else Error (errors |> Seq.toList)
/// Normalize all timestamps to bucket indices
let normalizeToBuckets (startDate: DateTimeOffset) (bucketSize: TimeSpan) (timestamp: DateTimeOffset) : Bucket =
int ((timestamp - startDate) / bucketSize)
```
---
## 2. Modeling Scope & Time Representation
### 2.1 Planning Horizon and Granularity
#### Time Bucket Definition
The planning horizon is divided into discrete **time buckets** \( t \in T = \{0, 1, 2, \ldots, |T|-1\} \).
| Horizon Segment | Bucket Granularity | Typical Length |
| ------------------ | ------------------ | -------------- |
| Near-term (frozen) | Hours | 1-2 days |
| Short-term | Days | 2-4 weeks |
| Medium-term | Days or Weeks | 1-3 months |
| Long-term | Weeks | 3-6 months |
**Bucket Mapping:**
```
Real Time → Bucket Index
─────────────────────────
2026-01-05 00:00 → t = 0
2026-01-06 00:00 → t = 1
2026-01-07 00:00 → t = 2
...
```
#### Lead Time Representation
All lead times are expressed as **bucket offsets** (integers):
- Production lead time: \( LT^{prod}_{r,k} \in \mathbb{Z}^+ \) buckets
- Transport lead time: \( LT^{trans}_m \in \mathbb{Z}^+ \) buckets
- Supplier lead time: \( LT^{sup}_{v,p,s} \in \mathbb{Z}^+ \) buckets
**Example:** If a transport leg has \( LT^{trans}_m = 2 \) and departs in bucket \( t = 5 \), it arrives in bucket \( t = 7 \).
### 2.2 Finite vs Infinite Capacity Mode
The optimizer supports two capacity modes controlled by parameter \( \text{FiniteCapMode} \in \{0, 1\} \):
| Mode | Behavior | Use Case |
| --------------------- | ------------------------------------------------------- | --------------------------- |
| **Finite** (\(=1\)) | Capacity constraints are hard; slack variable penalized | Normal planning |
| **Infinite** (\(=0\)) | Capacity constraints relaxed; overload just tracked | What-if, rough-cut planning |
**Mathematical Formulation:**
- Finite mode: Overload slack \( OverUtil_{g,t} \) is penalized heavily in objective
- Infinite mode: \( OverUtil_{g,t} \) has zero or minimal penalty
### 2.3 Firm vs Planned Artifacts
| Artifact Type | Treatment | Variables |
| ---------------------- | ------------------------ | ----------------------------------------- |
| **Firm supply** | Fixed input (parameter) | Subtracted in inventory balance |
| **Firm allocations** | Fixed input | Subtracted from capacity |
| **Locked operations** | Hard equality constraint | \( x^{prod}_{r,k,t} = \text{LockedQty} \) |
| **Planned production** | Optimizer decides | \( x^{prod}_{r,k,t} \) variable |
| **Planned transport** | Optimizer decides | \( x^{trans}_{m,p,t} \) variable |
| **Planned purchases** | Optimizer decides | \( x^{sup}_{v,p,s,t} \) variable |
### 2.4 Multi-Echelon Network Structure
The supply chain is modeled as a **directed graph**:
```
NODES = Stocking Points (S)
EDGES = Transport Legs (M)
Example Network:
┌─────────────┐
┌──────────▶│ DC-Central │──────────┐
│ └─────────────┘ │
│ ▼
┌───────┴───────┐ ┌──────────────┐
│ Plant-North │ │ Customer-East│
└───────────────┘ └──────────────┘
│ ▲
│ ┌─────────────┐ │
└──────────▶│ DC-South │──────────┘
└─────────────┘
```
**Network Properties:**
- Each stocking point \( s \in S \) has inventory variables \( Inv_{p,s,t} \)
- Each transport leg \( m \in M \) connects \( Origin_m \) to \( Dest_m \)
- Production happens at specific stocking points (plants)
- Demand delivery points are stocking points
---
## 3. Sets, Indices, and Mappings
> This section defines **all indices and sets** so the optimizer can be logic-light.
> Well-designed sets = less business logic in constraints.
**Notation Convention:**
- Sets are denoted by uppercase letters: \( P, S, T, \ldots \)
- Indices (elements of sets) are denoted by lowercase letters: \( p \in P, s \in S, t \in T, \ldots \)
- Subscripted sets denote subsets conditioned on other indices: \( R_{p,s} \) = routings for product \( p \) at stocking point \( s \)
- Binary masks use \( \{0,1\} \) values precomputed from business rules
### 3.1 Core Domain Indices
| Index | Set | Cardinality | Description |
| ----- | ----------------------------- | ---------------------------------------- | ------------------------------------------------- |
| \(p\) | \(P\) | \(\lvert P \rvert \leq 10{,}000\) | Products / SKUs |
| \(c\) | \(C \subseteq P\) | \(\lvert C \rvert \leq \lvert P \rvert\) | Components (products used in BOMs) |
| \(s\) | \(S\) | \(\lvert S \rvert \leq 100\) | Stocking points (locations / warehouses / plants) |
| \(d\) | \(D\) | \(\lvert D \rvert \leq 50{,}000\) | Demand lines (customer orders + forecasts) |
| \(t\) | \(T = \{0, 1, \ldots, H-1\}\) | \(\lvert T \rvert \leq 180\) | Time buckets (horizon \(H\) buckets) |
**Mathematical Definitions:**
$$P = \{p_1, p_2, \ldots, p_{|P|}\} \quad \text{(all products)}$$
$$T = \{0, 1, 2, \ldots, H-1\} \quad \text{(ordered time buckets, } H = \text{horizon length)}$$
**F# Representation:**
```fsharp
type Sets = {
Products: Set<ProductId> // P
StockingPoints: Set<StockingPointId> // S
Demands: Set<DemandId> // D
Horizon: int // |T| = H
}
```
### 3.2 Product-in-Stocking-Point (PISP)
| Index | Set | Description |
| --------- | ----------------------------- | ---------------------------- |
| \((p,s)\) | \(PISP \subseteq P \times S\) | Valid product-location pairs |
**Definition:**
$$PISP = \{(p, s) \in P \times S : \text{product } p \text{ can exist at location } s\}$$
This set is **sparse** — not all products exist at all locations. Typical density: 5-20%.
**Derived Functions:**
$$\text{Products}_s = \{p : (p,s) \in PISP\} \quad \text{(products at stocking point } s \text{)}$$
$$\text{Locations}_p = \{s : (p,s) \in PISP\} \quad \text{(stocking points for product } p \text{)}$$
**F# Representation:**
```fsharp
type PISP = ProductId * StockingPointId
let validPISPs: Set<PISP> = // precomputed from master data
let productsAt s = validPISPs |> Set.filter (fun (_, s') -> s' = s) |> Set.map fst
let locationsFor p = validPISPs |> Set.filter (fun (p', _) -> p' = p) |> Set.map snd
```
### 3.3 Production / Routing Indices
| Index | Set | Description |
| ----- | --------------------------------------------- | ---------------------------------------------------------------------- |
| \(r\) | \(R_{p,s}\) | Routing alternates for producing product \(p\) at stocking point \(s\) |
| \(k\) | \(K_r = \{1, 2, \ldots, \lvert K_r \rvert\}\) | Steps within routing \(r\) (ordered by precedence) |
| \(g\) | \(G\) | Resource groups (e.g., "Casting", "Rolling", "Finishing") |
| \(u\) | \(U_g\) | Individual resources within group \(g\) |
**Definitions:**
$$R = \bigcup_{(p,s) \in PISP} R_{p,s} \quad \text{(all routings)}$$
$$K_r = \{1, 2, \ldots, n_r\} \quad \text{where } n_r = \text{number of steps in routing } r$$
**Routing Structure:**
```
Routing r for Product p at Stocking Point s:
Step 1 ──▶ Step 2 ──▶ ... ──▶ Step n_r
│ │ │
▼ ▼ ▼
Group g₁ Group g₂ Group g_n
```
**Derived Sets and Mappings:**
| Notation | Type | Description |
| ------------------------------------------------ | -------- | ---------------------------------------------------------------- |
| \(ResGroup_{r,k} \in G\) | Function | Primary resource group for step \(k\) of routing \(r\) |
| \(AltRes_{r,k} \subseteq G\) | Set | Alternative resource groups for step \(k\) |
| \(Inputs_{r,k} \subseteq C \times \mathbb{R}^+\) | Set | \(\{(c, q_{c,r,k})\}\) components consumed with quantities |
| \(Output_r \in P\) | Function | Product output by routing \(r\) |
| \(OutputSP_r \in S\) | Function | Stocking point where output is placed |
| \(FinalStep_r = \max K_r\) | Function | Last step of routing |
| \(Precedence_r \subseteq K_r \times K_r\) | Relation | \((k_1, k_2)\) means \(k_1\) must complete before \(k_2\) starts |
**F# Representation:**
```fsharp
type RoutingStep = {
Routing: RoutingId
Step: StepIndex
ResourceGroup: ResourceGroupId
AlternateGroups: Set<ResourceGroupId>
Inputs: Map<ProductId, decimal> // component -> quantity per unit
LeadTime: int // buckets
ProcessingRate: decimal // time per unit
Yield: decimal // output/input ratio
}
type Routing = {
Id: RoutingId
Product: ProductId
StockingPoint: StockingPointId
Steps: RoutingStep list // ordered by precedence
}
let routingsFor (p, s) : Set<RoutingId> = // lookup from R_{p,s}
let stepsOf r : StepIndex list = [1 .. numSteps r]
let finalStep r = numSteps r
```
### 3.4 Campaign / Family Indices
| Index | Set | Description |
| ----- | ----------------- | -------------------------------------------------------------- |
| \(f\) | \(F\) | Product families (grouping for campaign/setup optimization) |
| \(l\) | \(L \subseteq G\) | Production lines (resource groups with sequencing constraints) |
**Definitions:**
$$F = \{f_1, f_2, \ldots, f_{|F|}\} \quad \text{(product families)}$$
$$Family: P \to F \quad \text{(mapping products to families)}$$
$$FamilyProducts_f = \{p \in P : Family(p) = f\} \quad \text{(products in family } f \text{)}$$
**Campaign Constraints Apply To:**
- **Lines** \(L\): Subset of resource groups where changeover matters
- **Families** \(F\): Products grouped by similar setup requirements
**F# Representation:**
```fsharp
type CampaignSets = {
Families: Set<FamilyId>
ProductToFamily: Map<ProductId, FamilyId>
ProductionLines: Set<ResourceGroupId> // L ⊆ G
FamilyProducts: Map<FamilyId, Set<ProductId>>
}
```
### 3.5 Transport Indices
| Index | Set | Description |
| ----- | ------------ | ------------------------------------------------------------ |
| \(m\) | \(M\) | Transport legs (directed edges in the network) |
| \(h\) | \(H_{s,s'}\) | Candidate itineraries from \(s\) to \(s'\) (multi-hop paths) |
**Definitions:**
$$M = \{m_1, m_2, \ldots, m_{|M|}\} \quad \text{(all transport legs)}$$
Each leg \(m\) is a directed edge:
$$m: Origin_m \to Dest_m \quad \text{where } Origin_m, Dest_m \in S$$
**Derived Sets:**
$$LegsFrom_s = \{m \in M : Origin_m = s\} \quad \text{(outbound legs from } s \text{)}$$
$$LegsTo_s = \{m \in M : Dest_m = s\} \quad \text{(inbound legs to } s \text{)}$$
$$Legs_{s \to s'} = \{m \in M : Origin_m = s \land Dest_m = s'\} \quad \text{(direct legs)}$$
**Network Graph Representation:**
```
G = (S, M) where:
- Nodes: S (stocking points)
- Edges: M (transport legs, directed)
- Edge weight: LeadTime^{trans}_m, Cost^{trans}_m
```
**F# Representation:**
```fsharp
type TransportSets = {
Legs: Set<TransportLegId>
Origin: Map<TransportLegId, StockingPointId>
Destination: Map<TransportLegId, StockingPointId>
LegsFrom: Map<StockingPointId, Set<TransportLegId>>
LegsTo: Map<StockingPointId, Set<TransportLegId>>
}
```
### 3.6 Supplier / Procurement Indices
| Index | Set | Description |
| ----------- | ---------------------------------------- | -------------------------------------------- |
| \(v\) | \(V\) | Suppliers |
| \((v,p,s)\) | \(Offers \subseteq V \times P \times S\) | Valid supplier-product-location combinations |
**Definitions:**
$$V = \{v_1, v_2, \ldots, v_{|V|}\} \quad \text{(all suppliers)}$$
$$Offers = \{(v, p, s) : \text{supplier } v \text{ can deliver product } p \text{ to stocking point } s\}$$
**Derived Sets:**
$$SuppliersFor_{p,s} = \{v : (v, p, s) \in Offers\} \quad \text{(suppliers for product-location)}$$
**F# Representation:**
```fsharp
type SupplierSets = {
Suppliers: Set<SupplierId>
Offers: Set<SupplierId * ProductId * StockingPointId>
SuppliersFor: Map<PISP, Set<SupplierId>>
}
```
### 3.7 Supply Sources (for Pegging / Assignment)
| Index | Set | Description |
| ------- | --------------- | ------------------------------------------------------------------- |
| \(src\) | \(SRC_{p,s,t}\) | Supply sources available for product \(p\) at \(s\) in bucket \(t\) |
**Supply Source Types:**
$$SourceType = \{INV, FIRM, PROD, TRANS, SUP\}$$
| Type | Description | Quantity Source |
| --------- | ------------------ | --------------------------------------------------- |
| \(INV\) | Existing inventory | \(Inv^{init}_{p,s}\) (parameter) |
| \(FIRM\) | Firm inbound | \(Inb^{firm}_{p,s,t}\) (parameter) |
| \(PROD\) | Planned production | \(x^{prod}_{r,k,t}\) (variable) |
| \(TRANS\) | Transport arrival | \(x^{trans}_{m,p,t'}\) arriving at \(t\) (variable) |
| \(SUP\) | Supplier receipt | \(x^{sup}_{v,p,s,t}\) (variable) |
**Source Identification:**
Each source has a unique identifier:
$$src = (type, id, bucket) \quad \text{e.g., } (PROD, r_5, 10) = \text{production from routing } r_5 \text{ completing in bucket } 10$$
> **Abstraction benefit**: Pegging constraints become source-agnostic; optimizer assigns demand to sources.
**F# Representation:**
```fsharp
type SupplySource =
| Inventory of PISP
| FirmInbound of PISP * Bucket
| Production of RoutingId * Bucket
| TransportArrival of TransportLegId * ProductId * Bucket
| SupplierReceipt of SupplierId * ProductId * StockingPointId * Bucket
let sourceId (src: SupplySource) : string = // deterministic ID generation
```
### 3.8 Shelf-Life / Age Indices (CPG / Perishables)
| Index | Set | Description |
| ----- | --------------------------------------- | ----------------------------- |
| \(a\) | \(A_p = \{0, 1, \ldots, ShelfLife_p\}\) | Age buckets for product \(p\) |
**Definitions:**
$$A_p = \{0, 1, 2, \ldots, ShelfLife_p\} \quad \text{(valid ages for product } p \text{)}$$
where:
- \(a = 0\): Freshly produced (age 0)
- \(a = ShelfLife_p\): Maximum allowable age (expires next bucket)
- \(a > ShelfLife_p\): Expired (must be discarded)
**Age Evolution:**
Inventory produced in bucket \(t_0\) has age \(a = t - t_0\) in bucket \(t\).
$$Age(produced\_at\_t_0, current\_bucket\_t) = t - t_0$$
**Shipping Constraint:**
$$MinRemainingLife_p = \text{minimum remaining life at shipment}$$
$$\text{Can ship if: } ShelfLife_p - a - TransitTime \geq MinRemainingLife_p$$
**F# Representation:**
```fsharp
type ShelfLifeSets = {
ShelfLife: Map<ProductId, int> // p -> max age
MinRemainingLife: Map<ProductId, int> // p -> min life at ship
AgeRange: ProductId -> int list // p -> [0..ShelfLife_p]
}
```
### 3.9 Validity / Feasibility Masks (Precomputed)
These binary masks encode business rules as **parameters**, keeping constraints logic-free.
| Mask | Domain | Definition |
| -------------------------- | ----------- | ----------------------------------------------------------------- |
| \(\delta^{routing}_{r,t}\) | \(\{0,1\}\) | \(= 1\) if routing \(r\) is effective in bucket \(t\) |
| \(\delta^{cutoff}_{m,t}\) | \(\{0,1\}\) | \(= 1\) if transport leg \(m\) has departure in bucket \(t\) |
| \(\delta^{cap}_{g,t}\) | \(\{0,1\}\) | \(= 1\) if resource group \(g\) is available in bucket \(t\) |
| \(\delta^{CIP}_{l,t}\) | \(\{0,1\}\) | \(= 1\) if line \(l\) is not in cleaning window in bucket \(t\) |
| \(\delta^{sup}_{v,p,s,t}\) | \(\{0,1\}\) | \(= 1\) if supplier \(v\) can deliver to \(s\) in bucket \(t\) |
| \(\delta^{compat}_{d,r}\) | \(\{0,1\}\) | \(= 1\) if routing \(r\) can satisfy demand \(d\) characteristics |
**Usage in Constraints:**
These masks multiply decision variables to enforce feasibility:
$$x^{prod}_{r,k,t} \leq M \cdot \delta^{routing}_{r,t}$$
where \(M\) is a tight upper bound. If \(\delta = 0\), production is forced to zero.
**F# Representation:**
```fsharp
type ValidityMasks = {
RoutingValid: Map<RoutingId * Bucket, bool>
TransportCutoff: Map<TransportLegId * Bucket, bool>
CapacityAvailable: Map<ResourceGroupId * Bucket, bool>
CleaningWindow: Map<ResourceGroupId * Bucket, bool> // true = available
SupplierAvailable: Map<SupplierId * ProductId * StockingPointId * Bucket, bool>
RoutingCompatible: Map<DemandId * RoutingId, bool>
}
```
### 3.10 Demand Characteristics (Characteristic-Based Planning)
| Index | Set | Description |
| ------- | ------------ | ------------------------------------------------------------- |
| \(ch\) | \(CH\) | Characteristic types (e.g., "Grade", "Width", "Thickness") |
| \(val\) | \(VAL_{ch}\) | Values for characteristic \(ch\) (e.g., Grade: "A", "B", "C") |
**Definitions:**
$$CharSpec = \{(ch, val) : ch \in CH, val \in VAL_{ch}\}$$
$$DemandChars_d \subseteq CharSpec \quad \text{(required characteristics for demand } d \text{)}$$
$$RoutingChars_r \subseteq CharSpec \quad \text{(characteristics producible by routing } r \text{)}$$
**Compatibility:**
$$\delta^{compat}_{d,r} = \begin{cases}
1 & \text{if } DemandChars_d \subseteq RoutingChars_r \\
0 & \text{otherwise}
\end{cases}$$
**Example:**
```
Demand d₁ requires: {(Grade, "A"), (Width, 1000mm)}
Routing r₁ produces: {(Grade, "A"), (Grade, "B"), (Width, 800mm), (Width, 1000mm)}
Compatible? Check: {(Grade, "A"), (Width, 1000mm)} ⊆ r₁'s chars
- (Grade, "A") ✓
- (Width, 1000mm) ✓
Result: δ^{compat}_{d₁,r₁} = 1
```
**F# Representation:**
```fsharp
type CharacteristicSpec = Map<string, string> // characteristic -> value
type CBPSets = {
Characteristics: Set<string>
DemandCharacteristics: Map<DemandId, CharacteristicSpec>
RoutingCharacteristics: Map<RoutingId, CharacteristicSpec>
Compatibility: Map<DemandId * RoutingId, bool>
}
let isCompatible d r =
let demandChars = cbpSets.DemandCharacteristics.[d]
let routingChars = cbpSets.RoutingCharacteristics.[r]
demandChars |> Map.forall (fun k v ->
routingChars |> Map.tryFind k |> Option.map ((=) v) |> Option.defaultValue false)
```
---
## 4. Parameters (Inputs to the Math Model)
> All parameters are **inputs** provided by upstream services. The optimizer does not compute business logic — it consumes normalized data.
**Parameter Naming Convention:**
- Greek letters for time-related parameters: \(\tau\) (tau) for deadlines
- Superscripts for type qualifiers: \(^{init}, ^{firm}, ^{trans}, ^{sup}\)
- Subscripts for indices: \(_{p,s,t}\) for product-stocking point-time
### 4.1 Demand Parameters
| Parameter | Domain | Description |
| ----------------------- | ----------------- | --------------------------------------------------------- |
| \(Qty_d\) | \(\mathbb{R}^+ \) | Quantity demanded |
| \(\tau^{due}_d\) | \(T\) | Due bucket (deadline) |
| \(w^{prio}_d\) | \(\mathbb{R}^+\) | Priority weight (higher = more important) |
| \(\delta^{full}_d\) | \(\{0,1\}\) | \(=1\) if demand must be fulfilled entirely or not at all |
| \(\delta^{order}_d\) | \(Order\) | Order ID (for full-delivery grouping) |
| \(\delta^{expedite}_d\) | \(\{0,1\}\) | Expedite flag |
| \(p_d\) | \(P\) | Product for demand \(d\) |
| \(s_d\) | \(S\) | Delivery stocking point for demand \(d\) |
**Computed Bounds:**
$$UB^{fulfill}_d = Qty_d \quad \text{(maximum quantity that can be fulfilled)}$$
$$M^{demand}_d = Qty_d \quad \text{(Big-M for demand-related constraints)}$$
**F# Representation:**
```fsharp
type DemandParams = {
Quantity: Map<DemandId, decimal> // Qty_d
DueBucket: Map<DemandId, Bucket> // τ^due_d
Priority: Map<DemandId, decimal> // w^prio_d
FullOrderFlag: Map<DemandId, bool> // δ^full_d
OrderId: Map<DemandId, OrderId> // δ^order_d
ExpediteFlag: Map<DemandId, bool> // δ^expedite_d
Product: Map<DemandId, ProductId> // p_d
StockingPoint: Map<DemandId, StockingPointId> // s_d
}
```
### 4.2 Inventory and Supply Parameters
| Parameter | Domain | Description |
| ---------------------- | ----------------------- | ---------------------------------------------- |
| \(Inv^{init}_{p,s}\) | \(\mathbb{R}^{\geq 0}\) | On-hand inventory at \(t=0\) |
| \(Inb^{firm}_{p,s,t}\) | \(\mathbb{R}^{\geq 0}\) | Firm inbound supply arriving in bucket \(t\) |
| \(Resv^{mat}_{p,s,t}\) | \(\mathbb{R}^{\geq 0}\) | Reserved material (subtract from available) |
| \(Safety_{p,s}\) | \(\mathbb{R}^{\geq 0}\) | Safety stock floor |
| \(InvMin_{p,s}\) | \(\mathbb{R}^{\geq 0}\) | Minimum inventory target |
| \(InvMax_{p,s}\) | \(\mathbb{R}^{\geq 0}\) | Maximum inventory target |
| \(InvTarget_{p,s}\) | \(\mathbb{R}^{\geq 0}\) | Target inventory level (for adherence scoring) |
**Inventory Relationships:**
$$Safety_{p,s} \leq InvMin_{p,s} \leq InvTarget_{p,s} \leq InvMax_{p,s}$$
**Available Inventory Computation:**
$$Avail^{init}_{p,s} = Inv^{init}_{p,s} - Resv^{mat}_{p,s,0} \quad \text{(initial available)}$$
**F# Representation:**
```fsharp
type InventoryParams = {
Initial: Map<PISP, decimal> // Inv^init_{p,s}
FirmInbound: Map<PISP * Bucket, decimal> // Inb^firm_{p,s,t}
Reservations: Map<PISP * Bucket, decimal> // Resv^mat_{p,s,t}
SafetyStock: Map<PISP, decimal> // Safety_{p,s}
MinTarget: Map<PISP, decimal> // InvMin_{p,s}
MaxTarget: Map<PISP, decimal> // InvMax_{p,s}
Target: Map<PISP, decimal> // InvTarget_{p,s}
}
```
### 4.3 Storage / Warehouse Capacity Parameters
| Parameter | Domain | Description |
| -------------------------- | ---------------- | -------------------------------------------------------------- |
| \(StorageCap_{s,t}\) | \(\mathbb{R}^+\) | Total storage capacity at stocking point \(s\) in bucket \(t\) |
| \(StorageCapProd_{p,s,t}\) | \(\mathbb{R}^+\) | Product-specific storage limit (optional) |
| \(\nu_{p}\) | \(\mathbb{R}^+\) | Storage space per unit of product \(p\) (volume/weight) |
**Storage Space Consumption:**
$$\text{Total space at } (s,t) = \sum_{p \in Products_s} \nu_p \cdot Inv_{p,s,t}$$
**Constraint Form:**
$$\sum_{p \in Products_s} \nu_p \cdot Inv_{p,s,t} \leq StorageCap_{s,t} + StorageOver_{s,t} \quad \forall s \in S, t \in T$$
where \(StorageOver_{s,t}\) is a penalty slack variable.
### 4.4 BOM Parameters
| Parameter | Domain | Description |
| ----------------- | ---------------- | -------------------------------------------------------------------------------- |
| \(\beta_{c,r,k}\) | \(\mathbb{R}^+\) | Units of component \(c\) consumed per unit output at step \(k\) of routing \(r\) |
| \(\gamma_{r,k}\) | \((0, 1]\) | Yield factor for step \(k\) (output/input ratio) |
**BOM Explosion Equation:**
For step \(k\) of routing \(r\) producing quantity \(x\):
$$\text{Component consumption: } \sum_{c \in Inputs_{r,k}} \beta_{c,r,k} \cdot \frac{x}{\gamma_{r,k}}$$
**Multi-Level BOM:**
$$BOM^{eff}_{c,p} = \sum_{r \in R_{p,s}} \sum_{k \in K_r} \beta_{c,r,k} \cdot \prod_{j < k} \frac{1}{\gamma_{r,j}} \quad \text{(effective component per finished good)}$$
**F# Representation:**
```fsharp
type BOMParams = {
ComponentUsage: Map<ProductId * RoutingId * StepIndex, decimal> // β_{c,r,k}
Yield: Map<RoutingId * StepIndex, decimal> // γ_{r,k}
}
let bomExplosion routing step outputQty =
let yield_ = bomParams.Yield.[(routing, step)]
let inputQty = outputQty / yield_
bomParams.ComponentUsage
|> Map.filter (fun (_, r, k) _ -> r = routing && k = step)
|> Map.map (fun _ rate -> rate * inputQty)
```
### 4.5 Production / Routing Parameters
| Parameter | Domain | Description |
| ------------------------- | ---------------- | ---------------------------------------------------- |
| \(LT_{r,k}\) | \(\mathbb{Z}^+\) | Lead time (buckets) for step \(k\) to complete |
| \(\rho_{r,k}\) | \(\mathbb{R}^+\) | Processing time per unit (capacity consumption rate) |
| \(\gamma_{r,k}\) | \((0, 1]\) | Yield factor (output/input) |
| \(Cost^{prod}_{r}\) | \(\mathbb{R}^+\) | Production cost per unit for routing \(r\) |
| \(Pref^{routing}_{r}\) | \(\mathbb{R}^+\) | Routing preference score (lower = better) |
| \(Quality^{routing}_{r}\) | \([0, 1]\) | Quality score for routing (higher = better) |
**Lead Time Relationship:**
If step \(k\) completes in bucket \(t\), then:
- Step \(k\) starts at bucket \(t - LT_{r,k}\)
- Previous step \(k-1\) must complete by bucket \(t - LT_{r,k}\)
**Capacity Consumption:**
$$CapUsage_{r,k,t} = \rho_{r,k} \cdot x^{prod}_{r,k,t} \quad \text{(capacity units consumed)}$$
**F# Representation:**
```fsharp
type ProductionParams = {
LeadTime: Map<RoutingId * StepIndex, int> // LT_{r,k}
ProcessingRate: Map<RoutingId * StepIndex, decimal> // ρ_{r,k}
Yield: Map<RoutingId * StepIndex, decimal> // γ_{r,k}
ProductionCost: Map<RoutingId, decimal> // Cost^prod_r
PreferenceScore: Map<RoutingId, decimal> // Pref^routing_r
QualityScore: Map<RoutingId, decimal> // Quality^routing_r
}
```
### 4.6 Capacity Parameters
| Parameter | Domain | Description |
| ---------------------- | ----------------------- | ----------------------------------------------------------- |
| \(Cap_{g,t}\) | \(\mathbb{R}^+\) | Available capacity for resource group \(g\) in bucket \(t\) |
| \(CapBuffer_{g,t}\) | \(\mathbb{R}^{\geq 0}\) | Safety buffer to withhold |
| \(Alloc^{firm}_{g,t}\) | \(\mathbb{R}^{\geq 0}\) | Already-allocated (locked) capacity |
| \(Resv^{cap}_{g,t}\) | \(\mathbb{R}^{\geq 0}\) | Reserved capacity |
| \(\delta^{finite}\) | \(\{0,1\}\) | Finite capacity mode toggle |
**Effective Capacity:**
$$Cap^{eff}_{g,t} = Cap_{g,t} - CapBuffer_{g,t} - Alloc^{firm}_{g,t} - Resv^{cap}_{g,t}$$
**Capacity Constraint (Finite Mode):**
$$\sum_{r,k : ResGroup_{r,k} = g} \rho_{r,k} \cdot x^{prod}_{r,k,t} \leq Cap^{eff}_{g,t} + OverUtil_{g,t} \quad \forall g \in G, t \in T$$
where \(OverUtil_{g,t} \geq 0\) is penalized in the objective.
**F# Representation:**
```fsharp
type CapacityParams = {
Available: Map<ResourceGroupId * Bucket, decimal> // Cap_{g,t}
Buffer: Map<ResourceGroupId * Bucket, decimal> // CapBuffer_{g,t}
FirmAllocations: Map<ResourceGroupId * Bucket, decimal> // Alloc^firm_{g,t}
Reservations: Map<ResourceGroupId * Bucket, decimal> // Resv^cap_{g,t}
FiniteMode: bool // δ^finite
}
let effectiveCapacity g t =
let cap = capacityParams.Available.[(g, t)]
let buffer = capacityParams.Buffer.TryFind (g, t) |> Option.defaultValue 0m
let firm = capacityParams.FirmAllocations.TryFind (g, t) |> Option.defaultValue 0m
let resv = capacityParams.Reservations.TryFind (g, t) |> Option.defaultValue 0m
cap - buffer - firm - resv
```
### 4.7 Campaign / Changeover Parameters
| Parameter | Domain | Description |
| ------------------------------ | ----------------------- | ------------------------------------------------ |
| \(\sigma_{i,j,l}\) | \(\mathbb{R}^{\geq 0}\) | Setup time: product \(i\) to \(j\) on line \(l\) |
| \(SetupCost_{i,j,l}\) | \(\mathbb{R}^{\geq 0}\) | Setup cost for changeover |
| \(\delta^{clean}_{f_1,f_2,l}\) | \(\{0,1\}\) | \(=1\) if cleaning required between families |
| \(MinRun_{p,l}\) | \(\mathbb{R}^+\) | Minimum run quantity |
| \(MinCamp_{p,l}\) | \(\mathbb{Z}^+\) | Minimum campaign length (buckets) |
| \(MaxCamp_{p,l}\) | \(\mathbb{Z}^+\) | Maximum campaign length (buckets) |
**Setup Matrix Properties:**
$$\sigma_{p,p,l} = 0 \quad \text{(no setup when continuing same product)}$$
$$\sigma_{i,j,l} \geq 0 \quad \text{(non-negative setup times)}$$
**Triangle Inequality (not always satisfied):**
$$\sigma_{i,k,l} \leq \sigma_{i,j,l} + \sigma_{j,k,l} \quad \text{(may or may not hold)}$$
**Family-Based Simplification:**
If setup depends only on families:
$$\sigma_{i,j,l} = \sigma^{fam}_{Family(i), Family(j), l}$$
**F# Representation:**
```fsharp
type CampaignParams = {
SetupTime: Map<ProductId * ProductId * ResourceGroupId, decimal> // σ_{i,j,l}
SetupCost: Map<ProductId * ProductId * ResourceGroupId, decimal>
CleaningRequired: Set<FamilyId * FamilyId * ResourceGroupId> // δ^clean
MinRun: Map<ProductId * ResourceGroupId, decimal>
MinCampaignLength: Map<ProductId * ResourceGroupId, int>
MaxCampaignLength: Map<ProductId * ResourceGroupId, int>
}
```
### 4.8 Transport Parameters
| Parameter | Domain | Description |
| --------------------- | ---------------- | ------------------------------------------------------ |
| \(LT^{trans}_m\) | \(\mathbb{Z}^+\) | Transit time (buckets) |
| \(Cap^{trans}_{m,t}\) | \(\mathbb{R}^+\) | Transport capacity on leg \(m\) departing bucket \(t\) |
| \(Cost^{trans}_{m}\) | \(\mathbb{R}^+\) | Transport cost per unit |
| \(Rel^{trans}_m\) | \([0, 1]\) | Reliability score |
| \(CO2^{trans}_m\) | \(\mathbb{R}^+\) | CO2 emissions per unit |
| \(Pref^{trans}_m\) | \(\mathbb{R}^+\) | Preference score (lower = better) |
**Transport Arrival Timing:**
If product \(p\) departs on leg \(m\) in bucket \(t\):
$$\text{Arrival bucket} = t + LT^{trans}_m$$
**Transport Capacity Constraint:**
$$\sum_{p \in P} x^{trans}_{m,p,t} \leq Cap^{trans}_{m,t} \cdot \delta^{cutoff}_{m,t} \quad \forall m \in M, t \in T$$
**F# Representation:**
```fsharp
type TransportParams = {
LeadTime: Map<TransportLegId, int> // LT^trans_m
Capacity: Map<TransportLegId * Bucket, decimal> // Cap^trans_{m,t}
CostPerUnit: Map<TransportLegId, decimal> // Cost^trans_m
Reliability: Map<TransportLegId, decimal> // Rel^trans_m
CO2PerUnit: Map<TransportLegId, decimal> // CO2^trans_m
Preference: Map<TransportLegId, decimal> // Pref^trans_m
}
```
### 4.9 Supplier Parameters
| Parameter | Domain | Description |
| ----------------------- | ---------------- | ----------------------------------- |
| \(MOQ_{v,p,s}\) | \(\mathbb{R}^+\) | Minimum order quantity |
| \(Lot_{v,p,s}\) | \(\mathbb{R}^+\) | Lot size (orders must be multiples) |
| \(LT^{sup}_{v,p,s}\) | \(\mathbb{Z}^+\) | Supplier lead time |
| \(Cap^{sup}_{v,p,s,t}\) | \(\mathbb{R}^+\) | Supplier capacity in bucket \(t\) |
| \(Cost^{sup}_{v,p,s}\) | \(\mathbb{R}^+\) | Purchase cost per unit |
| \(Rel^{sup}_{v,p,s}\) | \([0, 1]\) | Supplier reliability |
| \(Pref^{sup}_{v,p,s}\) | \(\mathbb{R}^+\) | Preference score (lower = better) |
**Lot Sizing Constraint:**
$$x^{sup}_{v,p,s,t} = Lot_{v,p,s} \cdot n^{lot}_{v,p,s,t} \quad \text{where } n^{lot} \in \mathbb{Z}^{\geq 0}$$
**MOQ Constraint:**
$$x^{sup}_{v,p,s,t} \geq MOQ_{v,p,s} \cdot z^{order}_{v,p,s,t} \quad \text{where } z^{order} \in \{0,1\}$$
**Supplier Arrival Timing:**
If order placed in bucket \(t\):
$$\text{Arrival bucket} = t + LT^{sup}_{v,p,s}$$
### 4.10 Shelf-Life Parameters (CPG / Perishables)
| Parameter | Domain | Description |
| ---------------------- | ----------------------- | ---------------------------------- |
| \(SL_p\) | \(\mathbb{Z}^+\) | Shelf life in buckets |
| \(MinLife^{ship}_p\) | \(\mathbb{Z}^{\geq 0}\) | Minimum remaining life at shipment |
| \(Inv^{init}_{p,s,a}\) | \(\mathbb{R}^{\geq 0}\) | Initial inventory by age bucket |
**Age Evolution:**
$$Inv^{age}_{p,s,t+1,a+1} = Inv^{age}_{p,s,t,a} - \text{consumption}_a - \text{shipment}_a + \text{production (if } a=0 \text{)}$$
**Expiration:**
$$Inv^{age}_{p,s,t,a} = 0 \quad \forall a > SL_p \quad \text{(expired inventory discarded)}$$
**Shipping Feasibility:**
$$ship^{age}_{d,t,a} > 0 \implies SL_p - a - LT^{trans} \geq MinLife^{ship}_p$$
### 4.11 Holding Cost Parameters
| Parameter | Domain | Description |
| -------------------- | ---------------- | ------------------------------------------------- |
| \(h^{FG}_{p,s}\) | \(\mathbb{R}^+\) | Holding cost per unit per bucket (finished goods) |
| \(h^{WIP}_{p,s}\) | \(\mathbb{R}^+\) | In-process holding cost (WIP) |
| \(h^{supply}_{p,s}\) | \(\mathbb{R}^+\) | Supply holding cost (raw materials) |
**Total Holding Cost:**
$$HoldingCost = \sum_{(p,s) \in PISP} \sum_{t \in T} \left( h^{FG}_{p,s} \cdot Inv_{p,s,t} + h^{WIP}_{p,s} \cdot Inv^{WIP}_{p,s,t} \right)$$
### 4.12 Objective Weights / Policy Parameters
| Parameter | Domain | Description | Typical Range |
| ---------------- | ---------------- | ---------------------------- | ------------- |
| \(w^{late}\) | \(\mathbb{R}^+\) | Lateness penalty weight | 100-1000 |
| \(w^{early}\) | \(\mathbb{R}^+\) | Earliness penalty weight | 1-10 |
| \(w^{short}\) | \(\mathbb{R}^+\) | Shortfall penalty weight | 1000-10000 |
| \(w^{cost}\) | \(\mathbb{R}^+\) | Cost weight | 1 |
| \(w^{hold}\) | \(\mathbb{R}^+\) | Holding cost weight | 1-10 |
| \(w^{overutil}\) | \(\mathbb{R}^+\) | Capacity overload penalty | 500-5000 |
| \(w^{co2}\) | \(\mathbb{R}^+\) | Emissions penalty | 0.1-10 |
| \(w^{churn}\) | \(\mathbb{R}^+\) | Plan stability penalty | 10-100 |
| \(w^{pref}\) | \(\mathbb{R}^+\) | Preference deviation penalty | 1-50 |
| \(w^{spread}\) | \(\mathbb{R}^+\) | Production leveling penalty | 10-100 |
| \(Cap^{cost}\) | \(\mathbb{R}^+\) | Hard cap on total cost | Optional |
| \(Cap^{co2}\) | \(\mathbb{R}^+\) | Hard cap on emissions | Optional |
**Weight Normalization Strategy:**
Weights should be scaled so contributions to the objective are comparable:
$$w^{late} \cdot \mathbb{E}[Lateness] \approx w^{cost} \cdot \mathbb{E}[Cost] \approx w^{hold} \cdot \mathbb{E}[Holding]$$
### 4.13 Baseline / Churn Reference Parameters
| Parameter | Domain | Description |
| ------------------------ | ----------------------- | ----------------------------------- |
| \(Base^{prod}_{r,k,t}\) | \(\mathbb{R}^{\geq 0}\) | Previous plan production quantities |
| \(Base^{trans}_{m,p,t}\) | \(\mathbb{R}^{\geq 0}\) | Previous plan transport quantities |
**Churn Measurement:**
$$Churn^{prod}_{r,k,t} = \lvert x^{prod}_{r,k,t} - Base^{prod}_{r,k,t} \rvert$$
Linearized using auxiliary variables:
$$Churn^{prod}_{r,k,t} \geq x^{prod}_{r,k,t} - Base^{prod}_{r,k,t}$$
$$Churn^{prod}_{r,k,t} \geq Base^{prod}_{r,k,t} - x^{prod}_{r,k,t}$$
### 4.14 Big-M and Bounds
> **Strategy**: Compute tight upper bounds from input data to avoid numerical issues.
**Bounds Derivation:**
| Bound | Formula | Usage |
| ------------------- | ------------------------------------------------------------- | ------------------------------ |
| \(M^{demand}_d\) | \(= Qty_d\) | Demand fulfillment constraints |
| \(M^{prod}_{r,t}\) | \(= \min(Cap^{eff}_{g,t} / \rho_{r,k}, \sum_d Qty_d)\) | Production constraints |
| \(M^{trans}_{m,t}\) | \(= Cap^{trans}_{m,t}\) | Transport constraints |
| \(M^{inv}_{p,s,t}\) | \(= Inv^{init}_{p,s} + \sum_t Inb^{firm}_{p,s,t} + M^{prod}\) | Inventory bounds |
**F# Bounds Computation:**
```fsharp
let computeBounds (input: OptimizerInput) =
let demandBounds =
input.Demands |> List.map (fun d -> d.Id, d.Quantity) |> Map.ofList
let productionBounds =
input.Routings |> List.collect (fun r ->
[0 .. input.Horizon - 1] |> List.map (fun t ->
let capBound =
input.Capacity
|> List.filter (fun c -> c.ResourceGroup = r.Steps.Head.ResourceGroup && c.Bucket = t)
|> List.sumBy (fun c -> c.AvailableCapacity)
(r.Id, t), min capBound (input.Demands |> List.sumBy (_.Quantity))
)
) |> Map.ofList
{ DemandBounds = demandBounds; ProductionBounds = productionBounds }
```
---
## 5. Decision Variables (Primary)
> Decision variables are what the optimizer **chooses**. Well-designed variables enable clean constraints.
**Variable Naming Convention:**
- Lowercase letters for primary decisions: \(x, y, z\)
- Superscripts for type: \(^{prod}, ^{trans}, ^{sup}\)
- Subscripts for indices: \(_{r,k,t}\) for routing-step-time
**Variable Count Estimation:**
| Variable Type | Cardinality | Example (1000 products, 90 days) |
| --------------------- | --------------------------------------------------------------- | -------------------------------- |
| \(y_{d,t}\) | \(\lvert D \rvert \cdot \lvert T \rvert\) | 50,000 × 90 = 4.5M |
| \(Inv_{p,s,t}\) | \(\lvert PISP \rvert \cdot \lvert T \rvert\) | 5,000 × 90 = 450K |
| \(x^{prod}_{r,k,t}\) | \(\lvert R \rvert \cdot \bar{K} \cdot \lvert T \rvert\) | 2,000 × 3 × 90 = 540K |
| \(x^{trans}_{m,p,t}\) | \(\lvert M \rvert \cdot \lvert P \rvert \cdot \lvert T \rvert\) | 50 × 1,000 × 90 = 4.5M |
> **Sparsity matters**: Use sparse indexing—only create variables where feasible.
### 5.1 Demand Fulfillment Variables
| Variable | Domain | Bounds | Description |
| ----------- | ----------------------- | -------------- | -------------------------------------------------- |
| \(y_{d,t}\) | \(\mathbb{R}^{\geq 0}\) | \([0, Qty_d]\) | Quantity of demand \(d\) fulfilled in bucket \(t\) |
| \(acc_d\) | \(\{0,1\}\) | — | Accept/reject binary for demand \(d\) (optional) |
**Mathematical Definition:**
$$y_{d,t} \in [0, Qty_d] \quad \forall d \in D, t \in T$$
$$\sum_{t \in T} y_{d,t} \leq Qty_d \quad \text{(cannot fulfill more than demanded)}$$
**Linking with Accept/Reject:**
$$y_{d,t} \leq Qty_d \cdot acc_d \quad \forall d, t \quad \text{(if not accepted, no fulfillment)}$$
**F# Variable Creation:**
```fsharp
let createFulfillmentVars (solver: ISolver) (demands: DemandInput list) (horizon: int) =
demands |> List.collect (fun d ->
[0 .. horizon - 1] |> List.map (fun t ->
let varName = sprintf "y[%s,%d]" d.Id t
let ub = d.Quantity
(d.Id, t), solver.AddVariable(varName, 0m, ub, Continuous)
)
) |> Map.ofList
```
### 5.2 Inventory Variables
| Variable | Domain | Bounds | Description |
| --------------------- | ----------------------- | ---------------------- | ------------------------- |
| \(Inv_{p,s,t}\) | \(\mathbb{R}^{\geq 0}\) | \([0, M^{inv}_{p,s}]\) | End-of-bucket inventory |
| \(Inv^{WIP}_{p,s,t}\) | \(\mathbb{R}^{\geq 0}\) | \([0, M^{WIP}_{p,s}]\) | Work-in-process inventory |
**Mathematical Definition:**
$$Inv_{p,s,t} \geq 0 \quad \forall (p,s) \in PISP, t \in T$$
**Initial Condition:**
$$Inv_{p,s,-1} = Inv^{init}_{p,s} \quad \text{(given parameter)}$$
**Inventory Balance (referenced in constraints):**
$$Inv_{p,s,t} = Inv_{p,s,t-1} + \text{Supply}_{p,s,t} - \text{Consumption}_{p,s,t}$$
where:
- \(\text{Supply}_{p,s,t}\) = inbound + production + transport arrivals + purchases
- \(\text{Consumption}_{p,s,t}\) = BOM usage + transport departures + demand fulfillment
### 5.3 Production Variables
| Variable | Domain | Bounds | Description |
| -------------------- | ----------------------- | ----------------------- | ---------------------------------------------- |
| \(x^{prod}_{r,k,t}\) | \(\mathbb{R}^{\geq 0}\) | \([0, M^{prod}_{r,t}]\) | Quantity completing step \(k\) in bucket \(t\) |
| \(z^{route}_{d,r}\) | \(\{0,1\}\) | — | Binary: demand \(d\) uses routing \(r\) |
| \(\psi_{r,k,t}\) | \(\{0,1\}\) | — | Binary: step \(k\) is active in bucket \(t\) |
**Mathematical Definition:**
$$x^{prod}_{r,k,t} \geq 0 \quad \forall r \in R, k \in K_r, t \in T$$
**Production Only When Routing Valid:**
$$x^{prod}_{r,k,t} \leq M^{prod}_{r,t} \cdot \delta^{routing}_{r,t} \quad \forall r, k, t$$
**Routing Selection (if demands require exclusive routing):**
$$\sum_{r \in R_{p_d,s_d}} z^{route}_{d,r} = 1 \quad \forall d \in D \text{ (exactly one routing)}$$
$$y_{d,t} \leq Qty_d \cdot z^{route}_{d,r} \quad \text{(only if routing selected)}$$
**Step Activation Linking:**
$$x^{prod}_{r,k,t} \leq M^{prod}_{r,t} \cdot \psi_{r,k,t}$$
$$x^{prod}_{r,k,t} \geq \epsilon \cdot \psi_{r,k,t} \quad \text{(if active, produce at least } \epsilon \text{)}$$
**F# Variable Creation:**
```fsharp
let createProductionVars (solver: ISolver) (routings: RoutingInput list) (horizon: int) =
routings |> List.collect (fun r ->
r.Steps |> List.collect (fun step ->
[0 .. horizon - 1]
|> List.filter (fun t -> r.ValidityMask.Contains t)
|> List.map (fun t ->
let varName = sprintf "xProd[%s,%d,%d]" r.Id step.StepIndex t
let ub = computeProductionBound r t
((r.Id, step.StepIndex, t), solver.AddVariable(varName, 0m, ub, Continuous))
)
)
) |> Map.ofList
```
### 5.4 Transport Variables
| Variable | Domain | Bounds | Description |
| --------------------- | ----------------------- | -------------------------- | ----------------------------------- |
| \(x^{trans}_{m,p,t}\) | \(\mathbb{R}^{\geq 0}\) | \([0, Cap^{trans}_{m,t}]\) | Quantity shipped on leg \(m\) |
| \(z^{itin}_{d,h}\) | \(\{0,1\}\) | — | Binary: demand uses itinerary \(h\) |
**Mathematical Definition:**
$$x^{trans}_{m,p,t} \geq 0 \quad \forall m \in M, p \in P, t \in T$$
**Transport Only at Cutoff Times:**
$$x^{trans}_{m,p,t} \leq Cap^{trans}_{m,t} \cdot \delta^{cutoff}_{m,t}$$
If \(\delta^{cutoff}_{m,t} = 0\) (no departure), then \(x^{trans}_{m,p,t} = 0\).
**Arrival Relationship:**
Material departing at bucket \(t\) arrives at bucket \(t + LT^{trans}_m\):
$$\text{Arrival}_{m,p,t'} = x^{trans}_{m,p,t'-LT^{trans}_m} \quad \text{where } t' = t + LT^{trans}_m$$
### 5.5 Procurement Variables
| Variable | Domain | Bounds | Description |
| ----------------------- | ----------------------- | ---------------------------------- | ------------------------------- |
| \(x^{sup}_{v,p,s,t}\) | \(\mathbb{R}^{\geq 0}\) | \([0, Cap^{sup}_{v,p,s,t}]\) | Quantity arriving from supplier |
| \(n^{lot}_{v,p,s,t}\) | \(\mathbb{Z}^{\geq 0}\) | \([0, \lfloor Cap / Lot \rfloor]\) | Number of lots ordered |
| \(z^{order}_{v,p,s,t}\) | \(\{0,1\}\) | — | Binary: order placed |
**Lot Size Constraint:**
$$x^{sup}_{v,p,s,t} = Lot_{v,p,s} \cdot n^{lot}_{v,p,s,t}$$
**MOQ Linking:**
$$x^{sup}_{v,p,s,t} \geq MOQ_{v,p,s} \cdot z^{order}_{v,p,s,t}$$
$$x^{sup}_{v,p,s,t} \leq Cap^{sup}_{v,p,s,t} \cdot z^{order}_{v,p,s,t}$$
**Order Timing:**
If order placed in bucket \(t_{order}\):
$$\text{Arrival bucket} = t_{order} + LT^{sup}_{v,p,s}$$
### 5.6 Campaign / Sequencing Variables
| Variable | Domain | Bounds | Description |
| ---------------------- | ----------------------- | ------------------ | -------------------------------------------------------- |
| \(run_{p,l,t}\) | \(\mathbb{R}^{\geq 0}\) | \([0, Cap_{l,t}]\) | Production run time for product \(p\) on line \(l\) |
| \(\phi_{p,l,t}\) | \(\{0,1\}\) | — | Binary: product \(p\) runs on line \(l\) in bucket \(t\) |
| \(\sigma_{i,j,l,t}\) | \(\{0,1\}\) | — | Binary: changeover from \(i\) to \(j\) |
| \(\phi^{fam}_{f,l,t}\) | \(\{0,1\}\) | — | Binary: family \(f\) runs on line \(l\) in bucket \(t\) |
**Product Run Linking:**
$$run_{p,l,t} \leq Cap_{l,t} \cdot \phi_{p,l,t} \quad \text{(only run if product is active)}$$
$$run_{p,l,t} \geq MinRun_{p,l} \cdot \phi_{p,l,t} \quad \text{(minimum run requirement)}$$
**Single Product Per Bucket (simplified):**
$$\sum_{p \in P} \phi_{p,l,t} \leq 1 \quad \forall l, t \quad \text{(at most one product per bucket)}$$
**Changeover Detection:**
$$\sigma_{i,j,l,t} \geq \phi_{i,l,t-1} + \phi_{j,l,t} - 1 \quad \text{(changeover if } i \text{ ran and now } j \text{ runs)}$$
**Family Aggregation:**
$$\phi^{fam}_{f,l,t} \geq \phi_{p,l,t} \quad \forall p \in FamilyProducts_f$$
$$\phi^{fam}_{f,l,t} \leq \sum_{p \in FamilyProducts_f} \phi_{p,l,t}$$
### 5.7 Shelf-Life / Age-Tracking Variables (CPG)
| Variable | Domain | Bounds | Description |
| ----------------------- | ----------------------- | ------ | ------------------------------------- |
| \(Inv^{age}_{p,s,t,a}\) | \(\mathbb{R}^{\geq 0}\) | — | Inventory with age \(a\) |
| \(ship^{age}_{d,t,a}\) | \(\mathbb{R}^{\geq 0}\) | — | Shipment from age \(a\) |
| \(use^{age}_{r,k,t,a}\) | \(\mathbb{R}^{\geq 0}\) | — | Production consumption from age \(a\) |
**Age Bucket Range:**
$$a \in A_p = \{0, 1, \ldots, SL_p\} \quad \forall p$$
**Age Balance Equation:**
$$Inv^{age}_{p,s,t,a} = \begin{cases}
\sum_r x^{prod}_{r,K_r,t}[\text{output}=(p,s)] & \text{if } a = 0 \text{ (fresh production)} \\
Inv^{age}_{p,s,t-1,a-1} - ship^{age}_{p,s,t,a} - use^{age}_{p,s,t,a} & \text{if } 0 < a \leq SL_p \\
0 & \text{if } a > SL_p \text{ (expired)}
\end{cases}$$
**FEFO (First Expired, First Out) Policy:**
Prefer shipping older inventory first:
$$ship^{age}_{d,t,a+1} \leq M \cdot (1 - \epsilon^{age}_{d,t,a})$$
where \(\epsilon^{age}_{d,t,a} = 1\) if age \(a\) inventory is exhausted.
**Minimum Life at Shipment:**
$$ship^{age}_{d,t,a} = 0 \quad \text{if } SL_{p_d} - a < MinLife^{ship}_{p_d}$$
### 5.8 Pegging / Assignment Variables
> **Pegging** links each demand to specific supply sources. This is essential for traceability, source capacity enforcement, and explainability.
#### 5.8.1 Supply Source Types
| Source Type | Symbol | Description | Availability |
| ---------------------- | --------------- | --------------------------------------------- | --------------------------------- |
| **Inventory** | \(INV\) | On-hand inventory at \(t=0\) | Parameter \(Inv^{init}_{p,s}\) |
| **Firm Inbound** | \(FIRM_j\) | Committed PO/WO/TO arriving | Parameter \(Inb^{firm}_{j}\) |
| **Planned Production** | \(PROD_{r,t}\) | Production from routing \(r\) in bucket \(t\) | Variable \(x^{prod}_{r,K_r,t}\) |
| **Transport Arrival** | \(TRANS_{m,t}\) | Transport on leg \(m\) arriving \(t\) | Variable \(x^{trans}_{m,p,t-LT}\) |
| **Supplier Receipt** | \(SUP_{v,t}\) | Purchase from supplier \(v\) arriving \(t\) | Variable \(x^{sup}_{v,p,s,t}\) |
#### 5.8.2 Assignment Variable Definitions
**By Source Type:**
| Variable | Domain | Bounds | Description |
| -------------------------- | ----------------------- | --------------------------- | ------------------------------------------------------------------------------------------ |
| \(\alpha^{INV}_{d,t}\) | \(\mathbb{R}^{\geq 0}\) | \([0, Qty_d]\) | Quantity assigned to demand \(d\) from initial inventory, used in bucket \(t\) |
| \(\alpha^{FIRM}_{d,j}\) | \(\mathbb{R}^{\geq 0}\) | \([0, \min(Qty_d, Qty_j)]\) | Quantity assigned to demand \(d\) from firm inbound \(j\) |
| \(\alpha^{PROD}_{d,r,t}\) | \(\mathbb{R}^{\geq 0}\) | \([0, Qty_d]\) | Quantity assigned to demand \(d\) from production routing \(r\) completing in bucket \(t\) |
| \(\alpha^{TRANS}_{d,m,t}\) | \(\mathbb{R}^{\geq 0}\) | \([0, Qty_d]\) | Quantity assigned to demand \(d\) from transport leg \(m\) arriving in bucket \(t\) |
| \(\alpha^{SUP}_{d,v,t}\) | \(\mathbb{R}^{\geq 0}\) | \([0, Qty_d]\) | Quantity assigned to demand \(d\) from supplier \(v\) receipt in bucket \(t\) |
#### 5.8.3 Mathematical Formulation
**Total Assignment for Demand:**
$$\sum_{t} \alpha^{INV}_{d,t} + \sum_{j \in FIRM_{p_d,s_d}} \alpha^{FIRM}_{d,j} + \sum_{r \in R_{p_d,s_d}} \sum_{t} \alpha^{PROD}_{d,r,t} + \sum_{m \in LegsTo_{s_d}} \sum_{t} \alpha^{TRANS}_{d,m,t} + \sum_{v \in Suppliers_{p_d,s_d}} \sum_{t} \alpha^{SUP}_{d,v,t} = \sum_{t} y_{d,t}$$
**Simplified notation:**
$$\sum_{src \in Sources_{p_d,s_d}} \alpha_{d,src} = Fulfilled_d \quad \forall d \in D$$
#### 5.8.4 Source Availability Constraints
**Inventory Source Constraint:**
$$\sum_{d : (p_d,s_d) = (p,s)} \sum_{t' \leq t} \alpha^{INV}_{d,t'} \leq Inv^{init}_{p,s} + \sum_{t'' < t} NetInflow_{p,s,t''} \quad \forall (p,s), t$$
> Cumulative assignment from inventory ≤ cumulative available inventory
**Firm Inbound Source Constraint:**
$$\sum_{d : (p_d,s_d) = (p_j,s_j)} \alpha^{FIRM}_{d,j} \leq Qty_j \quad \forall j \in FIRM$$
> Total assigned from a firm order ≤ firm order quantity
**Production Source Constraint (Key Linking):**
$$\sum_{d : (p_d,s_d) = (Output_r, OutputSP_r)} \alpha^{PROD}_{d,r,t} \leq x^{prod}_{r,K_r,t} + OverUtil^{PROD}_{r,t} \quad \forall r \in R, t \in T$$
> Total assigned from production ≤ production quantity (with slack for over-utilization)
**Transport Arrival Source Constraint:**
$$\sum_{d : s_d = Dest_m, p_d = p} \alpha^{TRANS}_{d,m,t} \leq x^{trans}_{m,p,t-LT^{trans}_m} + OverUtil^{TRANS}_{m,p,t} \quad \forall m, p, t$$
> Total assigned from transport arrival ≤ transported quantity
**Supplier Receipt Source Constraint:**
$$\sum_{d : (p_d,s_d) = (p,s)} \alpha^{SUP}_{d,v,t} \leq x^{sup}_{v,p,s,t} + OverUtil^{SUP}_{v,p,s,t} \quad \forall (v,p,s), t$$
> Total assigned from supplier ≤ purchased quantity
#### 5.8.5 Timing Feasibility Constraints
**Assignment only from available supply:**
$$\alpha^{INV}_{d,t} > 0 \implies t \leq \tau^{fulfill}_d \quad \text{(inventory available from t=0)}$$
$$\alpha^{FIRM}_{d,j} > 0 \implies ArrivalBucket_j \leq \tau^{fulfill}_d$$
$$\alpha^{PROD}_{d,r,t} > 0 \implies t \leq \tau^{fulfill}_d$$
$$\alpha^{TRANS}_{d,m,t} > 0 \implies t \leq \tau^{fulfill}_d$$
$$\alpha^{SUP}_{d,v,t} > 0 \implies t \leq \tau^{fulfill}_d$$
**Linearized Form:**
$$\alpha^{PROD}_{d,r,t} \leq Qty_d \cdot \mathbb{1}[t \leq \tau^{fulfill,max}_d]$$
where \(\tau^{fulfill,max}_d\) is the latest bucket demand \(d\) can be fulfilled.
#### 5.8.6 F# Variable Creation
```fsharp
/// Supply source types with identifiers
type SupplySource =
| Inventory // Initial on-hand
| FirmInbound of FirmOrderId // PO, WO, TO
| PlannedProduction of RoutingId * Bucket // Optimizer decides
| TransportArrival of TransportLegId * Bucket // Optimizer decides
| SupplierReceipt of SupplierId * Bucket // Optimizer decides
/// Create all assignment variables for demand-supply matching
let createAssignmentVariables (solver: ISolver) (input: OptimizerInput) =
let assignVars = Dictionary<DemandId * SupplySource, VarRef>()
for d in input.Demands do
let p, s = d.Product, d.StockingPoint
// 1. Assignment from INVENTORY
for t in 0 .. input.Horizon - 1 do
if t <= d.DueBucket + gracePeriod then // Only create if timing feasible
let varName = sprintf "α_INV[%s,%d]" d.Id t
let varRef = solver.AddVariable(varName, 0m, d.Quantity, Continuous)
assignVars.Add((d.Id, Inventory), varRef)
// 2. Assignment from FIRM INBOUND
for firm in getFirmInboundsFor p s do
if firm.ArrivalBucket <= d.DueBucket + gracePeriod then
let varName = sprintf "α_FIRM[%s,%s]" d.Id firm.Id
let ub = min d.Quantity firm.Quantity
let varRef = solver.AddVariable(varName, 0m, ub, Continuous)
assignVars.Add((d.Id, FirmInbound firm.Id), varRef)
// 3. Assignment from PLANNED PRODUCTION
for r in getRoutingsFor p s do
for t in 0 .. input.Horizon - 1 do
if t <= d.DueBucket + gracePeriod && isRoutingValidAt r t then
let varName = sprintf "α_PROD[%s,%s,%d]" d.Id r.Id t
let varRef = solver.AddVariable(varName, 0m, d.Quantity, Continuous)
assignVars.Add((d.Id, PlannedProduction (r.Id, t)), varRef)
// 4. Assignment from TRANSPORT ARRIVAL
for m in getLegsTo s do
for t in 0 .. input.Horizon - 1 do
let departBucket = t - getLeadTime m
if departBucket >= 0 && t <= d.DueBucket + gracePeriod then
let varName = sprintf "α_TRANS[%s,%s,%d]" d.Id m.Id t
let varRef = solver.AddVariable(varName, 0m, d.Quantity, Continuous)
assignVars.Add((d.Id, TransportArrival (m.Id, t)), varRef)
// 5. Assignment from SUPPLIER RECEIPT
for v in getSuppliersFor p s do
for t in 0 .. input.Horizon - 1 do
if t <= d.DueBucket + gracePeriod && isSupplierAvailable v p s t then
let varName = sprintf "α_SUP[%s,%s,%d]" d.Id v.Id t
let varRef = solver.AddVariable(varName, 0m, d.Quantity, Continuous)
assignVars.Add((d.Id, SupplierReceipt (v.Id, t)), varRef)
assignVars |> Seq.map (|KeyValue|) |> Map.ofSeq
/// Variable count estimation
let estimateAssignmentVarCount (input: OptimizerInput) =
let D = input.Demands.Length
let T = input.Horizon
let avgRoutings = 2 // average routings per PISP
let avgLegs = 3 // average transport legs to a stocking point
let avgSuppliers = 2 // average suppliers per PISP
let avgFirm = 5 // average firm inbounds per PISP
// INV: D × T (but sparse)
// FIRM: D × avgFirm
// PROD: D × avgRoutings × T
// TRANS: D × avgLegs × T
// SUP: D × avgSuppliers × T
D * (T + avgFirm + avgRoutings * T + avgLegs * T + avgSuppliers * T)
```
#### 5.8.7 Pegging Output
After optimization, pegging links are extracted:
```fsharp
type PeggingLink = {
DemandId: DemandId
SourceType: SupplySourceType
SourceId: string // Routing ID, Firm Order ID, etc.
SourceBucket: Bucket option // When source is available
Quantity: decimal
FulfillmentBucket: Bucket // When demand is fulfilled
}
let extractPeggingLinks (solution: Solution) (assignVars: Map<DemandId * SupplySource, VarRef>) =
assignVars
|> Map.toSeq
|> Seq.filter (fun ((d, src), varRef) -> solution.GetValue(varRef) > epsilon)
|> Seq.map (fun ((d, src), varRef) ->
let qty = solution.GetValue(varRef)
{
DemandId = d
SourceType = getSourceType src
SourceId = getSourceId src
SourceBucket = getSourceBucket src
Quantity = qty
FulfillmentBucket = getFulfillmentBucket d solution
}
)
|> Seq.toList
```
---
## 6. Support Variables (Slacks, Penalties, Explainability)
> Support variables capture **violations** and **soft constraint deviations**. Each maps to a KPI for explainability.
**Design Principle:** Every support variable \(s\) appears in the objective with weight \(w_s\):
$$\text{Objective} = \ldots + w_s \cdot s + \ldots$$
When \(s > 0\), it indicates a constraint violation or target deviation, which:
1. Increases the objective (cost)
2. Maps to a specific KPI for reporting
3. Provides explanatory information (why the plan isn't "perfect")
### 6.1 Delivery Performance Variables
| Variable | Domain | KPI | Definition |
| ----------- | ----------------------- | ------------------------ | -------------------- |
| \(Back_d\) | \(\mathbb{R}^{\geq 0}\) | DeliveryPerformanceScore | Weighted lateness |
| \(Early_d\) | \(\mathbb{R}^{\geq 0}\) | EarlinessScore | Weighted earliness |
| \(Short_d\) | \(\mathbb{R}^{\geq 0}\) | DeliveryFulfillmentScore | Unfulfilled quantity |
**Lateness Definition (Bucket-Weighted):**
$$Back_d = \sum_{t > \tau^{due}_d} (t - \tau^{due}_d) \cdot y_{d,t}$$
This measures "bucket-days late × quantity". Example: 100 units delivered 2 buckets late → \(Back_d = 200\).
**Alternative: Quantity-Based Lateness:**
$$Back^{qty}_d = \sum_{t > \tau^{due}_d} y_{d,t} \quad \text{(just quantity late, ignoring how late)}$$
**Earliness Definition:**
$$Early_d = \sum_{t < \tau^{due}_d} (\tau^{due}_d - t) \cdot y_{d,t}$$
**Shortfall Definition:**
$$Short_d = Qty_d - \sum_{t \in T} y_{d,t}$$
**Constraint Formulation:**
$$\sum_{t \in T} y_{d,t} + Short_d = Qty_d \quad \forall d \in D$$
**F# Slack Variable Creation:**
```fsharp
let createDeliverySlacks (solver: ISolver) (demands: DemandInput list) =
let lateVars = demands |> List.map (fun d ->
d.Id, solver.AddVariable(sprintf "Back[%s]" d.Id, 0m, Decimal.MaxValue, Continuous)
) |> Map.ofList
let shortVars = demands |> List.map (fun d ->
d.Id, solver.AddVariable(sprintf "Short[%s]" d.Id, 0m, d.Quantity, Continuous)
) |> Map.ofList
{ Lateness = lateVars; Shortfall = shortVars }
```
### 6.2 Inventory Adherence Variables
| Variable | Domain | KPI | Definition |
| ---------------------- | ----------------------- | ----------------------- | ------------ |
| \(InvBelow_{p,s,t}\) | \(\mathbb{R}^{\geq 0}\) | InventoryAdherenceScore | Below target |
| \(InvAbove_{p,s,t}\) | \(\mathbb{R}^{\geq 0}\) | InventoryAdherenceScore | Above max |
| \(SafetyViol_{p,s,t}\) | \(\mathbb{R}^{\geq 0}\) | SafetyViolation | Below safety |
**Target Adherence:**
$$Inv_{p,s,t} + InvBelow_{p,s,t} \geq InvTarget_{p,s}$$
$$Inv_{p,s,t} - InvAbove_{p,s,t} \leq InvMax_{p,s}$$
**Safety Stock Violation:**
$$Inv_{p,s,t} + SafetyViol_{p,s,t} \geq Safety_{p,s}$$
**Combined Adherence Score:**
$$InvAdherence = \sum_{(p,s) \in PISP} \sum_{t \in T} (InvBelow_{p,s,t} + InvAbove_{p,s,t})$$
### 6.3 Holding Cost Variables
| Variable | Domain | KPI | Definition |
| ------------------- | ----------------------- | --------------------- | ----------- |
| \(Hold_{p,s,t}\) | \(\mathbb{R}^{\geq 0}\) | SupplyHoldingScore | FG holding |
| \(HoldWIP_{p,s,t}\) | \(\mathbb{R}^{\geq 0}\) | InprocessHoldingScore | WIP holding |
**Holding Cost Relationship:**
$$Hold_{p,s,t} = h^{FG}_{p,s} \cdot Inv_{p,s,t}$$
$$HoldWIP_{p,s,t} = h^{WIP}_{p,s} \cdot Inv^{WIP}_{p,s,t}$$
**Total Holding Cost:**
$$TotalHolding = \sum_{(p,s) \in PISP} \sum_{t \in T} (Hold_{p,s,t} + HoldWIP_{p,s,t})$$
> **Note:** These are auxiliary variables—they linearize the product of cost and inventory for the objective.
### 6.4 Capacity Overload Variables
| Variable | Domain | KPI | Definition |
| ------------------- | ----------------------- | ------------------------ | -------------------- |
| \(OverUtil_{g,t}\) | \(\mathbb{R}^{\geq 0}\) | ResourceCapacityOverload | Overload amount |
| \(UnderUtil_{g,t}\) | \(\mathbb{R}^{\geq 0}\) | ResourceUtilization | Underutilization |
| \(Spread_{g,t}\) | \(\mathbb{R}^{\geq 0}\) | SpreadConsumption | Deviation from level |
**Capacity Constraint with Overload:**
$$\sum_{r,k : ResGroup_{r,k} = g} \rho_{r,k} \cdot x^{prod}_{r,k,t} \leq Cap^{eff}_{g,t} + OverUtil_{g,t}$$
**Underutilization Measurement:**
$$UnderUtil_{g,t} = Cap^{eff}_{g,t} - \sum_{r,k : ResGroup_{r,k} = g} \rho_{r,k} \cdot x^{prod}_{r,k,t}$$
**Utilization Rate:**
$$Util_{g,t} = \frac{\sum_{r,k} \rho_{r,k} \cdot x^{prod}_{r,k,t}}{Cap^{eff}_{g,t}} \in [0, 1+]$$
**Production Leveling (Spread):**
Let \(\bar{x}_g = \frac{1}{\lvert T \rvert} \sum_t \sum_{r,k:g} x^{prod}_{r,k,t}\) be average production.
$$Spread_{g,t} \geq \sum_{r,k:g} x^{prod}_{r,k,t} - \bar{x}_g$$
$$Spread_{g,t} \geq \bar{x}_g - \sum_{r,k:g} x^{prod}_{r,k,t}$$
### 6.5 Supply Chain Violation Variables
| Variable | Domain | KPI | Definition |
| ------------------- | ----------------------- | --------------------- | ----------------- |
| \(MissUp_{r,k,t}\) | \(\mathbb{R}^{\geq 0}\) | MissingUpstreamSupply | Material shortage |
| \(InvalUp_{r,k,t}\) | \(\mathbb{R}^{\geq 0}\) | InvalidUpstreamSupply | Wrong material |
| \(LateUp_{r,k,t}\) | \(\mathbb{R}^{\geq 0}\) | LateUpstreamSupply | Late material |
**Missing Upstream Material:**
For step \(k\) requiring components:
$$\sum_{c \in Inputs_{r,k}} \beta_{c,r,k} \cdot x^{prod}_{r,k,t} \leq Avail^{comp}_{c,s,t-LT} + MissUp_{r,k,t}$$
where \(Avail^{comp}\) is available component inventory at the right time.
**Invalid Upstream (Characteristic Mismatch):**
$$InvalUp_{r,k,t} = \sum_{src \in InvalidSources_{r,k,t}} \alpha_{r,k,src}$$
where \(InvalidSources\) are sources not matching required characteristics.
**Late Upstream:**
$$LateUp_{r,k,t} = \text{quantity from sources arriving after needed}$$
### 6.6 Assignment / Pegging Violation Variables
> These variables track when demand-to-source assignment violates source availability.
| Variable | Domain | KPI | Definition |
| ---------------------------- | ----------------------- | ------------------ | ------------------------------------- |
| \(OverAssign_d\) | \(\mathbb{R}^{\geq 0}\) | OverAssignedDemand | Assigned more than demanded |
| \(OverUtil^{INV}_{p,s,t}\) | \(\mathbb{R}^{\geq 0}\) | OverUtilizedSupply | Assigned from inventory > available |
| \(OverUtil^{FIRM}_{j}\) | \(\mathbb{R}^{\geq 0}\) | OverUtilizedSupply | Assigned from firm order > quantity |
| \(OverUtil^{PROD}_{r,t}\) | \(\mathbb{R}^{\geq 0}\) | OverUtilizedSupply | Assigned from production > produced |
| \(OverUtil^{TRANS}_{m,p,t}\) | \(\mathbb{R}^{\geq 0}\) | OverUtilizedSupply | Assigned from transport > transported |
| \(OverUtil^{SUP}_{v,p,s,t}\) | \(\mathbb{R}^{\geq 0}\) | OverUtilizedSupply | Assigned from supplier > purchased |
| \(PartPiece_d\) | \(\mathbb{R}^{\geq 0}\) | PartialPiece | Indivisible demand partially assigned |
**Over-Assignment (demand side):**
$$\sum_{src} \alpha_{d,src} \leq Qty_d + OverAssign_d \quad \forall d \in D$$
**Over-Utilization by Source Type:**
**Inventory:**
$$\sum_{d:(p_d,s_d)=(p,s)} \sum_{t' \leq t} \alpha^{INV}_{d,t'} \leq Inv^{init}_{p,s} + OverUtil^{INV}_{p,s,t} \quad \forall (p,s), t$$
**Firm Inbound:**
$$\sum_{d:(p_d,s_d)=(p_j,s_j)} \alpha^{FIRM}_{d,j} \leq Qty_j + OverUtil^{FIRM}_j \quad \forall j \in FIRM$$
**Planned Production (links to production variable):**
$$\sum_{d} \alpha^{PROD}_{d,r,t} \leq x^{prod}_{r,K_r,t} + OverUtil^{PROD}_{r,t} \quad \forall r \in R, t \in T$$
**Transport Arrival (links to transport variable):**
$$\sum_{d} \alpha^{TRANS}_{d,m,t} \leq x^{trans}_{m,p,t-LT^{trans}_m} + OverUtil^{TRANS}_{m,p,t} \quad \forall m, p, t$$
**Supplier Receipt (links to purchase variable):**
$$\sum_{d} \alpha^{SUP}_{d,v,t} \leq x^{sup}_{v,p,s,t} + OverUtil^{SUP}_{v,p,s,t} \quad \forall (v,p,s), t$$
**Partial Piece (for indivisible demands):**
If demand \(d\) has \(\delta^{full}_d = 1\):
$$\sum_t y_{d,t} = Qty_d \cdot z^{accept}_d + PartPiece_d$$
where \(z^{accept}_d \in \{0,1\}\) indicates full acceptance.
> **Note:** The over-utilization constraints are the **key linking constraints** between assignment variables and supply variables (production, transport, purchase). They ensure pegging is consistent with actual supply.
### 6.7 Storage Violation Variables
| Variable | Domain | KPI | Definition |
| ------------------- | ----------------------- | ------------- | ----------------- |
| \(StoreOver_{s,t}\) | \(\mathbb{R}^{\geq 0}\) | StorageExcess | Capacity exceeded |
**Storage Constraint:**
$$\sum_{p \in Products_s} \nu_p \cdot Inv_{p,s,t} \leq StorageCap_{s,t} + StoreOver_{s,t}$$
**Volume/Weight Conversion:**
$$\nu_p = \text{space per unit of product } p$$
### 6.8 Preference / Quality Variables
| Variable | Domain | KPI | Definition |
| ------------- | ----------------------- | --------------------- | -------------------- |
| \(PrefPen_d\) | \(\mathbb{R}^{\geq 0}\) | SupplyPreferenceScore | Non-preferred source |
| \(QualPen_d\) | \(\mathbb{R}^{\geq 0}\) | SupplyQualityScore | Below quality target |
**Preference Penalty:**
$$PrefPen_d = \sum_{src} Pref_{src} \cdot \alpha_{d,src}$$
where \(Pref_{src}\) is the preference score of source \(src\) (lower = better).
**Quality Penalty:**
$$QualPen_d = \max\left(0, QualTarget_d - \sum_{src} Quality_{src} \cdot \frac{\alpha_{d,src}}{Qty_d}\right)$$
Linearized:
$$QualPen_d \geq QualTarget_d - \sum_{src} Quality_{src} \cdot \frac{\alpha_{d,src}}{Qty_d}$$
### 6.9 Churn / Plan Stability Variables
| Variable | Domain | KPI | Definition |
| ------------------------- | ----------------------- | --------- | -------------------- |
| \(Churn^{prod}_{r,k,t}\) | \(\mathbb{R}^{\geq 0}\) | PlanChurn | Production deviation |
| \(Churn^{trans}_{m,p,t}\) | \(\mathbb{R}^{\geq 0}\) | PlanChurn | Transport deviation |
**Absolute Deviation Linearization:**
For \(|x - b|\):
$$Churn^{prod}_{r,k,t} \geq x^{prod}_{r,k,t} - Base^{prod}_{r,k,t}$$
$$Churn^{prod}_{r,k,t} \geq Base^{prod}_{r,k,t} - x^{prod}_{r,k,t}$$
**Total Churn:**
$$TotalChurn = \sum_{r,k,t} Churn^{prod}_{r,k,t} + \sum_{m,p,t} Churn^{trans}_{m,p,t}$$
### 6.10 Reliability / Risk Variables
| Variable | Domain | KPI | Definition |
| ---------- | ----------------------- | --------- | ---------------- |
| \(Risk_d\) | \(\mathbb{R}^{\geq 0}\) | RiskScore | Reliability risk |
**Risk Calculation:**
$$Risk_d = \sum_{src} (1 - Rel_{src}) \cdot \alpha_{d,src}$$
where \(Rel_{src} \in [0,1]\) is the reliability of source \(src\).
### 6.11 Setup / Changeover Variables
| Variable | Domain | KPI | Definition |
| ------------------- | ----------------------- | --------- | ---------------- |
| \(SetupTime_{l,t}\) | \(\mathbb{R}^{\geq 0}\) | SetupTime | Total setup time |
| \(SetupCost_{l,t}\) | \(\mathbb{R}^{\geq 0}\) | SetupCost | Total setup cost |
**Setup Time Calculation:**
$$SetupTime_{l,t} = \sum_{i,j \in P} \sigma_{i,j,l} \cdot \phi^{change}_{i,j,l,t}$$
**Setup Cost Calculation:**
$$SetupCost_{l,t} = \sum_{i,j \in P} Cost^{setup}_{i,j,l} \cdot \phi^{change}_{i,j,l,t}$$
### 6.12 Variable Summary Table
| Category | Variables | Objective Weight | Typical Penalty |
| -------------- | ------------------------------- | ----------------------- | -------------------- |
| **Delivery** | \(Back_d, Short_d\) | \(w^{late}, w^{short}\) | High (100-1000) |
| **Inventory** | \(InvBelow, InvAbove\) | \(w^{target}\) | Medium (10-100) |
| **Capacity** | \(OverUtil_{g,t}\) | \(w^{overutil}\) | Very High (500-5000) |
| **Storage** | \(StoreOver_{s,t}\) | \(w^{storage}\) | High (100-1000) |
| **Preference** | \(PrefPen_d\) | \(w^{pref}\) | Low (1-50) |
| **Churn** | \(Churn^{prod}, Churn^{trans}\) | \(w^{churn}\) | Medium (10-100) |
**F# Complete Slack Variables:**
```fsharp
type SlackVariables = {
// Delivery
Lateness: Map<DemandId, VarRef>
Earliness: Map<DemandId, VarRef>
Shortfall: Map<DemandId, VarRef>
// Inventory
BelowTarget: Map<PISP * Bucket, VarRef>
AboveTarget: Map<PISP * Bucket, VarRef>
SafetyViolation: Map<PISP * Bucket, VarRef>
// Capacity
Overutilization: Map<ResourceGroupId * Bucket, VarRef>
// Storage
StorageExcess: Map<StockingPointId * Bucket, VarRef>
// Preference & Quality
PreferencePenalty: Map<DemandId, VarRef>
QualityPenalty: Map<DemandId, VarRef>
// Churn
ProductionChurn: Map<RoutingId * StepIndex * Bucket, VarRef>
TransportChurn: Map<TransportLegId * ProductId * Bucket, VarRef>
}
```
---
## 7. Constraints (Core Feasibility)
> Constraints enforce **physics** of the supply chain. Business rules (eligibility, policy) should be encoded in **sets and parameters**, not constraint logic.
**Constraint Classification:**
- **Hard Constraints**: Must be satisfied for feasibility (physics, balance equations)
- **Soft Constraints**: Can be violated with penalty (targets, preferences)
**Constraint Count Estimation:**
| Constraint Family | Count Formula | Example |
| ----------------- | --------------------------------------------------------------- | --------- |
| Demand Balance | \(\lvert D \rvert\) | 50,000 |
| Inventory Balance | \(\lvert PISP \rvert \cdot \lvert T \rvert\) | 450,000 |
| Capacity | \(\lvert G \rvert \cdot \lvert T \rvert\) | 1,800 |
| Transport | \(\lvert M \rvert \cdot \lvert P \rvert \cdot \lvert T \rvert\) | 4,500,000 |
### 7.1 Demand Satisfaction & Timing
#### 7.1.1 Demand Balance (Hard)
$$\sum_{t \in T} y_{d,t} + Short_d = Qty_d \quad \forall d \in D$$
**Interpretation:** Total fulfillment plus shortfall equals demand quantity.
**F# Constraint Generation:**
```fsharp
let addDemandBalance (solver: ISolver) (demands: DemandInput list) (yVars: Map<DemandId*Bucket,VarRef>) (shortVars: Map<DemandId,VarRef>) =
demands |> List.iter (fun d ->
let lhs =
[0 .. horizon - 1]
|> List.map (fun t -> Var yVars.[(d.Id, t)])
|> List.fold Add (Const 0m)
|> fun sum -> Add(sum, Var shortVars.[d.Id])
solver.AddConstraint(sprintf "DemandBal[%s]" d.Id, lhs, Equal, d.Quantity)
)
```
#### 7.1.2 Lateness Calculation (Soft)
$$Back_d \geq \sum_{t > \tau^{due}_d} (t - \tau^{due}_d) \cdot y_{d,t} \quad \forall d \in D$$
**Interpretation:** Weighted lateness = (buckets past due) × (quantity delivered late).
**Linearization:** This is already linear since \((t - \tau^{due}_d)\) is a constant coefficient.
**Alternative (Quantity-Based):**
$$Back^{qty}_d \geq \sum_{t > \tau^{due}_d} y_{d,t}$$
#### 7.1.3 Earliness Calculation (Soft)
$$Early_d \geq \sum_{t < \tau^{due}_d} (\tau^{due}_d - t) \cdot y_{d,t} \quad \forall d \in D$$
#### 7.1.4 Full-Order Enforcement (Binary Linking)
For demands with \(\delta^{full}_d = 1\):
$$Short_d \leq Qty_d \cdot (1 - z^{accept}_d) \quad \forall d : \delta^{full}_d = 1$$
$$\sum_t y_{d,t} \leq Qty_d \cdot z^{accept}_d \quad \forall d : \delta^{full}_d = 1$$
where \(z^{accept}_d \in \{0,1\}\).
**Interpretation:** If accepted (\(z^{accept}=1\)), must fulfill fully (\(Short=0\)). If rejected (\(z^{accept}=0\)), cannot fulfill anything.
#### 7.1.5 Full-Delivery Enforcement (Order-Level)
For orders \(O\) with multiple demand lines \(D_O\):
$$y_{d_1,t} > 0 \implies y_{d_2,t} > 0 \quad \forall d_1, d_2 \in D_O, t \in T$$
**Linearized:**
$$y_{d_1,t} \leq Qty_{d_1} \cdot z^{deliver}_{O,t}$$
$$y_{d_2,t} \leq Qty_{d_2} \cdot z^{deliver}_{O,t}$$
$$\sum_t z^{deliver}_{O,t} = 1 \quad \text{(all lines in same bucket)}$$
### 7.2 Material Flow Conservation (Inventory Balance)
#### 7.2.1 Inventory Balance Equation (Hard)
$$Inv_{p,s,t} = Inv_{p,s,t-1} + \text{Supply}_{p,s,t} - \text{Consumption}_{p,s,t} \quad \forall (p,s) \in PISP, t \in T$$
**Detailed Expansion:**
$$\begin{aligned}
Inv_{p,s,t} = & \; Inv_{p,s,t-1} \\
& + Inb^{firm}_{p,s,t} & \text{(firm inbound)} \\
& + \sum_{r \in R_{p,s}} x^{prod}_{r,K_r,t} & \text{(production completion)} \\
& + \sum_{m : Dest_m = s} x^{trans}_{m,p,t-LT^{trans}_m} & \text{(transport arrival)} \\
& + \sum_{v : (v,p,s) \in Offers} x^{sup}_{v,p,s,t} & \text{(supplier receipt)} \\
& - \sum_{r,k : c=p \in Inputs_{r,k}} \beta_{p,r,k} \cdot x^{prod}_{r,k,t+LT_{r,k}} & \text{(BOM consumption)} \\
& - \sum_{m : Origin_m = s} x^{trans}_{m,p,t} & \text{(transport departure)} \\
& - \sum_{d : p_d = p, s_d = s} y_{d,t} & \text{(demand fulfillment)}
\end{aligned}$$
**Initial Condition:**
$$Inv_{p,s,-1} = Inv^{init}_{p,s}$$
**F# Constraint Generation:**
```fsharp
let addInventoryBalance (solver: ISolver) (pisp: PISP list) (horizon: int) =
pisp |> List.iter (fun (p, s) ->
[0 .. horizon - 1] |> List.iter (fun t ->
let prevInv = if t = 0 then Const params.Initial.[(p,s)] else Var invVars.[(p,s,t-1)]
let supply = computeSupply p s t
let consumption = computeConsumption p s t
let balance = Add(Add(prevInv, supply), Mul(-1m, consumption))
solver.AddConstraint(sprintf "InvBal[%s,%s,%d]" p s t, Sub(Var invVars.[(p,s,t)], balance), Equal, 0m)
)
)
```
#### 7.2.2 Non-Negativity (Hard)
$$Inv_{p,s,t} \geq 0 \quad \forall (p,s) \in PISP, t \in T$$
> **Note:** This is typically enforced via variable bounds, not explicit constraints.
#### 7.2.3 Safety Stock (Soft)
$$Inv_{p,s,t} + SafetyViol_{p,s,t} \geq Safety_{p,s} \quad \forall (p,s) \in PISP, t \in T$$
**Interpretation:** Safety violation slack captures amount below safety stock.
#### 7.2.4 Inventory Targets (Soft)
$$Inv_{p,s,t} + InvBelow_{p,s,t} \geq InvTarget_{p,s} \quad \text{(below target)}$$
$$Inv_{p,s,t} - InvAbove_{p,s,t} \leq InvMax_{p,s} \quad \text{(above max)}$$
### 7.3 BOM Consumption / Multi-Level Material Requirements
#### 7.3.1 Component Availability (Hard)
For each production step consuming components:
$$\sum_{r,k : c \in Inputs_{r,k}} \beta_{c,r,k} \cdot x^{prod}_{r,k,t} \leq Inv_{c,s,t-1} + MissUp_{c,s,t}$$
where \(MissUp_{c,s,t}\) is a slack for missing material (penalized).
**Interpretation:** Cannot consume more component than available (soft with penalty).
#### 7.3.2 Yield-Adjusted Consumption
If step \(k\) outputs quantity \(x\) with yield \(\gamma_{r,k}\):
$$\text{Input required} = \frac{x}{\gamma_{r,k}}$$
$$\text{Component consumption} = \beta_{c,r,k} \cdot \frac{x^{prod}_{r,k,t}}{\gamma_{r,k}}$$
#### 7.3.3 Multi-Level BOM Handling
For multi-level BOMs, components are themselves products:
$$c \in C \subseteq P$$
The inventory balance for \(c\) includes consumption as a component and (potentially) production as a finished good.
### 7.4 Production Precedence and Lead Times
#### 7.4.1 Step Precedence (Hard)
Step \(k\) cannot complete until step \(k-1\) has completed:
$$x^{prod}_{r,k,t} \leq \sum_{t' \leq t - LT_{r,k}} x^{prod}_{r,k-1,t'} \cdot \gamma_{r,k-1} \quad \forall r, k > 1, t$$
**Interpretation:** Output of step \(k\) is bounded by cumulative output of step \(k-1\) (adjusted for yield and lead time).
**Simplified (equality):**
$$x^{prod}_{r,k,t} = x^{prod}_{r,k-1,t-LT_{r,k}} \cdot \gamma_{r,k-1} \quad \text{(if steps are tightly coupled)}$$
#### 7.4.2 Routing Validity (Hard)
$$x^{prod}_{r,k,t} \leq M^{prod}_{r,t} \cdot \delta^{routing}_{r,t} \quad \forall r \in R, k \in K_r, t \in T$$
**Interpretation:** Production only when routing is valid/effective.
#### 7.4.3 Routing Selection (Optional)
If demands require exclusive routing assignment:
$$\sum_{r \in R_{p_d,s_d}} z^{route}_{d,r} = 1 \quad \forall d \in D$$
$$y_{d,t} \leq Qty_d \cdot z^{route}_{d,r} + M \cdot (1 - z^{route}_{d,r}) \quad \forall d, r, t$$
#### 7.4.4 Characteristic Compatibility (CBP)
$$z^{route}_{d,r} \leq \delta^{compat}_{d,r} \quad \forall d \in D, r \in R_{p_d,s_d}$$
**Interpretation:** Cannot select routing that doesn't produce required characteristics.
### 7.5 Capacity Constraints (Finite + Locks)
#### 7.5.1 Resource Group Capacity (Finite Mode)
$$\sum_{r,k : ResGroup_{r,k} = g} \rho_{r,k} \cdot x^{prod}_{r,k,t} \leq Cap^{eff}_{g,t} + OverUtil_{g,t} \quad \forall g \in G, t \in T$$
where:
$$Cap^{eff}_{g,t} = Cap_{g,t} - Alloc^{firm}_{g,t} - Resv^{cap}_{g,t} - CapBuffer_{g,t}$$
**Interpretation:** Total capacity consumption ≤ effective capacity + overload slack.
**F# Constraint Generation:**
```fsharp
let addCapacityConstraints (solver: ISolver) (groups: ResourceGroupId list) (horizon: int) =
groups |> List.iter (fun g ->
[0 .. horizon - 1] |> List.iter (fun t ->
let usage =
routingSteps
|> List.filter (fun rs -> rs.ResourceGroup = g)
|> List.map (fun rs -> Mul(rs.ProcessingRate, Var prodVars.[(rs.Routing, rs.Step, t)]))
|> List.fold Add (Const 0m)
let effCap = effectiveCapacity g t
let constraint = Sub(usage, Add(Const effCap, Var overUtilVars.[(g,t)]))
solver.AddConstraint(sprintf "Cap[%s,%d]" g t, constraint, LessOrEqual, 0m)
)
)
```
#### 7.5.2 Infinite Capacity Mode
When \(\delta^{finite} = 0\):
$$OverUtil_{g,t} \geq 0 \quad \text{(uncapped, tracked but not penalized heavily)}$$
#### 7.5.3 Locked Operations (Hard)
$$x^{prod}_{r,k,t} = Locked^{prod}_{r,k,t} \quad \forall (r,k,t) \in LockedOps$$
**Interpretation:** Firm/frozen operations cannot be changed.
#### 7.5.4 Alternative Resource Groups
If step \(k\) can use any of alternative groups \(AltRes_{r,k}\):
$$\sum_{g \in AltRes_{r,k}} x^{prod,g}_{r,k,t} = x^{prod}_{r,k,t} \quad \text{(total equals step production)}$$
$$x^{prod,g}_{r,k,t} \leq M \cdot z^{res}_{r,k,g,t} \quad \text{(link to binary)}$$
$$\sum_{g \in AltRes_{r,k}} z^{res}_{r,k,g,t} = 1 \quad \text{(choose one group)}$$
### 7.6 Storage / Warehouse Capacity
#### 7.6.1 Total Storage Capacity (Soft)
$$\sum_{p \in Products_s} \nu_p \cdot Inv_{p,s,t} \leq StorageCap_{s,t} + StoreOver_{s,t} \quad \forall s \in S, t \in T$$
where \(\nu_p\) = space per unit of product \(p\).
#### 7.6.2 Product-Specific Storage Limits
$$Inv_{p,s,t} \leq StorageCapProd_{p,s,t} \quad \forall (p,s) \in PISP, t \in T$$
### 7.7 Campaign / Changeover Constraints
#### 7.7.1 Single Product Per Line Per Bucket
$$\sum_{p \in P} \phi_{p,l,t} \leq 1 \quad \forall l \in L, t \in T$$
where \(\phi_{p,l,t} \in \{0,1\}\) indicates product \(p\) runs on line \(l\) in bucket \(t\).
#### 7.7.2 Production-Activity Linking
$$run_{p,l,t} \leq Cap_{l,t} \cdot \phi_{p,l,t} \quad \text{(production only if active)}$$
$$run_{p,l,t} \geq \epsilon \cdot \phi_{p,l,t} \quad \text{(if active, produce at least } \epsilon \text{)}$$
#### 7.7.3 Changeover Detection
$$\sigma_{i,j,l,t} \geq \phi_{i,l,t-1} + \phi_{j,l,t} - 1 \quad \forall i \neq j \in P, l \in L, t \in T$$
**Interpretation:** If product \(i\) ran in \(t-1\) and \(j\) runs in \(t\), then \(\sigma_{i,j,l,t} = 1\).
#### 7.7.4 Setup Time Consumption
$$\sum_{p \in P} run_{p,l,t} + \sum_{i,j \in P} \sigma_{i,j,l} \cdot \sigma_{i,j,l,t} \leq Cap_{l,t} \quad \forall l \in L, t \in T$$
**Interpretation:** Production time + setup time ≤ available capacity.
#### 7.7.5 Minimum Run Quantity
$$run_{p,l,t} \geq MinRun_{p,l} \cdot \phi_{p,l,t} \quad \forall p \in P, l \in L, t \in T$$
#### 7.7.6 Campaign Length Bounds
**Minimum Campaign Length:**
$$\sum_{\tau=t}^{t+MinCamp_{p,l}-1} \phi_{p,l,\tau} \geq MinCamp_{p,l} \cdot (\phi_{p,l,t} - \phi_{p,l,t-1})$$
**Interpretation:** If campaign starts at \(t\), it must run for at least \(MinCamp\) buckets.
**Maximum Campaign Length:**
$$\sum_{\tau=t-MaxCamp_{p,l}+1}^{t} \phi_{p,l,\tau} \leq MaxCamp_{p,l} \quad \forall p, l, t$$
#### 7.7.7 Family-Level Constraints
$$\phi_{p,l,t} \leq \phi^{fam}_{f,l,t} \quad \forall p \in FamilyProducts_f, l \in L, t \in T$$
$$\phi^{fam}_{f,l,t} \leq \sum_{p \in FamilyProducts_f} \phi_{p,l,t}$$
#### 7.7.8 Cleaning (CIP) Windows
$$\phi_{p,l,t} \leq \delta^{CIP}_{l,t} \quad \forall p \in P, l \in L, t \in T$$
**Interpretation:** No production during cleaning windows.
#### 7.7.9 Cleaning Between Families
If \(\delta^{clean}_{f_1,f_2,l} = 1\):
$$\phi^{fam}_{f_1,l,t} + \phi^{fam}_{f_2,l,t+1} \leq 1 + hasCleaning_{l,t} \quad \forall t$$
where \(hasCleaning_{l,t} = 1\) indicates a cleaning window exists between \(t\) and \(t+1\).
### 7.8 Transport Feasibility Constraints
#### 7.8.1 Leg Capacity (Hard)
$$\sum_{p \in P} x^{trans}_{m,p,t} \leq Cap^{trans}_{m,t} \quad \forall m \in M, t \in T$$
#### 7.8.2 Cutoff Feasibility (Hard)
$$x^{trans}_{m,p,t} \leq Cap^{trans}_{m,t} \cdot \delta^{cutoff}_{m,t} \quad \forall m \in M, p \in P, t \in T$$
**Interpretation:** Can only ship when there's a departure (cutoff).
#### 7.8.3 Network Flow Conservation
**At Origin:**
$$\sum_{m \in LegsFrom_s} x^{trans}_{m,p,t} \leq Inv_{p,s,t-1} \quad \text{(can't ship more than available)}$$
**At Destination:**
$$\text{Arrival}_{p,s,t} = \sum_{m \in LegsTo_s} x^{trans}_{m,p,t-LT^{trans}_m}$$
#### 7.8.4 Itinerary Selection (Multi-Hop)
If using itineraries \(H_{s,s'}\):
$$\sum_{h \in H_{s_d}} z^{itin}_{d,h} \leq 1 \quad \forall d \in D$$
$$y_{d,t} \leq Qty_d \cdot \sum_{h : ArrivalTime_h = t} z^{itin}_{d,h}$$
### 7.9 Supplier Constraints
#### 7.9.1 MOQ and Lot Sizing (Hard)
**Lot Sizing:**
$$x^{sup}_{v,p,s,t} = Lot_{v,p,s} \cdot n^{lot}_{v,p,s,t} \quad \forall (v,p,s) \in Offers, t \in T$$
where \(n^{lot} \in \mathbb{Z}^{\geq 0}\).
**MOQ:**
$$x^{sup}_{v,p,s,t} \geq MOQ_{v,p,s} \cdot z^{order}_{v,p,s,t} \quad \forall (v,p,s) \in Offers, t \in T$$
$$x^{sup}_{v,p,s,t} \leq Cap^{sup}_{v,p,s,t} \cdot z^{order}_{v,p,s,t}$$
where \(z^{order} \in \{0,1\}\).
#### 7.9.2 Supplier Capacity (Hard)
$$x^{sup}_{v,p,s,t} \leq Cap^{sup}_{v,p,s,t} \quad \forall (v,p,s) \in Offers, t \in T$$
#### 7.9.3 Lead Time Integration
Order placed in bucket \(t_{order}\) arrives in bucket:
$$t_{arrival} = t_{order} + LT^{sup}_{v,p,s}$$
The variable \(x^{sup}_{v,p,s,t}\) represents **arrival**, so the order was placed in \(t - LT^{sup}\).
### 7.10 Shelf-Life / Freshness Constraints (CPG)
#### 7.10.1 Age-Tracking Inventory Balance
$$Inv^{age}_{p,s,t,a} = \begin{cases}
\sum_{r \in R_{p,s}} x^{prod}_{r,K_r,t} & \text{if } a = 0 \\
Inv^{age}_{p,s,t-1,a-1} - ship^{age}_{p,s,t,a} - use^{age}_{p,s,t,a} & \text{if } 0 < a \leq SL_p \\
0 & \text{if } a > SL_p
\end{cases}$$
**Initial Condition:**
$$Inv^{age}_{p,s,0,a} = Inv^{init,age}_{p,s,a}$$
#### 7.10.2 Total Inventory Consistency
$$Inv_{p,s,t} = \sum_{a=0}^{SL_p} Inv^{age}_{p,s,t,a}$$
#### 7.10.3 FEFO Policy (First Expired, First Out)
$$ship^{age}_{d,t,a+1} \leq M \cdot \epsilon^{age}_{d,t,a} \quad \text{(only ship age } a+1 \text{ if age } a \text{ exhausted)}$$
$$ship^{age}_{d,t,a} + \epsilon^{age}_{d,t,a} \cdot M \geq Inv^{age}_{p_d,s_d,t,a}$$
#### 7.10.4 Minimum Life at Shipment
$$ship^{age}_{d,t,a} = 0 \quad \text{if } SL_{p_d} - a - LT^{trans} < MinLife^{ship}_{p_d}$$
**Interpretation:** Cannot ship inventory that won't have enough remaining life upon arrival.
### 7.11 Production Leveling (Smoothing)
#### 7.11.1 Spread Constraint
Let \(\bar{x}_{g}\) be the average production across the horizon:
$$\bar{x}_g = \frac{1}{\lvert T \rvert} \sum_{t \in T} \sum_{r,k : ResGroup_{r,k} = g} x^{prod}_{r,k,t}$$
**Spread Deviation:**
$$Spread_{g,t} \geq \sum_{r,k:g} x^{prod}_{r,k,t} - \bar{x}_g$$
$$Spread_{g,t} \geq \bar{x}_g - \sum_{r,k:g} x^{prod}_{r,k,t}$$
**Interpretation:** Penalize deviation from average production level.
### 7.12 Pegging / Assignment Constraints
> Pegging constraints link demand fulfillment to specific supply sources, enabling traceability and proper source capacity enforcement.
#### 7.12.1 Assignment Balance (Hard)
Total assigned to demand must equal total fulfilled:
$$\sum_{t} \alpha^{INV}_{d,t} + \sum_{j} \alpha^{FIRM}_{d,j} + \sum_{r,t} \alpha^{PROD}_{d,r,t} + \sum_{m,t} \alpha^{TRANS}_{d,m,t} + \sum_{v,t} \alpha^{SUP}_{d,v,t} = \sum_{t} y_{d,t} \quad \forall d \in D$$
**F# Constraint Generation:**
```fsharp
let addAssignmentBalance (solver: ISolver) (d: DemandId) =
let totalAssigned =
getAssignmentVarsForDemand d
|> Seq.map Var
|> Seq.fold Add (Const 0m)
let totalFulfilled =
[0 .. horizon - 1]
|> List.map (fun t -> Var fulfillmentVars.[(d, t)])
|> List.fold Add (Const 0m)
solver.AddConstraint($"AssignBal[{d}]", Sub(totalAssigned, totalFulfilled), Equal, 0m)
```
#### 7.12.2 Inventory Source Availability (Soft)
Cumulative assignment from inventory cannot exceed cumulative available:
$$\sum_{d : (p_d,s_d) = (p,s)} \sum_{t' \leq t} \alpha^{INV}_{d,t'} \leq Inv^{init}_{p,s} - Resv^{mat}_{p,s,0} + OverUtil^{INV}_{p,s,t} \quad \forall (p,s) \in PISP, t \in T$$
**Interpretation:** Cannot assign more from inventory than what's initially available (minus reservations).
#### 7.12.3 Firm Inbound Source Availability (Hard)
$$\sum_{d : (p_d,s_d) = (p_j,s_j)} \alpha^{FIRM}_{d,j} \leq Qty_j \quad \forall j \in FIRM$$
**Interpretation:** Total assigned from a firm order ≤ the firm order quantity.
#### 7.12.4 Production Source Availability (Soft)
**Key constraint linking assignment to production variables:**
$$\sum_{d : (p_d,s_d) = (Output_r, OutputSP_r)} \alpha^{PROD}_{d,r,t} \leq x^{prod}_{r,K_r,t} + OverUtil^{PROD}_{r,t} \quad \forall r \in R, t \in T$$
**Interpretation:** Total assigned from a production run ≤ production quantity.
**F# Constraint Generation:**
```fsharp
let addProductionSourceConstraint (solver: ISolver) (r: RoutingId) (t: Bucket) =
let p, s = getRoutingOutput r
// Sum of all assignments from this production
let totalAssigned =
demands
|> List.filter (fun d -> d.Product = p && d.StockingPoint = s)
|> List.map (fun d ->
match assignVars.TryFind (d.Id, PlannedProduction(r, t)) with
| Some v -> Var v
| None -> Const 0m)
|> List.fold Add (Const 0m)
// Production variable for final step
let prodVar = Var productionVars.[(r, finalStep r, t)]
let overUtilVar = Var overUtilProdVars.[(r, t)]
// totalAssigned <= production + overUtil
let lhs = Sub(totalAssigned, Add(prodVar, overUtilVar))
solver.AddConstraint($"ProdSrcAvail[{r},{t}]", lhs, LessOrEqual, 0m)
```
#### 7.12.5 Transport Arrival Source Availability (Soft)
$$\sum_{d : s_d = Dest_m, p_d = p} \alpha^{TRANS}_{d,m,t} \leq x^{trans}_{m,p,t-LT^{trans}_m} + OverUtil^{TRANS}_{m,p,t} \quad \forall m \in M, p \in P, t \in T$$
**Interpretation:** Total assigned from transport arrival ≤ transported quantity.
**Note:** \(t\) is the arrival bucket; transport departed at \(t - LT^{trans}_m\).
#### 7.12.6 Supplier Receipt Source Availability (Soft)
$$\sum_{d : (p_d,s_d) = (p,s)} \alpha^{SUP}_{d,v,t} \leq x^{sup}_{v,p,s,t} + OverUtil^{SUP}_{v,p,s,t} \quad \forall (v,p,s) \in Offers, t \in T$$
**Interpretation:** Total assigned from supplier receipt ≤ purchased quantity.
#### 7.12.7 Timing Feasibility (Hard)
Assignment only valid if supply arrives before or at fulfillment time:
**For Inventory (always available from t=0):**
$$\alpha^{INV}_{d,t} \leq Qty_d \cdot \mathbb{1}[t \geq 0] \quad \text{(always feasible)}$$
**For Firm Inbound:**
$$\alpha^{FIRM}_{d,j} \leq Qty_d \cdot \mathbb{1}[Arrival_j \leq \tau^{fulfill}_d]$$
**For Production:**
$$\alpha^{PROD}_{d,r,t} \leq Qty_d \cdot \mathbb{1}[t \leq \tau^{fulfill}_d]$$
**For Transport:**
$$\alpha^{TRANS}_{d,m,t} \leq Qty_d \cdot \mathbb{1}[t \leq \tau^{fulfill}_d]$$
**For Supplier:**
$$\alpha^{SUP}_{d,v,t} \leq Qty_d \cdot \mathbb{1}[t \leq \tau^{fulfill}_d]$$
> **Implementation Note:** Instead of adding these as constraints, simply don't create assignment variables for infeasible (demand, source) combinations.
#### 7.12.8 Fulfillment Timing Consistency
Link assignment timing to fulfillment timing:
$$y_{d,t} \leq \sum_{src : AvailTime_{src} \leq t} \alpha_{d,src} \quad \forall d \in D, t \in T$$
**Interpretation:** Fulfillment in bucket \(t\) can only use supply available by bucket \(t\).
#### 7.12.9 Firm Peg Protection (Optional)
For demands with existing firm pegging links:
$$\alpha_{d,src} \geq FirmPegQty_{d,src} \quad \forall (d, src) \in FirmPegs$$
**Interpretation:** Honor existing confirmed pegging links.
#### 7.12.10 Complete F# Constraint Generator
```fsharp
let addAllPeggingConstraints (solver: ISolver) (input: OptimizerInput) (vars: Variables) =
// 7.12.1 Assignment balance for each demand
for d in input.Demands do
addAssignmentBalance solver d vars
// 7.12.2 Inventory availability (by PISP, cumulative)
for (p, s) in input.Inventory |> List.map (fun i -> i.Product, i.StockingPoint) do
for t in 0 .. input.Horizon - 1 do
addInventorySourceConstraint solver p s t vars
// 7.12.3 Firm inbound availability
for firm in input.FirmInbounds do
addFirmSourceConstraint solver firm vars
// 7.12.4 Production availability (links to production variables)
for r in input.Routings do
for t in 0 .. input.Horizon - 1 do
if isRoutingValidAt r t then
addProductionSourceConstraint solver r t vars
// 7.12.5 Transport arrival availability
for m in input.TransportLegs do
for p in getProductsForLeg m do
for t in 0 .. input.Horizon - 1 do
let departBucket = t - m.LeadTime
if departBucket >= 0 then
addTransportSourceConstraint solver m p t vars
// 7.12.6 Supplier receipt availability
for (v, p, s) in input.Suppliers |> List.collect getOffers do
for t in 0 .. input.Horizon - 1 do
if isSupplierAvailable v p s t then
addSupplierSourceConstraint solver v p s t vars
// 7.12.9 Firm peg protection
match input.FirmPegs with
| Some pegs ->
for peg in pegs do
addFirmPegConstraint solver peg vars
| None -> ()
```
### 7.13 Churn / Plan Stability Constraints
#### 7.13.1 Production Churn
$$Churn^{prod}_{r,k,t} \geq x^{prod}_{r,k,t} - Base^{prod}_{r,k,t}$$
$$Churn^{prod}_{r,k,t} \geq Base^{prod}_{r,k,t} - x^{prod}_{r,k,t}$$
**Interpretation:** Captures absolute deviation from baseline.
#### 7.13.2 Transport Churn
$$Churn^{trans}_{m,p,t} \geq x^{trans}_{m,p,t} - Base^{trans}_{m,p,t}$$
$$Churn^{trans}_{m,p,t} \geq Base^{trans}_{m,p,t} - x^{trans}_{m,p,t}$$
### 7.14 Hard Caps (Optional)
#### 7.14.1 Cost Cap
$$\sum \text{(all costs)} \leq Cap^{cost}$$
#### 7.14.2 CO2 Cap
$$\sum_{m,p,t} CO2^{trans}_m \cdot x^{trans}_{m,p,t} + \sum_{r,k,t} CO2^{prod}_r \cdot x^{prod}_{r,k,t} \leq Cap^{co2}$$
### 7.15 Constraint Summary
| Family | Count | Type | Primary Purpose |
| ----------------------- | --------------------------------------------------- | ---------- | ------------------------------ |
| **Demand Balance** | \(\lvert D \rvert\) | Equality | Fulfillment + Short = Quantity |
| **Inventory Balance** | \(\lvert PISP \rvert \cdot T\) | Equality | Flow conservation |
| **Capacity** | \(\lvert G \rvert \cdot T\) | Inequality | Resource limits |
| **Transport** | \(\lvert M \rvert \cdot T\) | Inequality | Capacity limits |
| **BOM** | \(\lvert R \rvert \cdot \bar{K} \cdot T\) | Inequality | Material availability |
| **Campaigns** | \(\lvert L \rvert \cdot T \cdot \lvert P \rvert^2\) | Mixed | Sequencing |
| **Shelf-Life** | \(\lvert PISP^{per} \rvert \cdot T \cdot \bar{A}\) | Mixed | Age tracking |
| **Pegging Balance** | \(\lvert D \rvert\) | Equality | Assignment = Fulfillment |
| **Source Availability** | \(\lvert Sources \rvert\) | Inequality | Don't over-assign |
### 7.16 Conceptual Clarification: Fulfillment vs. Assignment
> This section clarifies the relationship between **fulfillment** (\(y_{d,t}\)) and **assignment** (\(\alpha_{d,src}\)).
**Fulfillment (\(y_{d,t}\)):**
- Represents WHEN and HOW MUCH of demand \(d\) is delivered
- Triggers inventory consumption in inventory balance equation
- Determines lateness/earliness
**Assignment (\(\alpha_{d,src}\)):**
- Represents WHERE the supply comes from (pegging)
- Tracks traceability: which source fulfills which demand
- Enables preference/quality scoring by source
- Must sum to total fulfillment: \(\sum_{src} \alpha_{d,src} = \sum_t y_{d,t}\)
**Relationship:**
```
Demand d needs 100 units, due bucket 10
Fulfillment: y_{d,10} = 60, y_{d,12} = 40 (100 total, 40 late)
Assignment: α^{INV}_{d} = 30 (from on-hand inventory)
α^{PROD}_{d,r1,8} = 50 (from production r1 completing bucket 8)
α^{PROD}_{d,r2,11} = 20 (from production r2 completing bucket 11)
Sum = 100 ✓
The 40 late units come from α^{PROD}_{d,r2,11} which wasn't ready until bucket 11.
```
**Why Both Variables?**
- \(y_{d,t}\) drives timing (lateness) and inventory consumption
- \(\alpha_{d,src}\) drives pegging, source capacity, and preference scoring
- Together they provide complete visibility into fulfillment plan
**F# Constraint Builder Summary:**
```fsharp
let buildAllConstraints (solver: ISolver) (input: OptimizerInput) (vars: Variables) =
// === HARD CONSTRAINTS ===
// Demand
addDemandBalance solver input.Demands vars.Fulfillment vars.Shortfall
// Material
addInventoryBalance solver input.Inventory vars.Inventory
addBOMConsumption solver input.Routings vars.Production vars.Inventory
// Capacity
addCapacityConstraints solver input.Capacity vars.Production vars.Overload
// Transport
addTransportCapacity solver input.TransportLegs vars.Transport
// Pegging (KEY: links assignment to supply variables)
addAssignmentBalance solver input.Demands vars.Assignment vars.Fulfillment
addProductionSourceAvailability solver vars.Assignment vars.Production vars.OverUtilProd
addTransportSourceAvailability solver vars.Assignment vars.Transport vars.OverUtilTrans
addInventorySourceAvailability solver vars.Assignment vars.OverUtilInv
addFirmInboundAvailability solver input.FirmInbounds vars.Assignment vars.OverUtilFirm
// === SOFT CONSTRAINTS ===
addSafetyStock solver input.Inventory vars.Inventory vars.SafetyViolation
addInventoryTargets solver input.Inventory vars.Inventory vars.BelowTarget vars.AboveTarget
addStorageCapacity solver input.Storage vars.Inventory vars.StorageExcess
// === OPTIONAL MODULES ===
if input.Policy.IncludeCampaigns then
addCampaignConstraints solver input.Campaigns vars.Campaign
if input.Policy.IncludeShelfLife then
addShelfLifeConstraints solver input.ShelfLife vars.AgeInventory vars.AgeShipment
if input.Policy.IncludeSuppliers then
addSupplierConstraints solver input.Suppliers vars.Purchase
addSupplierSourceAvailability solver vars.Assignment vars.Purchase vars.OverUtilSup
// === CHURN ===
match input.Baseline with
| Some baseline -> addChurnConstraints solver baseline vars.Production vars.ProductionChurn
| None -> ()
```
---
## 8. Objective Function(s)
> Objective is a **weighted sum of KPIs**. Weights are parameters, not hard-coded.
**Objective Design Principles:**
1. **Linearity**: All terms must be linear in decision variables
2. **Normalization**: Weights scaled so terms have comparable magnitude
3. **Interpretability**: Each term maps to a business KPI
4. **Flexibility**: Weights are parameters, not constants
### 8.1 Complete Objective Function (Weighted Sum)
$$\min Z = Z^{delivery} + Z^{inventory} + Z^{capacity} + Z^{cost} + Z^{preference} + Z^{validation} + Z^{stability} + Z^{sustainability}$$
#### 8.1.1 Delivery Performance Component
$$Z^{delivery} = w^{late} \sum_{d \in D} w^{prio}_d \cdot Back_d + w^{early} \sum_{d \in D} Early_d + w^{short} \sum_{d \in D} w^{prio}_d \cdot Short_d$$
**Interpretation:**
- **Lateness**: Priority-weighted bucket-days late
- **Earliness**: Penalty for delivering too early (inventory at customer)
- **Shortfall**: Priority-weighted unfulfilled quantity
**Example Calculation:**
```
Demand d₁: Qty = 100, Due = bucket 10, Priority = 2.0
Delivered: 60 units in bucket 10, 30 units in bucket 12, 10 units short
Back_d₁ = (12 - 10) × 30 = 60
Short_d₁ = 10
Contribution = w^{late} × 2.0 × 60 + w^{short} × 2.0 × 10
```
#### 8.1.2 Inventory Component
$$Z^{inventory} = w^{target} \sum_{(p,s) \in PISP} \sum_{t \in T} (InvBelow_{p,s,t} + InvAbove_{p,s,t}) + w^{safety} \sum_{(p,s),t} SafetyViol_{p,s,t}$$
$$\quad + w^{hold} \sum_{(p,s),t} h^{FG}_{p,s} \cdot Inv_{p,s,t} + w^{WIP} \sum_{(p,s),t} h^{WIP}_{p,s} \cdot Inv^{WIP}_{p,s,t}$$
**Interpretation:**
- **Target Adherence**: Deviation from inventory targets
- **Safety Violation**: Breaking safety stock floor
- **Holding Cost**: Cost of carrying inventory
#### 8.1.3 Capacity Component
$$Z^{capacity} = w^{overutil} \sum_{g \in G} \sum_{t \in T} OverUtil_{g,t} + w^{spread} \sum_{g,t} Spread_{g,t}$$
$$\quad + w^{setup} \sum_{l \in L} \sum_{t \in T} SetupTime_{l,t}$$
**Interpretation:**
- **Overutilization**: Penalty for exceeding capacity
- **Spread**: Penalty for uneven production (leveling)
- **Setup**: Changeover time cost
#### 8.1.4 Cost Component
$$Z^{cost} = w^{prod} \sum_{r,k,t} Cost^{prod}_r \cdot x^{prod}_{r,k,t} + w^{trans} \sum_{m,p,t} Cost^{trans}_m \cdot x^{trans}_{m,p,t}$$
$$\quad + w^{purch} \sum_{v,p,s,t} Cost^{sup}_{v,p,s} \cdot x^{sup}_{v,p,s,t} + w^{setup} \sum_{i,j,l,t} Cost^{setup}_{i,j,l} \cdot \sigma_{i,j,l,t}$$
**Cost Breakdown:**
| Cost Type | Variable | Parameter | Unit |
| ---------- | --------------------- | ------------------------ | ------------ |
| Production | \(x^{prod}_{r,k,t}\) | \(Cost^{prod}_r\) | $/unit |
| Transport | \(x^{trans}_{m,p,t}\) | \(Cost^{trans}_m\) | $/unit |
| Purchase | \(x^{sup}_{v,p,s,t}\) | \(Cost^{sup}_{v,p,s}\) | $/unit |
| Setup | \(\sigma_{i,j,l,t}\) | \(Cost^{setup}_{i,j,l}\) | $/changeover |
#### 8.1.5 Preference and Quality Component
$$Z^{preference} = w^{pref} \sum_{d \in D} PrefPen_d + w^{quality} \sum_{d \in D} QualPen_d$$
**Preference Penalty Calculation:**
$$PrefPen_d = \sum_{r \in R_{p_d,s_d}} Pref^{routing}_r \cdot z^{route}_{d,r} + \sum_{src} Pref_{src} \cdot \frac{\alpha_{d,src}}{Qty_d}$$
#### 8.1.6 Validation Component (Supply Chain Integrity)
$$Z^{validation} = w^{missUp} \sum_{r,k,t} MissUp_{r,k,t} + w^{overAssign} \sum_{d} OverAssign_d$$
$$\quad + w^{overUtil} \left( \sum_{p,s,t} OverUtil^{INV}_{p,s,t} + \sum_j OverUtil^{FIRM}_j + \sum_{r,t} OverUtil^{PROD}_{r,t} + \sum_{m,p,t} OverUtil^{TRANS}_{m,p,t} + \sum_{v,p,s,t} OverUtil^{SUP}_{v,p,s,t} \right)$$
$$\quad + w^{partial} \sum_{d} PartPiece_d + w^{storage} \sum_{s,t} StoreOver_{s,t}$$
> **Note:** Source over-utilization penalties are **high** because they indicate the optimizer is assigning from supply that doesn't exist. This should be rare if constraints are properly formulated.
#### 8.1.7 Plan Stability Component (Churn)
$$Z^{stability} = w^{churn} \left( \sum_{r,k,t} Churn^{prod}_{r,k,t} + \sum_{m,p,t} Churn^{trans}_{m,p,t} \right)$$
#### 8.1.8 Sustainability Component
$$Z^{sustainability} = w^{co2} \left( \sum_{m,p,t} CO2^{trans}_m \cdot x^{trans}_{m,p,t} + \sum_{r,k,t} CO2^{prod}_r \cdot x^{prod}_{r,k,t} \right)$$
### 8.2 Complete Mathematical Objective
$$\begin{aligned}
\min Z = & \; \textbf{// Delivery Performance} \\
& \; w^{late} \sum_{d} w^{prio}_d \cdot Back_d + w^{early} \sum_{d} Early_d + w^{short} \sum_{d} w^{prio}_d \cdot Short_d \\
& + \textbf{// Inventory Adherence} \\
& \; w^{target} \sum_{p,s,t} (InvBelow_{p,s,t} + InvAbove_{p,s,t}) + w^{safety} \sum_{p,s,t} SafetyViol_{p,s,t} \\
& + \textbf{// Holding Costs} \\
& \; w^{hold} \sum_{p,s,t} h^{FG}_{p,s} \cdot Inv_{p,s,t} + w^{WIP} \sum_{p,s,t} h^{WIP}_{p,s} \cdot Inv^{WIP}_{p,s,t} \\
& + \textbf{// Capacity Utilization} \\
& \; w^{capOver} \sum_{g,t} CapOver_{g,t} + w^{spread} \sum_{g,t} Spread_{g,t} \\
& + \textbf{// Operations Costs} \\
& \; w^{prod} \sum_{r,k,t} Cost^{prod}_r \cdot x^{prod}_{r,k,t} + w^{trans} \sum_{m,p,t} Cost^{trans}_m \cdot x^{trans}_{m,p,t} \\
& + w^{purch} \sum_{v,p,s,t} Cost^{sup}_{v,p,s} \cdot x^{sup}_{v,p,s,t} + w^{setup} \sum_{i,j,l,t} Cost^{setup}_{i,j,l} \cdot \sigma_{i,j,l,t} \\
& + \textbf{// Supply Source Preference} \\
& \; w^{pref} \sum_{d} PrefPen_d + w^{quality} \sum_{d} QualPen_d \\
& + \textbf{// Pegging / Assignment Violations (High Penalty)} \\
& \; w^{srcOver} \Big( \sum_{p,s,t} OverUtil^{INV}_{p,s,t} + \sum_j OverUtil^{FIRM}_j \\
& \qquad\qquad + \sum_{r,t} OverUtil^{PROD}_{r,t} + \sum_{m,p,t} OverUtil^{TRANS}_{m,p,t} + \sum_{v,p,s,t} OverUtil^{SUP}_{v,p,s,t} \Big) \\
& + w^{overAssign} \sum_{d} OverAssign_d \\
& + \textbf{// Integrity Violations} \\
& \; w^{missUp} \sum_{r,k,t} MissUp_{r,k,t} + w^{storage} \sum_{s,t} StoreOver_{s,t} \\
& + \textbf{// Plan Stability} \\
& \; w^{churn} \sum_{r,k,t} Churn^{prod}_{r,k,t} + w^{churn} \sum_{m,p,t} Churn^{trans}_{m,p,t} \\
& + \textbf{// Sustainability} \\
& \; w^{co2} \Big( \sum_{m,p,t} CO2^{trans}_m \cdot x^{trans}_{m,p,t} + \sum_{r,k,t} CO2^{prod}_r \cdot x^{prod}_{r,k,t} \Big)
\end{aligned}$$
> **Weight Guidelines:**
> - \(w^{srcOver}\): Very high (e.g., 10000) — assigning from non-existent supply should be avoided
> - \(w^{short}\): High (e.g., 1000) — shortfall is serious
> - \(w^{late}\): Medium-high (e.g., 100) — lateness is important but sometimes unavoidable
> - \(w^{hold}\): Low (e.g., 1) — holding cost is a regular business cost
### 8.3 F# Objective Builder
```fsharp
let buildObjective (solver: ISolver) (input: OptimizerInput) (vars: Variables) (slacks: SlackVariables) =
let weights = input.Policy
let mutable objective = Const 0m
// === DELIVERY PERFORMANCE ===
// Lateness (bucket-weighted)
for d in input.Demands do
let backVar = slacks.Lateness.[d.Id]
let coef = weights.WeightLateness * d.Priority
objective <- Add(objective, Mul(coef, Var backVar))
// Shortfall
for d in input.Demands do
let shortVar = slacks.Shortfall.[d.Id]
let coef = weights.WeightFulfillment * d.Priority
objective <- Add(objective, Mul(coef, Var shortVar))
// === INVENTORY ===
// Holding cost
for (p, s) in input.Inventory |> List.map (fun i -> i.Product, i.StockingPoint) do
for t in 0 .. input.Horizon - 1 do
let invVar = vars.Inventory.[(p, s, t)]
let holdCost = getHoldingCost p s
objective <- Add(objective, Mul(weights.WeightHolding * holdCost, Var invVar))
// Safety stock violation
for (p, s, t), safetyVar in slacks.SafetyViolation |> Map.toSeq do
objective <- Add(objective, Mul(weights.WeightSafety, Var safetyVar))
// === CAPACITY ===
// Resource capacity overload
for (g, t), overVar in slacks.CapacityOverload |> Map.toSeq do
objective <- Add(objective, Mul(weights.WeightCapacityOverload, Var overVar))
// === OPERATIONS COSTS ===
// Production cost
for r in input.Routings do
for step in r.Steps do
for t in 0 .. input.Horizon - 1 do
if vars.Production.ContainsKey (r.Id, step.StepIndex, t) then
let prodVar = vars.Production.[(r.Id, step.StepIndex, t)]
objective <- Add(objective, Mul(weights.WeightCost * r.ProductionCost, Var prodVar))
// Transport cost + CO2
for leg in input.TransportLegs do
for p in getProductsForLeg leg do
for t in 0 .. input.Horizon - 1 do
if vars.Transport.ContainsKey (leg.Id, p, t) then
let transVar = vars.Transport.[(leg.Id, p, t)]
let costCoef = weights.WeightCost * leg.CostPerUnit + weights.WeightCO2 * leg.CO2PerUnit
objective <- Add(objective, Mul(costCoef, Var transVar))
// === PEGGING / SOURCE OVER-UTILIZATION (HIGH PENALTY) ===
// Production source over-utilization
for (r, t), overUtilVar in slacks.OverUtilProd |> Map.toSeq do
objective <- Add(objective, Mul(weights.WeightSourceOverUtil, Var overUtilVar))
// Transport source over-utilization
for (m, p, t), overUtilVar in slacks.OverUtilTrans |> Map.toSeq do
objective <- Add(objective, Mul(weights.WeightSourceOverUtil, Var overUtilVar))
// Inventory source over-utilization
for (p, s, t), overUtilVar in slacks.OverUtilInv |> Map.toSeq do
objective <- Add(objective, Mul(weights.WeightSourceOverUtil, Var overUtilVar))
// Firm inbound source over-utilization
for j, overUtilVar in slacks.OverUtilFirm |> Map.toSeq do
objective <- Add(objective, Mul(weights.WeightSourceOverUtil, Var overUtilVar))
// Supplier source over-utilization
for (v, p, s, t), overUtilVar in slacks.OverUtilSup |> Map.toSeq do
objective <- Add(objective, Mul(weights.WeightSourceOverUtil, Var overUtilVar))
// Over-assignment to demand
for d, overAssignVar in slacks.OverAssign |> Map.toSeq do
objective <- Add(objective, Mul(weights.WeightOverAssign, Var overAssignVar))
// === PREFERENCE / QUALITY ===
for d in input.Demands do
if slacks.PreferencePenalty.ContainsKey d.Id then
let prefVar = slacks.PreferencePenalty.[d.Id]
objective <- Add(objective, Mul(weights.WeightPreference, Var prefVar))
// === PLAN STABILITY (CHURN) ===
match input.Baseline with
| Some _ ->
for (r, k, t), churnVar in slacks.ProductionChurn |> Map.toSeq do
objective <- Add(objective, Mul(weights.WeightChurn, Var churnVar))
for (m, p, t), churnVar in slacks.TransportChurn |> Map.toSeq do
objective <- Add(objective, Mul(weights.WeightChurn, Var churnVar))
| None -> ()
solver.SetObjective(objective, minimize = true)
/// Default weight configuration
let defaultWeights = {
// Delivery (per bucket-day late, per unit short)
WeightLateness = 100m
WeightFulfillment = 1000m
WeightEarliness = 5m
// Inventory
WeightHolding = 1m
WeightSafety = 500m
WeightTargetDeviation = 10m
// Capacity
WeightCapacityOverload = 5000m
WeightSpread = 10m
// Operations
WeightCost = 1m
WeightCO2 = 0.5m
// Pegging/Assignment (very high - these should not happen)
WeightSourceOverUtil = 10000m
WeightOverAssign = 10000m
// Preference
WeightPreference = 20m
WeightQuality = 50m
// Stability
WeightChurn = 50m
}
```
### 8.4 Hard Caps (Optional Constraints)
When business requires absolute limits:
**CO2 Emissions Cap:**
$$\sum_{m,p,t} CO2^{trans}_m \cdot x^{trans}_{m,p,t} + \sum_{r,k,t} CO2^{prod}_r \cdot x^{prod}_{r,k,t} \leq Cap^{co2}$$
**Total Cost Cap:**
$$\sum_{\text{all costs}} \leq Cap^{cost}$$
**F# Implementation:**
```fsharp
let addHardCaps (solver: ISolver) (policy: PolicyInput) (vars: Variables) =
match policy.CO2Cap with
| Some cap ->
let co2Expr = computeTotalCO2 vars
solver.AddConstraint("CO2Cap", co2Expr, LessOrEqual, cap)
| None -> ()
match policy.CostCap with
| Some cap ->
let costExpr = computeTotalCost vars
solver.AddConstraint("CostCap", costExpr, LessOrEqual, cap)
| None -> ()
```
### 8.5 Lexicographic / Multi-Stage Optimization
For strict priority ordering of objectives:
| Stage | Objective | Fixed From Previous |
| ----- | ------------------------------------------- | ------------------------------- |
| 1 | \(\min \sum Short_d + \sum OverUtil_{g,t}\) | — |
| 2 | \(\min \sum Back_d\) | \(Z_1 \leq Z^*_1 + \epsilon_1\) |
| 3 | \(\min TotalCost\) | \(Z_2 \leq Z^*_2 + \epsilon_2\) |
| 4 | \(\min CO2Emissions\) | \(Z_3 \leq Z^*_3 + \epsilon_3\) |
**Stage 1: Feasibility**
$$\min Z_1 = \sum_{d} Short_d + \sum_{g,t} OverUtil_{g,t} + \sum_{r,k,t} MissUp_{r,k,t}$$
**Stage 2: Service Level**
$$\min Z_2 = \sum_{d} w^{prio}_d \cdot Back_d$$
$$\text{s.t. } Z_1 \leq Z^*_1 + \epsilon_1$$
**Stage 3: Cost Minimization**
$$\min Z_3 = TotalCost$$
$$\text{s.t. } Z_1 \leq Z^*_1 + \epsilon_1, \; Z_2 \leq Z^*_2 + \epsilon_2$$
**F# Lexicographic Solver:**
```fsharp
let solveLexicographic (solver: ISolver) (input: OptimizerInput) (vars: Variables) =
// Stage 1: Feasibility
let z1Objective = buildFeasibilityObjective vars
solver.SetObjective(z1Objective, minimize = true)
let result1 = solver.Solve(stageConfig 1)
let z1Star = result1.ObjectiveValue.Value
// Add bound constraint
solver.AddConstraint("Z1Bound", z1Objective, LessOrEqual, z1Star * 1.01m)
// Stage 2: Service Level
let z2Objective = buildServiceLevelObjective vars
solver.SetObjective(z2Objective, minimize = true)
let result2 = solver.Solve(stageConfig 2)
let z2Star = result2.ObjectiveValue.Value
solver.AddConstraint("Z2Bound", z2Objective, LessOrEqual, z2Star * 1.01m)
// Stage 3: Cost
let z3Objective = buildCostObjective vars
solver.SetObjective(z3Objective, minimize = true)
solver.Solve(stageConfig 3)
```
### 8.6 KPI-to-Objective Mapping
| KPI Name | Objective Term | Typical Weight | Business Interpretation |
| ----------------------- | ---------------------------------------- | -------------- | ----------------------------------------- |
| **DeliveryPerformance** | \(w^{late} \cdot Back\) | 100-1000 | High weight = prioritize on-time delivery |
| **DeliveryFulfillment** | \(w^{short} \cdot Short\) | 500-5000 | Very high = avoid stockouts at all costs |
| **InventoryAdherence** | \(w^{target} \cdot (Below + Above)\) | 10-100 | Medium = maintain target levels |
| **HoldingCost** | \(w^{hold} \cdot h \cdot Inv\) | 1-10 | Low = accept some inventory |
| **CapacityOverload** | \(w^{overutil} \cdot OverUtil\) | 500-5000 | Very high = respect capacity limits |
| **SpreadConsumption** | \(w^{spread} \cdot Spread\) | 10-100 | Medium = smooth production |
| **ProductionCost** | \(w^{prod} \cdot Cost^{prod} \cdot x\) | 1 | Base cost weight |
| **TransportCost** | \(w^{trans} \cdot Cost^{trans} \cdot x\) | 1 | Base cost weight |
| **Preference** | \(w^{pref} \cdot Pref\) | 1-50 | Low-Medium = prefer good sources |
| **CO2Emissions** | \(w^{co2} \cdot CO2 \cdot x\) | 0.1-10 | Sustainability priority |
| **PlanChurn** | \(w^{churn} \cdot Churn\) | 10-100 | Medium = prefer stable plans |
### 8.7 Weight Normalization Strategy
To ensure terms are comparable:
$$\frac{w^{late} \cdot \mathbb{E}[\text{Lateness}]}{\text{Total Objective}} \approx \frac{w^{cost} \cdot \mathbb{E}[\text{Cost}]}{\text{Total Objective}}$$
**Practical Approach:**
1. Estimate expected value of each slack/cost term
2. Set weights so contributions are in desired ratio
3. Example: If lateness ~1000 bucket-units and cost ~\$100,000:
- Set \(w^{late} = 100\) → contribution = 100,000
- Set \(w^{cost} = 1\) → contribution = 100,000
- Both contribute equally
**F# Weight Calibration:**
```fsharp
let calibrateWeights (historicalData: HistoricalStats) =
let expectedLateness = historicalData.AvgLateness
let expectedCost = historicalData.AvgCost
let expectedShortfall = historicalData.AvgShortfall
// Target: lateness contributes 30%, cost 40%, shortfall 30%
let baselineContribution = 100000m // arbitrary reference
{
WeightLateness = (0.30m * baselineContribution) / expectedLateness
WeightCost = (0.40m * baselineContribution) / expectedCost
WeightFulfillment = (0.30m * baselineContribution) / expectedShortfall
// ... other weights
}
```
---
## 9. Explainability, Limiters, and Diagnostics
> Every non-zero slack variable and every unfulfilled demand must be **explainable**.
### 9.1 Explainability Framework
**Principle:** For every demand \(d\) where \(Short_d > 0\) or \(Back_d > 0\), the optimizer must provide:
1. **What**: The specific constraint that limited fulfillment
2. **Where**: The resource/material/transport that was the bottleneck
3. **When**: The time bucket(s) where the limitation occurred
4. **How much**: The magnitude of the gap
5. **Alternatives**: Possible remediation actions
### 9.2 Limiter Taxonomy
| Domain | Limiter Code | Slack Variable | Threshold | Business Meaning |
| ------------- | ----------------- | ---------------------- | --------------- | ----------------------- |
| **Material** | MaterialShortfall | \(Short_d\) | \(> 0\) | Insufficient supply |
| **Material** | SafetyViolation | \(SafetyViol_{p,s,t}\) | \(> 0\) | Below safety stock |
| **Material** | MissingUpstream | \(MissUp_{r,k,t}\) | \(> 0\) | Component not available |
| **Capacity** | CapacityOverload | \(OverUtil_{g,t}\) | \(> 0\) | Resource overloaded |
| **Capacity** | SpreadViolation | \(Spread_{g,t}\) | \(> threshold\) | Uneven production |
| **Transport** | TransportCapacity | \(TransOver_{m,t}\) | \(> 0\) | Leg capacity exceeded |
| **Supplier** | SupplierMOQ | — | MOQ not met | Order below minimum |
| **Storage** | StorageExcess | \(StoreOver_{s,t}\) | \(> 0\) | Warehouse full |
| **Quality** | QualityMismatch | \(QualPen_d\) | \(> 0\) | Below quality target |
| **Freshness** | ShelfLifeExceeded | — | age > SL | Inventory expired |
### 9.3 Binding Constraint Analysis
**Shadow Prices (Dual Values):**
For a binding constraint \(i\), the shadow price \(\pi_i\) indicates:
$$\frac{\partial Z^*}{\partial b_i} = \pi_i$$
where \(b_i\) is the RHS of constraint \(i\).
**Interpretation:**
- \(\pi_i > 0\) for capacity: Additional capacity is valuable
- \(\pi_i > 0\) for material: Additional supply is needed
- Highest \(\pi_i\) identifies the bottleneck
**Bottleneck Identification Algorithm:**
```fsharp
let identifyBottleneck (solution: SolveResult) (demand: DemandId) =
// Get all constraints related to demand d
let relatedConstraints = getConstraintsForDemand demand
// Find binding constraints (slack = 0 or dual > 0)
let bindingConstraints =
relatedConstraints
|> List.filter (fun c ->
let dual = solution.DualValues.[c.Name]
dual > 0.0 || isBinding c solution)
// Rank by shadow price magnitude
bindingConstraints
|> List.sortByDescending (fun c -> solution.DualValues.[c.Name])
|> List.head
|> constraintToLimiter
```
### 9.4 Infeasibility Diagnosis
When solver returns `INFEASIBLE`:
**Step 1: Extract IIS (Irreducible Infeasible Subsystem)**
$$IIS = \{c_1, c_2, \ldots, c_k\} \subseteq \text{Constraints}$$
such that:
- The IIS constraints together are infeasible
- Removing any single constraint makes the rest feasible
**Step 2: Map IIS to Limiters**
```fsharp
let diagnoseInfeasibility (iis: Constraint list) =
iis
|> List.map (fun c ->
match parseConstraintName c.Name with
| DemandBalance d -> { Domain = Material; Code = "MaterialShortfall"; Demand = Some d }
| CapacityLimit (g, t) -> { Domain = Capacity; Code = "CapacityOverload"; Resource = Some g; Bucket = Some t }
| InventoryBalance (p, s, t) -> { Domain = Material; Code = "InventoryShortage"; PISP = Some (p, s); Bucket = Some t }
| _ -> { Domain = System; Code = "Unknown" }
)
|> groupByDomain
|> generateExplanation
```
### 9.5 Lateness Root Cause Analysis
When \(Back_d > 0\):
**Trace Algorithm:**
1. Find earliest bucket where demand could be fulfilled
2. Trace backward through supply chain:
- Check production availability → capacity constraints
- Check material availability → inventory constraints
- Check transport timing → lead time constraints
- Check supplier timing → supplier lead time
```fsharp
let traceLateness (d: DemandId) (solution: Solution) =
let dueBucket = params.DueBucket.[d]
let fulfillmentBuckets =
solution.Fulfillment.[d]
|> Map.filter (fun t qty -> t > dueBucket && qty > 0)
for (bucket, qty) in fulfillmentBuckets |> Map.toSeq do
let lateDays = bucket - dueBucket
let cause = traceSupplyChain d bucket solution
yield {
Demand = d
LateBucket = bucket
LateDays = lateDays
Quantity = qty
RootCause = cause
Suggestions = generateSuggestions cause
}
```
### 9.6 KPI Contribution Breakdown
For any solution, provide objective term breakdown:
$$Z^* = Z^*_{delivery} + Z^*_{inventory} + Z^*_{capacity} + Z^*_{cost} + \ldots$$
**F# Implementation:**
```fsharp
let computeKPIBreakdown (solution: Solution) (weights: PolicyInput) =
let latenessContrib =
solution.Lateness
|> Map.toSeq
|> Seq.sumBy (fun (d, back) -> weights.WeightLateness * params.Priority.[d] * back)
let shortfallContrib =
solution.Shortfall
|> Map.toSeq
|> Seq.sumBy (fun (d, short) -> weights.WeightFulfillment * params.Priority.[d] * short)
let holdingContrib =
solution.Inventory
|> Map.toSeq
|> Seq.sumBy (fun ((p,s,t), inv) -> weights.WeightHolding * params.HoldingCost.[(p,s)] * inv)
{
DeliveryPerformance = latenessContrib
DeliveryFulfillment = shortfallContrib
HoldingCost = holdingContrib
// ... other contributions
Total = solution.ObjectiveValue
}
```
### 9.7 Limiter Output Format
```fsharp
type Limiter = {
Domain: LimiterDomain
Code: string
Severity: LimiterSeverity // Critical, Warning, Info
Message: string
AffectedDemands: DemandId list
AffectedResources: ResourceGroupId list option
AffectedBuckets: Bucket list
Magnitude: decimal
Suggestions: string list
}
type LimiterDomain =
| Material | Capacity | Transport | Supplier | Storage | Quality | Freshness | System
type LimiterSeverity = Critical | Warning | Info
// Example output
{
Domain = Capacity
Code = "CapacityOverload"
Severity = Critical
Message = "Resource group 'Casting' exceeded capacity by 120 hours in bucket 15"
AffectedDemands = ["D001"; "D002"; "D005"]
AffectedResources = Some ["Casting"]
AffectedBuckets = [15; 16]
Magnitude = 120m
Suggestions = [
"Add overtime capacity in bucket 15-16"
"Shift some production to bucket 14"
"Consider alternate routing R2 which uses 'Rolling' instead"
]
}
```
### 9.8 Diagnostic Queries
Post-solve diagnostic queries for troubleshooting:
| Query | Formula | Usage |
| -------------------- | -------------------------------------------- | ------------------------------------- |
| Bottleneck resources | \(\{g : \sum_t OverUtil_{g,t} > 0\}\) | Find overloaded resources |
| Material shortages | \(\{(p,s,t) : Inv_{p,s,t} < Safety_{p,s}\}\) | Find safety stock violations |
| Late demands | \(\{d : Back_d > 0\}\) | Find late deliveries |
| Unused capacity | \(\{(g,t) : Util_{g,t} < 50\%\}\) | Find underutilized resources |
| Plan changes | \(\{(r,k,t) : Churn^{prod}_{r,k,t} > 0\}\) | Find production changes from baseline |
---
## 10. Solution Post-Processing (Still No Business Logic)
> Post-processing converts optimizer variables into **domain artifacts**. Logic-free: just mapping.
### 10.1 Output Artifacts
| Artifact | Source Variables | Description |
| ------------------------ | -------------------------------------------- | ---------------------------------------------- |
| **Fulfillment Plan** | \(y_{d,t}\), \(assign_{d,src,t}\) | Which demand fulfilled when, from which source |
| **Production Schedule** | \(x^{prod}_{r,k,t}\), \(run_{p,l,t}\) | What to produce, when, where |
| **Purchase Orders** | \(x^{sup}_{v,p,s,t}\), \(n^{lot}_{v,p,s,t}\) | What to buy, from whom, when |
| **Transport Orders** | \(x^{trans}_{m,p,t}\) | What to ship, on which leg, when |
| **Inventory Trajectory** | \(Inv_{p,s,t}\) | Projected end-of-bucket inventory |
| **Pegging Map** | \(assign_{d,src,t}\) | Demand → supply source traceability |
| **KPI Scorecard** | Slack variables | Objective term breakdown |
| **Limiter Report** | Non-zero slacks | Explainability per demand |
### 10.2 Converting Variables to Domain Events
```fsharp
// Work Order from production variables
let toWorkOrders solution =
solution.x_prod
|> Seq.filter (fun (r,k,t,qty) -> qty > 0 && k = finalStep r)
|> Seq.map (fun (r,k,t,qty) -> {
WorkOrderId = deterministicId(r,k,t)
RoutingId = r
ProductId = output(r)
Quantity = qty
PlannedStart = t - totalLeadTime(r)
PlannedEnd = t
State = Planned
})
// Pegging links from assignment variables
let toPeggingLinks solution =
solution.assign
|> Seq.filter (fun (d,src,t,qty) -> qty > 0)
|> Seq.map (fun (d,src,t,qty) -> {
PeggingId = deterministicId(d,src,t)
DemandId = d
SupplySourceType = sourceType(src)
SupplySourceId = sourceId(src)
Quantity = qty
AvailableBucket = t
})
```
### 10.3 Idempotency Keys & Deterministic IDs
| Artifact | ID Formula | Purpose |
| -------------- | ----------------------------------------------- | -------------------- |
| WorkOrder | hash(routing, product, stocking_point, bucket) | Stable across reruns |
| PurchaseOrder | hash(supplier, product, stocking_point, bucket) | Stable across reruns |
| TransportOrder | hash(leg, product, depart_bucket) | Stable across reruns |
| PeggingLink | hash(demand_id, source_type, source_id, bucket) | Stable across reruns |
### 10.4 Consistency Checks
| Check | Formula | Action if Failed |
| ---------------------- | ------------------------------------ | ----------------------------------- |
| Mass balance | Σ inflows = Σ outflows + Δ inventory | Log error, flag solution |
| Non-negative inventory | Inv_{p,s,t} ≥ 0 ∀p,s,t | Should be guaranteed by constraints |
| Date sanity | PlannedEnd ≥ PlannedStart | Log error, flag solution |
| Fulfilled ≤ Demanded | Σ y_{d,t} ≤ Qty_d ∀d | Should be guaranteed by constraints |
| Source availability | Σ assign ≤ available | Check OverUtilized = 0 |
---
## 11. Performance, Decomposition, and Scaling Patterns
### 11.1 Precomputation to Shrink Solver Work
| Precomputation | Benefit | When to Apply |
| -------------------------------- | -------------------------------------- | ------------------- |
| **Feasible routing sets** | Remove invalid r,t combinations | Always |
| **k-shortest itineraries** | Reduce transport combinatorics | Multi-hop transport |
| **BOM flattening** | Single-level consumption for deep BOMs | Depth > 5 |
| **Demand aggregation** | Reduce D by grouping similar demands | Many small demands |
| **Time bucket pruning** | Remove t where nothing can happen | Large horizons |
| **Capacity bucket aggregation** | Coarser granularity in far future | Hybrid horizon |
| **Characteristic compatibility** | Pre-filter Compatible_{d,r} | CBP active |
### 11.2 Decomposition Options
**11.2.1 Rolling Horizon (Time Decomposition)**
```
For each window W of size H:
1. Fix decisions in frozen zone (t < W_start)
2. Solve for W with demand/supply in W
3. Roll forward: W_start += step
4. Repeat
```
- **Benefit**: Smaller models per solve
- **Risk**: Suboptimal due to myopic decisions
**11.2.2 Demand-Based Decomposition**
```
1. Partition demands by priority/family/region
2. Solve high-priority first (reserve capacity)
3. Solve lower-priority with remaining capacity
```
- **Benefit**: Ensures high-priority feasibility
- **Risk**: Low-priority may be overly constrained
**11.2.3 Lagrangian Relaxation (Future)**
- Relax capacity/transport coupling constraints
- Solve subproblems per stocking point
- Coordinate via multiplier updates
**11.2.4 Fix-and-Optimize (Matheuristic)**
```
1. Solve LP relaxation
2. Fix near-integer binaries
3. Solve smaller MIP
4. Repeat with different fixing
```
### 11.3 Warm Starts & Incumbent Reuse
| Strategy | When | Benefit |
| ---------------------------------- | ------------------ | ---------------------- |
| **Previous solution as MIP start** | Replan/incremental | Faster convergence |
| **Heuristic solution as start** | Fresh solve | Better incumbent early |
| **Fix baseline, optimize delta** | Churn minimization | Smaller search space |
### 11.4 Runtime Limits and Fallback
```fsharp
type SolverConfig = {
TimeLimit: TimeSpan // e.g., 30 minutes
GapTolerance: float // e.g., 0.01 (1%)
FallbackToHeuristic: bool // if timeout, use best found
MaxIterations: int option // for iterative methods
}
let solve config model =
match runSolver config model with
| Optimal solution -> solution
| Feasible solution when config.FallbackToHeuristic -> solution
| Timeout when config.FallbackToHeuristic -> runHeuristic model
| Infeasible -> extractIIS model |> toError
| _ -> Error "Solver failed"
```
### 11.5 Scaling Guidelines (from puzzle.pdf benchmarks)
| Scenario | Typical Runtime | COLs | PISPs | Depth | Routings/PISP |
| ---------- | --------------- | ---- | ----- | ----- | ------------- |
| Small | 20 min | 400 | 15 | 5 | 1.2 |
| Medium | 45 min | 600 | 1200 | 5 | 1.0 |
| Large | 4 hr | 4000 | 1000 | 5 | 1.0 |
| Very Large | 10 hr | 6500 | 4000 | 7 | 1.0 |
> **Key drivers**: Number of demand lines × BOM depth × routings per PISP × time buckets
---
## 12. Validation & Testing Strategy (Model Level)
### 12.1 Unit Tests for Constraints (Toy Instances)
| Test Category | Example | Expected Result |
| ------------------------- | -------------------------------------------- | ---------------------------------- |
| **Demand satisfaction** | 1 demand, 1 product, sufficient inventory | Fulfilled on time, Short=0, Back=0 |
| **Inventory balance** | Track inventory across 3 buckets | Inv trajectory matches manual calc |
| **BOM consumption** | 1 product, 2 components | Components consumed correctly |
| **Capacity limit** | 2 demands, 1 resource, insufficient capacity | One demand late or shorted |
| **Transport lead time** | Ship in t=1, LT=2 | Arrives in t=3 |
| **MOQ enforcement** | Order 50, MOQ=100 | Either 0 or ≥100 ordered |
| **Campaign min run** | MinRun=100, demand=50 | Either 0 or ≥100 produced |
| **Shelf-life expiration** | Inventory ages past ShelfLife | Discarded (Inv=0) |
### 12.2 Regression Test Sets
| Scenario | Purpose | Size |
| ------------------- | ------------------------- | -------------------------------- |
| **Baseline** | Smoke test | 10 demands, 5 products |
| **Multi-level BOM** | BOM explosion correctness | 3 levels, 20 products |
| **Campaign** | Changeover/sequencing | 1 line, 5 products, setup matrix |
| **Multi-site** | Transport network | 3 sites, 5 legs |
| **Full scale** | Performance benchmark | 1000 demands, 100 products |
### 12.3 Property Tests (Invariants)
| Property | Formula | Rationale |
| -------------------------- | ----------------------------------- | -------------------------- |
| **Mass conservation** | Σ inflows = Σ outflows + Δ stock | Physics of material flow |
| **Non-negative inventory** | Inv_{p,s,t} ≥ 0 | Cannot have negative stock |
| **Demand balance** | Σ y_{d,t} + Short_d = Qty_d | All demand accounted for |
| **Capacity respect** | Usage ≤ Cap + OverUtil | Finite mode enforced |
| **Lead time respect** | Arrival ≥ Depart + LT | Time physics |
| **Source availability** | Assigned ≤ Available + OverUtilized | Source not overdrawn |
### 12.4 Performance Tests
| Metric | Target | Measurement |
| ----------------------- | ---------------------- | ------------------------------- |
| **Model build time** | < 60s for 1000 demands | Time from input to solver call |
| **Solve time** | < 30 min for full run | Solver wall clock |
| **Memory usage** | < 4GB | Peak memory during solve |
| **Gap achieved** | < 1% | Optimality gap at termination |
| **Solution extraction** | < 10s | Time from solver done to output |
### 12.5 Edge Case Tests
| Edge Case | Test | Expected Behavior |
| ------------------------ | ---------------------------- | ---------------------------------- |
| **No feasible solution** | Demand > all possible supply | Infeasible + IIS |
| **Empty demand** | Zero demands | Empty solution, no errors |
| **Cyclic BOM** | A requires B requires A | Detect and reject (or break cycle) |
| **Zero capacity** | Cap_{g,t} = 0 | Production blocked, use slack |
| **Past-due demand** | Due date in the past | Immediate lateness, fulfill ASAP |
---
## 13. Open Questions / Required Decisions (Before Finalizing the Model)
### 13.1 Time Representation
| Question | Options | Recommendation |
| ---------------------- | ----------------------------- | ----------------------------------------------------------- |
| **Bucket granularity** | Hours / Days / Weeks / Hybrid | Days for tactical (hybrid: hours near-term, days/weeks far) |
| **Lead time modeling** | Integer buckets / Continuous | Integer buckets (simpler, aligns with MPS practice) |
| **Horizon length** | Fixed / Rolling | Fixed horizon with rolling execution |
### 13.2 Sequencing & Campaigns
| Question | Options | Recommendation |
| ------------------------ | -------------------------------------------------- | -------------------------------------------------------- |
| **Detailed sequencing** | Full sequencing / Bucket-level only | Bucket-level (per Planning.md: "no detailed sequencing") |
| **Campaign constraints** | Optional / Always | Include but make weights configurable |
| **Changeover modeling** | Ignore / Sequence-independent / Sequence-dependent | Sequence-dependent (from Planning.md 6.14) |
### 13.3 Pegging
| Question | Options | Recommendation |
| ----------------------- | -------------------------------------- | ---------------------------------------------- |
| **Pegging granularity** | Lot-level / Bucket-level / Slice-level | Bucket-level (balance of detail vs complexity) |
| **Firm peg handling** | Hard constraint / Soft penalty | Hard constraint for confirmed pegs |
### 13.4 Reservations
| Question | Options | Recommendation |
| ----------------------------------- | -------------------------------------- | ---------------------------------------------------------------- |
| **Reservation treatment** | Explicit variables / Input subtraction | Input subtraction (simpler, reservations from Promise are input) |
| **Optimizer creates reservations?** | Yes / No | No (optimizer recommends; Promise creates reservations) |
### 13.5 Scope Decisions
| Question | Options | Recommendation |
| --------------------------------- | ------------------------------ | ------------------------------------- |
| **Supplier optimization** | Full / Optional / Out of scope | Optional (include but can disable) |
| **Shelf-life** | Full / Optional / Out of scope | Optional (for CPG customers) |
| **Characteristic-based planning** | Full / Optional / Out of scope | Optional (for metals/steel customers) |
---
## 14. Self-Review Checklist
### 14.1 Review #1: Mathematical Modeling Correctness
| Aspect | Check | Status | Notes |
| ------------------------- | -------------------------------------------------------- | ---------- | -------------------------------------------- |
| **Set completeness** | All entities in Planning.md have corresponding sets | ✅ Complete | §3 covers all domain entities |
| **Parameter coverage** | All data inputs are parameters, not hard-coded | ✅ Complete | §4 defines 50+ parameters |
| **Variable completeness** | All decisions and slacks have variables | ✅ Complete | §5-6 cover 30+ variable types |
| **Constraint soundness** | Each constraint enforces physics/business rule correctly | ✅ Complete | §7 has 15 constraint families with equations |
| **Objective alignment** | All KPIs from puzzle.pdf have objective terms | ✅ Complete | §8 maps all KPIs to terms |
| **Index consistency** | Variables indexed correctly (no missing indices) | ✅ Complete | Consistent notation throughout |
| **Big-M tightness** | No arbitrary large M values; bounds derived | ✅ Complete | §4.14 provides bound derivation |
| **Linearization** | Non-linear terms properly linearized | ✅ Complete | Absolute values, products linearized |
| **Feasibility** | Model has feasible region (slacks ensure) | ✅ Complete | All hard constraints have slack alternatives |
| **Explainability** | Every slack maps to a limiter | ✅ Complete | §9 provides complete mapping |
### 14.2 Review #2: CS/Implementation Realism
| Aspect | Check | Status |
| ---------------------------------- | --------------------------------------- | ------ |
| **Input contracts clear** | Types/shapes defined for all inputs | ✅ |
| **Output contracts clear** | Types/shapes defined for all outputs | ✅ |
| **Solver abstraction** | Not coupled to specific solver | ✅ |
| **No business logic in optimizer** | All rules in sets/parameters | ✅ |
| **Deterministic IDs** | Stable keys for artifacts | ✅ |
| **Explainability** | Every slack maps to a limiter | ✅ |
| **Performance guidance** | Scaling estimates provided | ✅ |
| **Decomposition options** | For large instances | ✅ |
| **Fallback strategy** | For timeout/infeasible | ✅ |
| **Testing strategy** | Unit, regression, property, performance | ✅ |
### 14.3 Gap Analysis Summary (vs Planning.md and puzzle.pdf)
| Feature | Planning.md | puzzle.pdf | Our Coverage |
| --------------------------- | -------------- | ---------- | -------------------------------- |
| Finite capacity | ✅ Required | ✅ | ✅ Sections 4.6, 7.5 |
| BOM explosion | ✅ Required | ✅ | ✅ Sections 4.4, 7.3 |
| Transport | ✅ Required | ✅ | ✅ Sections 3.5, 4.8, 7.8 |
| Campaigns | ✅ Section 6.14 | ✅ | ✅ Sections 3.4, 4.7, 7.7 |
| Shelf-life | ✅ Section 6.16 | ❌ | ✅ Sections 3.8, 4.10, 7.10 |
| **Pegging/Assignment** | ✅ Section 5.9 | ✅ | ✅ **Enhanced §5.8, §6.6, §7.12** |
| Delivery performance | ✅ | ✅ | ✅ Section 8.1 |
| Inventory adherence | ✅ | ✅ | ✅ Section 8.1 |
| Supply/WIP holding | ✅ | ✅ | ✅ Sections 4.11, 6.3 |
| Capacity overload | ✅ | ✅ | ✅ Sections 6.4, 7.5 |
| Campaign overload | ✅ | ✅ | ✅ Sections 6.4, 7.7 |
| Supply preference | ⚠️ Implicit | ✅ | ✅ Sections 4.5, 8.5 |
| Supply quality | ⚠️ Implicit | ✅ | ✅ Sections 4.5, 6.8 |
| Upstream validation | ✅ | ✅ | ✅ Sections 6.5, 7.14 |
| **Source over-utilization** | ⚠️ Implicit | ✅ | ✅ **Enhanced §6.6, §8.2** |
| Storage capacity | ❌ Not explicit | ✅ | ✅ Sections 4.3, 7.6 |
| Production leveling | ✅ Section 6.16 | ✅ Spread | ✅ Section 7.11 |
| Piece indivisibility | ⚠️ Implicit | ✅ | ✅ Section 7.12 |
| Characteristic-based | ❌ Not explicit | ✅ (Steel) | ✅ Section 3.10 |
### 14.4 Review #3: Pegging / Assignment Model Completeness
| Aspect | Check | Status | Notes |
| --------------------------- | ----------------------------------------------------- | ------ | ------------------------------------- |
| **Variable structure** | Explicit by source type (INV, FIRM, PROD, TRANS, SUP) | ✅ | §5.8 defines per-type variables |
| **Balance constraint** | Σ assignments = Σ fulfillment | ✅ | §7.12.1 |
| **Source availability** | Assignment ≤ source quantity | ✅ | §7.12.2-7.12.6 |
| **Timing feasibility** | Supply available before fulfillment | ✅ | §7.12.7 |
| **Link to production** | α^PROD ≤ x^prod | ✅ | §7.12.4 links to production variables |
| **Link to transport** | α^TRANS ≤ x^trans | ✅ | §7.12.5 links to transport variables |
| **Link to purchase** | α^SUP ≤ x^sup | ✅ | §7.12.6 links to purchase variables |
| **Over-utilization slacks** | Per source type | ✅ | §6.6 defines all OverUtil types |
| **Objective penalty** | High weight for over-utilization | ✅ | §8.2 includes w^srcOver |
| **Pegging output** | Extractable from solution | ✅ | §5.8.7 defines extraction |
| **F# implementation** | Variable creation pseudocode | ✅ | §5.8.6 |
| **Constraint builder** | F# constraint generation | ✅ | §7.12.10 |
---
## 15. Changelog
| Date | Version | Changes |
| ---------- | ------- | --------------------------------------------------------------------------------------------- |
| 2026-01-04 | 1.0 | Initial outline created |
| 2026-01-04 | 1.1 | Gap analysis vs puzzle.pdf and Planning.md |
| 2026-01-04 | 1.2 | Added: Storage capacity, Campaigns, Shelf-life, Leveling, Indivisibility, CBP, KPI alignment |
| 2026-01-04 | 2.0 | **Full Mathematical Specification** with equations and F# pseudocode: |
| | | - §0-2: Complete purpose, principles, input/output contracts with F# types |
| | | - §3: Formal set definitions with cardinality estimates and derived sets |
| | | - §4: Parameter specifications with domains, relationships, and F# representations |
| | | - §5: Decision variable definitions with bounds, linking constraints, and creation pseudocode |
| | | - §6: Support variable taxonomy with KPI mappings and contribution formulas |
| | | - §7: Complete constraint equations (15+ constraint families) with F# generators |
| | | - §8: Full objective function formulation with component breakdown and weight calibration |
| | | - §9: Enhanced explainability framework with binding constraint analysis and IIS diagnosis |
| 2026-01-05 | 2.1 | **Enhanced Pegging / Assignment Model** (per user review): |
| | | - §5.8: Explicit assignment variables by source type (INV, FIRM, PROD, TRANS, SUP) |
| | | - §5.8: F# variable creation with timing feasibility checks |
| | | - §5.8: Pegging output extraction pseudocode |
| | | - §6.6: Source-specific over-utilization slack variables |
| | | - §7.12: Complete pegging constraints (balance, availability, timing, linking) |
| | | - §7.16: Clarification of fulfillment vs. assignment relationship |
| | | - §8.2: Updated objective with source over-utilization penalties |
| | | - §8.3: Enhanced F# objective builder with all pegging-related terms |
| | | - §14.4: New self-review checklist for pegging model completeness |
---
## 16. References
- `documents/Planning.md` — Functional requirements and module architecture
- puzzle.pdf — Industry MPS optimization patterns and KPI taxonomy
- CPLEX/OR-Tools documentation — Solver capabilities and best practices
# Medhāvī ProductionPlanning — Optimizer Architecture
**Status**: 📋 Architecture Design
**Version**: 2.0
**Last Updated**: 2026-01-06
**Purpose**: Define modular, meta-layer optimizer architecture with Google OR-Tools
**Related**: `documents/Optimizer.md` (mathematical model), `documents/Planning.md` (functional requirements)
---
## Document Overview
| Section | Content | Purpose |
| ------- | ----------------------------------- | ------------------------------------------ |
| §1 | Design Principles | Why modular, not monolithic |
| §2 | Architecture Overview | High-level component structure |
| §3 | Component Deep Dive | Optimizer, OptimizationRun, Steps |
| §4 | Decomposition Strategies | Anchor-based, demand/supply partitioning |
| §5 | Heuristic Approaches | Fast initial solutions |
| §6 | Google OR-Tools Integration | CP-SAT, hints, incremental solving |
| §7 | **Iterative Optimization Framework**| Iterations, demand selection, KPI tracking |
| §8 | F# Implementation Design | Types, interfaces, workflows |
| §9 | Execution Modes | Light vs Full, What-If |
| §10 | Performance & Scaling | Parallelization, checkpointing |
| §11 | Self-Review & Recommendations | Gap analysis |
---
## 1. Design Principles
### 1.1 Why Not Monolithic?
A monolithic optimizer that processes all demands, all products, all time periods in a single solve has critical limitations:
| Problem | Impact | Solution |
| --------------- | ---------------------------------------------------- | ----------------------------- |
| **Scale** | 50K demands × 100 products × 90 days = massive model | Decompose by anchors |
| **Time** | Full solve may take hours; users need fast feedback | Heuristic → Refine pipeline |
| **Flexibility** | Can't easily switch strategies | Modular step architecture |
| **Debugging** | Hard to understand why plan is bad | Step-by-step visibility |
| **Incremental** | Rerun everything for small changes | Fix-and-Optimize, warm starts |
### 1.2 Core Design Principles
```
1. PIPELINE OVER MONOLITH
─────────────────────────────────────────────────────────
Each optimization run is a sequence of composable steps.
Steps can be reordered, skipped, or replaced.
2. ANCHOR-BASED DECOMPOSITION
─────────────────────────────────────────────────────────
Don't solve everything at once. Select "anchors" (demands,
resources, products) and solve focused subproblems.
3. HEURISTIC FIRST, OPTIMIZE LATER
─────────────────────────────────────────────────────────
Fast heuristic provides feasible solution quickly.
Optimization refines it within time budget.
4. WARM STARTS AND INCREMENTAL SOLVING
─────────────────────────────────────────────────────────
Previous solutions seed new runs. Small changes don't
require full re-solve.
5. EXPLAINABILITY AT EVERY STEP
─────────────────────────────────────────────────────────
Each step produces artifacts, metrics, and limiters.
User can understand what happened and why.
6. SOLVER ABSTRACTION
─────────────────────────────────────────────────────────
Business logic independent of solver (OR-Tools today,
could be CPLEX/Gurobi tomorrow).
7. ITERATIVE REFINEMENT WITH UNIFIED WORKFLOW
─────────────────────────────────────────────────────────
Monolithic and iterative modes use same workflow.
Monolithic = single iteration. Configuration-based.
```
### 1.3 Meta-Layer Concept
```
┌─────────────────────────────────────────────────────────────────┐
│ META LAYER │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Strategy Selection │ │
│ │ - Analyze problem characteristics │ │
│ │ - Select decomposition strategy │ │
│ │ - Choose heuristic/optimization balance │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Anchor Selection │ │
│ │ - Demand-based: high-priority demands first │ │
│ │ - Supply-based: bottleneck resources first │ │
│ │ - Product-based: high-volume products first │ │
│ │ - Time-based: near-term detailed, far-term aggregate │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Sub-Problem Generation │ │
│ │ - Partition problem based on selected anchors │ │
│ │ - Generate focused subproblems │ │
│ │ - Define coordination constraints │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Iterative Refinement │ │
│ │ - Solve subproblems (parallel where possible) │ │
│ │ - Lock satisfied demands │ │
│ │ - Adaptive selection of next batch │ │
│ │ - Repeat until stopping criteria met │ │
│ └──────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
```
---
## 2. Architecture Overview
### 2.1 Component Hierarchy
```
┌─────────────────────────────────────────────────────────────────┐
│ Optimizer (Orchestrator) │
│ - Entry point for all optimization requests │
│ - Manages run lifecycle, history, metrics │
│ - Handles concurrency (one active run per tenant) │
└───────────────────────────┬─────────────────────────────────────┘
│ triggers
┌─────────────────────────────────────────────────────────────────┐
│ OptimizationRun │
│ - Single execution of optimization pipeline │
│ - Contains ordered list of Steps │
│ - Tracks progress, checkpoints, artifacts │
│ - Can be paused, resumed, cancelled │
└───────────────────────────┬─────────────────────────────────────┘
│ executes
┌─────────────────────────────────────────────────────────────────┐
│ OptimizationStep (Abstract) │
│ - Single unit of work in the pipeline │
│ - Input: StepContext (previous outputs + config) │
│ - Output: StepResult (artifacts + metrics + status) │
└───────────────────────────┬─────────────────────────────────────┘
│ (OptimizationStep specifically)
┌─────────────────────────────────────────────────────────────────┐
│ Iteration (within Step) │
│ - Single solver execution on a demand subset │
│ - Captures before/after KPI snapshots │
│ - Tracks demand outcomes, locking decisions │
│ - Multiple iterations = iterative optimization │
└─────────────────────────────────────────────────────────────────┘
```
### 2.2 Standard Pipeline
```
┌────────────────────────────────────────────────────────────────────────────┐
│ STANDARD OPTIMIZATION PIPELINE │
├────────────────────────────────────────────────────────────────────────────┤
│ │
│ 1. PREPROCESSING STEP │
│ ├── Validate input data │
│ ├── Normalize timestamps (UTC) │
│ ├── Resolve references (products, routings, etc.) │
│ ├── Apply reservations and firm allocations │
│ └── Identify infeasibilities early │
│ │
│ 2. ANCHOR SELECTION STEP │
│ ├── Analyze problem characteristics │
│ ├── Select decomposition strategy │
│ ├── Partition demands into groups │
│ └── Output: AnchorPlan (which subproblems to solve) │
│ │
│ 3. HEURISTIC STEP (optional, but recommended) │
│ ├── Fast constructive heuristic │
│ ├── Greedy demand allocation by priority │
│ ├── Rule-based scheduling (EDD, SPT, etc.) │
│ └── Output: Initial feasible solution │
│ │
│ 4. OPTIMIZATION STEP (Google OR-Tools) ◄── Contains Iterations │
│ ├── For each iteration: │
│ │ ├── Select demand batch (adaptive) │
│ │ ├── Capture "before" KPI snapshot │
│ │ ├── Build model, add hints, solve │
│ │ ├── Capture "after" KPI snapshot │
│ │ ├── Lock satisfied demands │
│ │ └── Update tracking state │
│ ├── Continue until stopping criteria met │
│ └── Output: Optimized solution + iteration history │
│ │
│ 5. REPAIR STEP (if needed) │
│ ├── Check for violated constraints │
│ ├── Apply local fixes │
│ └── Output: Feasible solution │
│ │
│ 6. POSTPROCESSING STEP │
│ ├── Convert variables to domain events │
│ ├── Generate pegging links │
│ ├── Compute final KPIs │
│ ├── Generate limiters/explanations │
│ └── Output: Final plan + artifacts │
│ │
└────────────────────────────────────────────────────────────────────────────┘
```
### 2.3 Step Categories
| Category | Steps | Purpose |
| ---------------- | ------------------------------- | -------------------------------- |
| **Setup** | Preprocessing, AnchorSelection | Prepare data, plan decomposition |
| **Solving** | Heuristic, Optimization, Repair | Generate and improve solutions |
| **Output** | Postprocessing, Validation | Convert to domain artifacts |
| **Coordination** | Merge, Propagate | Combine subproblem results |
---
## 3. Component Deep Dive
### 3.1 Optimizer (Orchestrator)
The top-level component that manages the optimization lifecycle.
**Key Responsibilities:**
- Entry point for all optimization requests
- Manages configuration (time limits, decomposition settings)
- Maintains run history for warm starts
- Handles concurrency (one active run per tenant)
- Provides telemetry integration
**Key Types:**
- `OptimizerId` - Unique identifier
- `OptimizerConfig` - Time limits, default strategy, solver settings
- `OptimizationMode` - Light | Standard | Full | WhatIf
- `DecompositionStrategy` - NoDecomposition | DemandBased | SupplyBased | TimeBased | Hybrid
### 3.2 OptimizationRun
A single execution of the optimization pipeline.
**Key Responsibilities:**
- Execute pipeline steps in sequence
- Track progress and current step
- Manage checkpoints for resume capability
- Collect metrics across steps
- Handle cancellation gracefully
**Key Types:**
- `RunId` - Unique run identifier
- `RunStatus` - NotStarted | Running | Paused | Completed | Failed | Cancelled
- `StepContext` - Input data, current solution, artifacts, remaining time
- `OptimizationResult` - Final solution, artifacts, KPIs, limiters
### 3.3 OptimizationStep (Abstract)
The base interface for all pipeline steps.
**Interface Contract:**
- `Name` - Human-readable step name
- `Description` - What the step does
- `EstimatedTime` - Expected duration
- `Execute(context)` - Run the step
- `CanSkip(context)` - Whether step can be bypassed
- `Validate(context)` - Pre-execution validation
**Step Result Contents:**
- Updated solution (if modified)
- Artifacts produced (maps, diagnostics)
- Metrics (duration, items processed)
- Status (Success | PartialSuccess | Skipped | Failed)
- Limiters (explanations for the user)
---
## 4. Decomposition Strategies
### 4.1 Anchor Selection Concept
**Anchors** are the primary entities around which we partition the optimization problem.
```
┌─────────────────────────────────────────────────────────────────┐
│ ANCHOR SELECTION │
├─────────────────────────────────────────────────────────────────┤
│ │
│ DEMAND-BASED ANCHORS │
│ ──────────────────── │
│ Select demands adaptively, solve subproblems around them. │
│ │
│ ┌─────────────────────────────────────────────────────────────┐│
│ │ Iteration 1: Demands A, B, C (clustered by resource) ││
│ │ Iteration 2: Demands D, E, F (similar products) ││
│ │ Iteration 3: Demands G, H (problematic from iteration 1) ││
│ └─────────────────────────────────────────────────────────────┘│
│ │
├─────────────────────────────────────────────────────────────────┤
│ │
│ SUPPLY-BASED ANCHORS │
│ ───────────────────── │
│ Fix capacity-constrained resources first, allocate demands. │
│ │
│ ┌───────────┐ ┌───────────┐ ┌───────────┐ │
│ │Bottleneck │──▶ │ Dependent │──▶ │Downstream │ │
│ │ Resource │ │ Resources │ │ Resources │ │
│ └───────────┘ └───────────┘ └───────────┘ │
│ ▲ │
│ │ Solve first (most constrained) │
│ │
├─────────────────────────────────────────────────────────────────┤
│ │
│ TIME-BASED ANCHORS │
│ ─────────────────── │
│ Near-term detailed, far-term aggregate. │
│ │
│ ┌──────────────┬──────────────┬──────────────┐ │
│ │ Week 1-2 │ Week 3-6 │ Week 7+ │ │
│ │ (Detailed) │ (Medium) │ (Aggregate) │ │
│ │ Hourly │ Daily │ Weekly │ │
│ └──────────────┴──────────────┴──────────────┘ │
│ │
├─────────────────────────────────────────────────────────────────┤
│ │
│ PRODUCT-BASED ANCHORS │
│ ───────────────────── │
│ Solve by product family, then coordinate. │
│ │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │Family A │ │Family B │ │Family C │ ◀── Solve in parallel │
│ └────┬────┘ └────┬────┘ └────┬────┘ │
│ └────────────┴────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────┐ │
│ │ Merge & │ ◀── Coordinate shared resources │
│ │Coordinate│ │
│ └──────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
```
### 4.2 Demand-Based Decomposition
**Configuration Options:**
- **PartitionStrategy**: ByPriority | ByCustomerTier | ByDueDate | ByProductFamily | Custom
- **SolveOrder**: Sequential | Parallel | Prioritized
- **CrossPartitionHandling**: FixPreviousPartition | SoftPenalty | Coordinate
### 4.3 Supply-Based Decomposition
**Configuration Options:**
- **BottleneckDetection**: ByUtilization | ByContention | Explicit | Combined
- **SolveOrder**: BottleneckFirst | UpstreamFirst | DownstreamFirst | TopologicalSort
- **Propagation**: How downstream resources are updated
### 4.4 Hybrid Decomposition
Combines multiple strategies:
- **Primary**: Determines main partitions (e.g., Time-based)
- **Secondary**: Applied within each partition (e.g., Demand-based)
- **Coordination**: LagrangianRelaxation | FixAndOptimize | MergeWithConflictResolution
---
## 5. Heuristic Approaches
### 5.1 Why Heuristics?
| Benefit | Description |
| ------------------ | ------------------------------------------------------------- |
| **Speed** | Get feasible solution in seconds, not hours |
| **Warm Start** | Provides starting point for optimization |
| **Fallback** | If optimization times out, heuristic solution is still usable |
| **Explainability** | Simple rules are easier to explain |
| **Incremental** | Easy to update for small changes |
### 5.2 Heuristic Types
**Constructive Heuristics** (build solution from scratch):
- **GreedyByPriority**: Allocate demands in priority order
- **EarliestDueDate**: Allocate by earliest due date first
- **ShortestProcessingTime**: Allocate by shortest processing time
- **CriticalRatio**: Allocate by due date / processing time ratio
- **RuleBased**: Custom allocation rules
**Improvement Heuristics** (improve existing solution):
- **PairwiseSwap**: Swap allocations between demands
- **TimeShift**: Move allocations to different buckets
- **RoutingSwitch**: Change routing selections
- **LocalSearch**: Neighborhood-based improvement
### 5.3 Greedy Allocation Algorithm
```
FOR each demand (sorted by priority DESC, due date ASC):
1. Check inventory availability
2. Find production options (routing + earliest bucket)
3. Find transport options (leg + earliest bucket)
4. Score each option (cost, lateness, preference)
5. Select best feasible option
6. Update remaining capacity and inventory
IF no feasible option:
Add demand to shortfall list
```
### 5.4 Local Search Algorithm
```
WHILE improved AND iterations < max:
FOR each neighbor in neighborhood:
IF feasible(neighbor) AND cost(neighbor) < cost(current):
current = neighbor
improved = true
BREAK (first improvement)
```
---
## 6. Google OR-Tools Integration
### 6.1 OR-Tools Solver Options
| Solver | Use Case | Strengths |
| ---------- | ------------------------------------ | -------------------------------------------------------------- |
| **CP-SAT** | Combinatorial, sequencing, campaigns | Fast for discrete problems, good at finding feasible solutions |
| **GLOP** | LP relaxation, continuous variables | Fast for pure LP |
| **SCIP** | Mixed-integer programming | Good for MIP with many continuous variables |
| **PDLP** | Large-scale LP | Handles very large LPs |
**Recommendation for Medhāvī:**
- **Primary**: CP-SAT for the main optimization
- **Secondary**: GLOP for LP relaxations and bounding
### 6.2 Solver Abstraction Layer
The `ISolver` interface decouples business logic from OR-Tools specifics:
```
┌─────────────────────────────────────────────────────────────────┐
│ ISolver Interface │
├─────────────────────────────────────────────────────────────────┤
│ AddVariable(name, lb, ub) → VarRef │
│ AddBinaryVariable(name) → VarRef │
│ AddIntegerVariable(name, lb, ub) → VarRef │
│ AddConstraint(expr, sense, rhs) │
│ SetObjective(expr, minimize/maximize) │
│ AddHint(varRef, value) │
│ Solve(config) → SolveResult │
│ GetValue(varRef) → decimal │
│ Reset() │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ OrToolsSolver (Implementation) │
├─────────────────────────────────────────────────────────────────┤
│ - Wraps Google.OrTools.Sat.CpModel │
│ - Handles decimal scaling (×1000 for integer representation) │
│ - Translates LinearExpr to OR-Tools LinearExpr │
│ - Configures CpSolver parameters from SolverConfig │
│ - Extracts solution values with unscaling │
└─────────────────────────────────────────────────────────────────┘
```
### 6.3 Decimal Scaling Strategy
CP-SAT works with integers only. We use a scaling factor:
```
SCALE_FACTOR = 1000
Encoding: business_value × SCALE_FACTOR → integer_for_solver
Decoding: integer_from_solver ÷ SCALE_FACTOR → business_value
Example:
Quantity 123.456 → Scaled to 123456 → Solver works with 123456
Solver returns 123456 → Unscaled to 123.456
```
### 6.4 Solution Hints (Warm Start)
Warm starts significantly improve solver performance:
```
┌─────────────────────────────────────────────────────────────────┐
│ WARM START FLOW │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 1. HEURISTIC produces initial solution │
│ └── Fulfillment, Production, Assignment values │
│ │
│ 2. HINT INJECTION into CP-SAT model │
│ └── model.AddHint(variable, heuristic_value) │
│ │
│ 3. SOLVER uses hints as starting point │
│ └── Faster feasibility, better initial bound │
│ │
│ 4. PREVIOUS ITERATION hints next iteration │
│ └── Lock satisfied demands, hint remaining │
│ │
└─────────────────────────────────────────────────────────────────┘
```
### 6.5 Solver Configuration
Key `SolverConfig` parameters:
| Parameter | Purpose | Default |
| -------------------- | -------------------------------------------- | ----------- |
| `MaxTimeSeconds` | Time limit for solver | 300 |
| `NumWorkers` | Parallel threads for solving | CPU count |
| `RelativeGapLimit` | Stop when gap below threshold | 0.01 (1%) |
| `RandomSeed` | For reproducibility | 42 |
| `EnablePresolve` | CP-SAT preprocessing | true |
| `EnableLogging` | Solver output logging | false |
### 6.6 Incremental Solving
For small changes, avoid full model rebuild:
```
┌─────────────────────────────────────────────────────────────────┐
│ INCREMENTAL SOLVING │
├─────────────────────────────────────────────────────────────────┤
│ │
│ SMALL CHANGE (< 5% demands changed): │
│ ───────────────────────────────────── │
│ 1. Keep cached model structure │
│ 2. Update only changed constraints │
│ 3. Filter previous solution (remove changed parts) │
│ 4. Use filtered solution as hint │
│ 5. Solve with shorter time limit │
│ │
│ LARGE CHANGE (≥ 5% demands changed): │
│ ───────────────────────────────────── │
│ 1. Full model rebuild │
│ 2. Use previous solution as hint (if compatible) │
│ 3. Solve with full time budget │
│ │
└─────────────────────────────────────────────────────────────────┘
```
---
## 7. Iterative Optimization Framework
This section describes how the OptimizationStep internally manages multiple iterations to handle large-scale problems efficiently.
### 7.1 Core Concept: Unified Workflow
**Key Design Decision**: Monolithic and iterative modes use the **same workflow**. Monolithic is simply a single iteration.
```
┌─────────────────────────────────────────────────────────────────┐
│ UNIFIED ITERATION MODEL │
├─────────────────────────────────────────────────────────────────┤
│ │
│ MONOLITHIC MODE: │
│ ───────────────── │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Iteration 1: ALL demands → Solve → Done │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │
│ ITERATIVE MODE: │
│ ───────────────── │
│ ┌────────────┐ ┌────────────┐ ┌────────────┐ ┌────────────┐│
│ │ Iteration 1│→│ Iteration 2│→│ Iteration 3│→...→│ Iteration N││
│ │ (50 demands│ │ (50 demands│ │ (45 demands│ │ (remaining)││
│ └────────────┘ └────────────┘ └────────────┘ └────────────┘│
│ │
│ SAME CODE PATH - DIFFERENT CONFIGURATION │
│ │
└─────────────────────────────────────────────────────────────────┘
```
### 7.2 Iteration Lifecycle
Each iteration follows a consistent lifecycle:
```
┌─────────────────────────────────────────────────────────────────┐
│ ITERATION LIFECYCLE │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ 1. DEMAND SELECTION │ │
│ │ - Select batch from unlocked demands │ │
│ │ - Apply clustering (resource, product, geography) │ │
│ │ - Ensure coverage guarantee │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ 2. SNAPSHOT "BEFORE" │ │
│ │ - Capture current KPI scorecard │ │
│ │ - Record solver state (variables, constraints) │ │
│ │ - Snapshot constraint violations │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ 3. BUILD & SOLVE │ │
│ │ - Build model for selected demands │ │
│ │ - Fix locked demand variables │ │
│ │ - Add solution hints from previous iteration │ │
│ │ - Execute solver with time allocation │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ 4. SNAPSHOT "AFTER" │ │
│ │ - Capture updated KPI scorecard │ │
│ │ - Record solver metrics (gap, iterations, time) │ │
│ │ - Calculate delta from "before" │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ 5. LOCKING DECISION │ │
│ │ - Evaluate each demand outcome │ │
│ │ - Lock demands meeting satisfaction criteria │ │
│ │ - Update tracking state │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ 6. CHECK STOPPING CRITERIA │ │
│ │ - Time budget exhausted? │ │
│ │ - All demands satisfied? │ │
│ │ - Convergence achieved? │ │
│ │ - Max iterations reached? │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │ │
│ ┌───────────────┴───────────────┐ │
│ ▼ ▼ │
│ ┌──────────┐ ┌──────────┐ │
│ │ CONTINUE │ │ STOP │ │
│ │ Next │ │ Return │ │
│ │ Iteration│ │ History │ │
│ └──────────┘ └──────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
```
### 7.3 Demand Selection Strategy: Adaptive-First
**Key Design Decision**: Use adaptive selection from the start, not priority-based phases.
**Rationale**:
- Priority is encoded in the **objective function** (penalty weights), not iteration order
- The optimizer naturally prioritizes important demands through higher shortfall/lateness penalties
- Iteration selection focuses on **solver efficiency** (clustering, coverage)
```
┌─────────────────────────────────────────────────────────────────┐
│ ADAPTIVE SELECTION WITH COVERAGE GUARANTEE │
├─────────────────────────────────────────────────────────────────┤
│ │
│ PRIORITY HANDLING: │
│ ────────────────── │
│ NOT controlled by iteration order │
│ INSTEAD encoded in objective function weights: │
│ │
│ High Priority Demand: ShortfallPenalty = 10 × BaseValue │
│ Low Priority Demand: ShortfallPenalty = 1 × BaseValue │
│ │
│ → Solver NATURALLY prioritizes important demands │
│ │
├─────────────────────────────────────────────────────────────────┤
│ │
│ SELECTION CRITERIA (for solver efficiency): │
│ ──────────────────────────────────────────── │
│ │
│ 1. PROBLEM SCORE (40%) │
│ - Demands with current shortfall │
│ - Demands with lateness │
│ - Demands with high-cost allocation │
│ - Demands that are unstable (keep changing) │
│ │
│ 2. CLUSTER AFFINITY (30%) │
│ - Group demands sharing resources │
│ - Group demands in same time period │
│ - Group demands in same product family │
│ - Group demands in same geography │
│ │
│ 3. COVERAGE URGENCY (30%) │
│ - How long since demand was last visited │
│ - Guarantees every demand seen within N iterations │
│ │
│ COMBINED SCORE = 0.4×Problem + 0.3×Cluster + 0.3×Coverage │
│ │
└─────────────────────────────────────────────────────────────────┘
```
### 7.4 Demand Selection Algorithm
```
┌─────────────────────────────────────────────────────────────────┐
│ SELECTION ALGORITHM │
├─────────────────────────────────────────────────────────────────┤
│ │
│ INPUT: All demands, tracking state, current solution │
│ OUTPUT: Selected demand IDs for this iteration │
│ │
│ 1. FILTER: Remove locked demands │
│ candidates = demands.filter(not locked) │
│ │
│ 2. SCORE: Calculate combined score for each candidate │
│ FOR each candidate: │
│ problemScore = calcProblemScore(candidate, solution) │
│ clusterScore = calcClusterAffinity(candidate, candidates) │
│ coverageScore = calcCoverageUrgency(candidate, state) │
│ combined = 0.4×problem + 0.3×cluster + 0.3×coverage │
│ │
│ 3. MUST-INCLUDE: Demands with high coverage urgency │
│ mustInclude = candidates.filter(coverageScore > 0.9) │
│ .take(BatchSize × MinNewRatio) │
│ │
│ 4. FILL: Top-scored candidates for remaining slots │
│ remaining = BatchSize - mustInclude.count │
│ topScored = candidates.except(mustInclude) │
│ .sortByDescending(combined) │
│ .take(remaining) │
│ │
│ 5. CLUSTER: Optimize grouping for solver efficiency │
│ selected = mustInclude + topScored │
│ clustered = applyClusterOptimization(selected) │
│ │
│ RETURN clustered.map(d => d.Id) │
│ │
└─────────────────────────────────────────────────────────────────┘
```
### 7.5 Locking Mechanism
Locking prevents re-optimization of satisfied demands:
```
┌─────────────────────────────────────────────────────────────────┐
│ LOCKING MECHANISM │
├─────────────────────────────────────────────────────────────────┤
│ │
│ LOCKING CRITERIA (configurable): │
│ ───────────────────────────────── │
│ A demand is locked when ALL of: │
│ ✓ Fully satisfied (quantity met) │
│ ✓ On-time (no lateness) OR acceptable lateness │
│ ✓ Allocation stable for N iterations │
│ ✓ Within cost threshold │
│ │
│ LOCKING EFFECTS: │
│ ───────────────── │
│ - Excluded from demand selection in future iterations │
│ - Variables FIXED in model (not re-optimized) │
│ - Resources consumed are committed │
│ - Can be unlocked if external changes occur │
│ │
│ LOCKING MODES: │
│ ─────────────── │
│ Strict: Lock only perfect outcomes │
│ Normal: Lock good-enough outcomes (configurable threshold) │
│ Relaxed: Lock any feasible allocation │
│ │
│ UNLOCKING TRIGGERS: │
│ ──────────────────── │
│ - Capacity change affecting locked demand's allocation │
│ - New higher-priority demand competing for resources │
│ - Explicit user request │
│ │
└─────────────────────────────────────────────────────────────────┘
```
### 7.6 Stopping Criteria
Multiple criteria can trigger iteration termination:
```
┌─────────────────────────────────────────────────────────────────┐
│ STOPPING CRITERIA │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────┐│
│ │ TIME BUDGET ││
│ │ ─────────── ││
│ │ Stop when: elapsed >= maxTime ││
│ │ Complete current iteration, then stop ││
│ │ Example: "Run for at most 5 minutes" ││
│ └─────────────────────────────────────────────────────────────┘│
│ │
│ ┌─────────────────────────────────────────────────────────────┐│
│ │ ITERATION COUNT ││
│ │ ─────────────── ││
│ │ Stop when: iterations >= maxIterations ││
│ │ Example: "Run at most 20 iterations" ││
│ └─────────────────────────────────────────────────────────────┘│
│ │
│ ┌─────────────────────────────────────────────────────────────┐│
│ │ DEMAND COVERAGE ││
│ │ ─────────────── ││
│ │ Stop when: lockedDemands / totalDemands >= threshold ││
│ │ Example: "Stop when 95% demands are satisfied" ││
│ └─────────────────────────────────────────────────────────────┘│
│ │
│ ┌─────────────────────────────────────────────────────────────┐│
│ │ OBJECTIVE CONVERGENCE ││
│ │ ───────────────────── ││
│ │ Stop when: |obj[n] - obj[n-k]| / obj[n-k] < threshold ││
│ │ Example: "Stop when improvement < 0.1% over last 3 iters" ││
│ └─────────────────────────────────────────────────────────────┘│
│ │
│ ┌─────────────────────────────────────────────────────────────┐│
│ │ SOLUTION STABILITY ││
│ │ ────────────────── ││
│ │ Stop when: changes between iterations < threshold ││
│ │ Example: "Stop when < 5 demands change allocation" ││
│ └─────────────────────────────────────────────────────────────┘│
│ │
│ EVALUATION: Check all criteria after each iteration │
│ PRIORITY: First criterion met triggers stop │
│ GRACEFUL: Complete current iteration before stopping │
│ │
└─────────────────────────────────────────────────────────────────┘
```
### 7.7 KPI Integration and Snapshots
**Key Design Decision**: Compose with existing `KPIs` module - don't duplicate.
```
┌─────────────────────────────────────────────────────────────────┐
│ KPI SNAPSHOT ARCHITECTURE │
├─────────────────────────────────────────────────────────────────┤
│ │
│ COMPOSITION WITH KPIs MODULE: │
│ ───────────────────────────── │
│ │
│ ┌─────────────────────┐ │
│ │ KPIs.Types.fs │ ◄── Existing module │
│ │ - KPIScorecard │ │
│ │ - KPIValue │ │
│ │ - KPITrend │ │
│ └──────────┬──────────┘ │
│ │ │
│ │ COMPOSED INTO │
│ ▼ │
│ ┌─────────────────────┐ │
│ │ IterationSnapshot │ ◄── New type │
│ │ - BusinessKPIs: KPIScorecard (from KPIs module) │
│ │ - SolverSnapshot: {...} (optimizer-specific) │
│ │ - ConstraintSnapshot: {...} (violation tracking) │
│ └──────────┬──────────┘ │
│ │ │
│ │ USED BY │
│ ▼ │
│ ┌─────────────────────┐ │
│ │ Iteration │ │
│ │ - Before: IterationSnapshot │
│ │ - After: IterationSnapshot │
│ │ - Delta: IterationDelta │
│ └─────────────────────┘ │
│ │
├─────────────────────────────────────────────────────────────────┤
│ │
│ SNAPSHOT CONTENTS: │
│ ────────────────── │
│ │
│ BusinessKPIs (from KPIs module): │
│ - OTD (On-Time Delivery) │
│ - OTIF (On-Time In-Full) │
│ - Fill Rate │
│ - Capacity Utilization │
│ - Inventory Turnover │
│ - Plan Stability │
│ │
│ SolverSnapshot (optimizer-specific): │
│ - Objective Value │
│ - Gap (relative optimality) │
│ - Variables count │
│ - Constraints count │
│ - Solve time │
│ - Solver status │
│ │
│ ConstraintSnapshot: │
│ - Hard violations count │
│ - Soft violations (slack usage) │
│ - Capacity overloads │
│ - Inventory excesses │
│ │
└─────────────────────────────────────────────────────────────────┘
```
### 7.8 Iteration Delta Calculation
Track changes between before and after:
```
┌─────────────────────────────────────────────────────────────────┐
│ DELTA CALCULATION │
├─────────────────────────────────────────────────────────────────┤
│ │
│ FOR each KPI: │
│ delta.value = after.value - before.value │
│ delta.percentChange = (after - before) / before × 100 │
│ delta.trend = calculateTrend(before, after) ◄── Reuse from │
│ KPIs.Types │
│ │
│ TREND VALUES (from KPIs module): │
│ Improving - KPI moving toward target │
│ Declining - KPI moving away from target │
│ Stable - KPI within tolerance │
│ Unknown - Insufficient data │
│ │
│ AGGREGATE METRICS: │
│ - Demands improved: count where outcome better │
│ - Demands degraded: count where outcome worse │
│ - Net improvement: improved - degraded │
│ - Objective improvement: obj_before - obj_after │
│ │
└─────────────────────────────────────────────────────────────────┘
```
### 7.9 Iteration History and Summary
Aggregate all iterations for the step:
```
┌─────────────────────────────────────────────────────────────────┐
│ ITERATION HISTORY │
├─────────────────────────────────────────────────────────────────┤
│ │
│ IterationHistory: │
│ - Iterations: list of all iteration records │
│ - Summary: aggregated statistics │
│ - StoppingReason: why iterations stopped │
│ - TotalDuration: sum of all iteration times │
│ │
│ Summary Contents: │
│ - Total iterations run │
│ - Demands covered (visited at least once) │
│ - Demands locked (satisfied) │
│ - Average iteration time │
│ - Objective trajectory (first → last) │
│ - Overall KPI trend │
│ - Convergence rate │
│ │
│ VISUALIZATION DATA: │
│ ──────────────────── │
│ The history enables: │
│ - Progress charts (objective over iterations) │
│ - KPI dashboards (before/after comparisons) │
│ - Convergence graphs (gap over time) │
│ - Demand coverage heatmaps │
│ │
└─────────────────────────────────────────────────────────────────┘
```
### 7.10 Time Allocation Strategy
Distribute time budget across iterations:
```
┌─────────────────────────────────────────────────────────────────┐
│ TIME ALLOCATION STRATEGIES │
├─────────────────────────────────────────────────────────────────┤
│ │
│ EQUAL DISTRIBUTION: │
│ ─────────────────── │
│ Each iteration gets: totalTime / expectedIterations │
│ Simple, predictable │
│ │
│ FRONT-LOADED: │
│ ───────────── │
│ Early iterations get more time (establishing good base) │
│ Later iterations get less time (refinement) │
│ Example: 40%, 30%, 20%, 10% distribution │
│ │
│ ADAPTIVE: │
│ ───────── │
│ Time based on iteration difficulty: │
│ - More time for iterations with problematic demands │
│ - Less time for iterations with easy demands │
│ - Adjust based on previous iteration's gap │
│ │
│ REMAINING-BUDGET: │
│ ───────────────── │
│ Each iteration gets: remainingTime / remainingIterations │
│ Self-adjusting as iterations complete │
│ │
│ RECOMMENDED: Remaining-Budget with minimum per iteration │
│ │
└─────────────────────────────────────────────────────────────────┘
```
### 7.11 Configuration Types
```
┌─────────────────────────────────────────────────────────────────┐
│ ITERATION CONFIGURATION TYPES │
├─────────────────────────────────────────────────────────────────┤
│ │
│ IterationConfig: │
│ - BatchSize: int (demands per iteration) │
│ - MaxIterations: int (upper limit) │
│ - TimeBudget: TimeSpan (total time allowed) │
│ - MinTimePerIteration: TimeSpan │
│ - TimeAllocation: TimeAllocationStrategy │
│ - StoppingCriteria: StoppingCriteria list │
│ - SelectionConfig: DemandSelectionConfig │
│ - LockingConfig: LockingConfig │
│ │
│ DemandSelectionConfig: │
│ - ClusteringConfig: which dimensions to cluster │
│ - CoverageGuarantee: max iterations without visit │
│ - ProblemFocusWeights: shortfall, lateness, cost, instability │
│ │
│ LockingConfig: │
│ - Mode: Strict | Normal | Relaxed │
│ - SatisfactionThreshold: decimal │
│ - StabilityWindow: int (iterations) │
│ - AllowUnlock: bool │
│ │
│ DEFAULT FOR MONOLITHIC: │
│ BatchSize = int.MaxValue │
│ MaxIterations = 1 │
│ → Single iteration with all demands │
│ │
└─────────────────────────────────────────────────────────────────┘
```
---
## 8. F# Implementation Design
### 8.1 Project Structure
```
Medhavi.Planning/
├── Optimizer/
│ ├── Core/
│ │ ├── Types.fs # Core types (Optimizer, Run, Step)
│ │ ├── Contracts.fs # Interfaces (ISolver, IOptimizationStep)
│ │ ├── IterationTypes.fs # Iteration, Snapshot, Delta types
│ │ └── Pipeline.fs # Pipeline execution logic
│ │
│ ├── Decomposition/
│ │ ├── AnchorSelection.fs # Anchor selection strategies
│ │ ├── DemandBased.fs # Demand partitioning
│ │ ├── SupplyBased.fs # Supply/resource partitioning
│ │ └── Hybrid.fs # Combined strategies
│ │
│ ├── Heuristics/
│ │ ├── GreedyAllocation.fs # Greedy demand allocation
│ │ ├── LocalSearch.fs # Local improvement
│ │ └── Constructive.fs # Constructive heuristics
│ │
│ ├── Solver/
│ │ ├── OrToolsSolver.fs # OR-Tools CP-SAT implementation
│ │ └── SolverFactory.fs # Solver creation
│ │
│ ├── Model/
│ │ ├── Variables.fs # Decision variable definitions
│ │ ├── Constraints.fs # Constraint building
│ │ ├── Objective.fs # Objective function
│ │ └── SolutionExtractor.fs # Extract solution from solver
│ │
│ ├── Steps/
│ │ ├── PreprocessingStep.fs # Data validation, normalization
│ │ ├── HeuristicStep.fs # Heuristic solving
│ │ ├── OptimizationStep.fs # OR-Tools optimization (with iterations)
│ │ ├── RepairStep.fs # Constraint repair
│ │ └── PostprocessStep.fs # Output generation
│ │
│ ├── Iteration/
│ │ ├── DemandSelection.fs # Adaptive demand selection
│ │ ├── Locking.fs # Demand locking logic
│ │ ├── Snapshots.fs # KPI snapshot building
│ │ └── StoppingCriteria.fs # Stopping condition evaluation
│ │
│ └── Coordination/
│ ├── SubproblemManager.fs # Manage parallel subproblems
│ └── SolutionMerger.fs # Merge solutions
```
### 8.2 Key Interfaces
**ISolver** - Solver abstraction:
- `AddVariable`, `AddBinaryVariable`, `AddIntegerVariable`
- `AddConstraint`, `SetObjective`
- `AddHint`, `Solve`, `GetValue`, `Reset`
**IOptimizationStep** - Pipeline step:
- `Name`, `Description`, `EstimatedTime`
- `Execute(context)`, `CanSkip(context)`, `Validate(context)`
**IDecompositionStrategy** - Decomposition:
- `Partition(input)`, `GetSolveOrder(partitions)`, `Merge(solutions)`
### 8.3 Key Type Definitions
**Core Types:**
- `OptimizerId`, `RunId`, `StepId`, `IterationId` - Strong-typed identifiers
- `Bucket` - Time period (0-based index)
- `SolverConfig` - Solver parameters
- `OptimizerConfig` - Overall optimizer settings
**Iteration Types:**
- `IterationScope` - Demands, time range, fixed decisions for iteration
- `IterationSnapshot` - KPIs + solver state + constraints at a point
- `IterationDelta` - Changes between before/after
- `Iteration` - Complete iteration record
- `IterationHistory` - All iterations with summary
**Result Types:**
- `SolveResult` - Status, objective, gap, solution
- `StepResult` - Solution, artifacts, metrics, limiters
- `OptimizationResult` - Final solution, KPIs, execution log
### 8.4 Integration with KPIs Module
The iteration framework composes with existing KPIs:
```
┌─────────────────────────────────────────────────────────────────┐
│ MODULE COMPOSITION │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Medhavi.Planning/KPIs/ │
│ ├── Types.fs → KPIScorecard, KPIValue, KPITrend │
│ ├── Calculators.fs → IKPICalculator │
│ └── OptimizerIntegration.fs → calculateObjectiveScore │
│ │ │
│ │ USED BY │
│ ▼ │
│ Medhavi.Planning/Optimizer/ │
│ ├── Iteration/Snapshots.fs │
│ │ └── buildSnapshot calls IKPICalculator.GetScorecard │
│ │ │
│ ├── Model/Objective.fs │
│ │ └── buildObjective calls OptimizerIntegration functions │
│ │ │
│ └── Steps/PostprocessStep.fs │
│ └── generateKPIs calls IKPICalculator │
│ │
│ PRINCIPLE: No KPI logic duplication │
│ Optimizer uses KPIs module for all KPI calculations │
│ │
└─────────────────────────────────────────────────────────────────┘
```
---
## 9. Execution Modes
### 9.1 Mode Comparison
| Mode | Pipeline | Iterations | Time | Use Case |
| --------------- | -------------------------------------------- | ---------- | ----- | ------------------------------ |
| **Light** | Preprocess → Heuristic → Postprocess | 0 | <30s | Real-time ATP, quick estimates |
| **Standard** | Full pipeline with limited optimization | 1-5 | <5min | Daily planning, what-if |
| **Full** | Complete pipeline with extensive iteration | 10-50+ | Hours | Nightly batch, comprehensive |
| **WhatIf** | Same as Standard, but no commit | 1-5 | <5min | Scenario analysis |
| **Incremental** | Diff-based, reuse previous | 1-3 | <1min | Small changes |
### 9.2 Mode-Specific Iteration Settings
| Mode | BatchSize | MaxIterations | TimeBudget | Locking |
| ----------- | -------------- | ------------- | ---------- | ----------- |
| Light | N/A | N/A | 30s | N/A |
| Standard | All demands | 1 | 5min | None |
| Full | 50-100 | 50 | 2-4 hours | Normal |
| Incremental | Changed only | 3 | 1min | Strict |
### 9.3 What-If Mode
For scenario analysis:
- Run optimization without committing results
- Compare multiple scenarios in parallel
- Generate comparison reports
---
## 10. Performance & Scaling
### 10.1 Parallelization Opportunities
```
┌─────────────────────────────────────────────────────────────────┐
│ PARALLELIZATION POINTS │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 1. SUBPROBLEM SOLVING (when decomposed) │
│ Independent partitions can solve in parallel │
│ │
│ 2. PREPROCESSING │
│ Validate different entity types concurrently │
│ │
│ 3. POSTPROCESSING │
│ Generate different artifacts in parallel │
│ │
│ 4. WHAT-IF SCENARIOS │
│ Run multiple scenarios concurrently │
│ │
│ 5. KPI CALCULATION │
│ Different KPI categories can compute in parallel │
│ │
│ NOTE: Iterations within a step are SEQUENTIAL │
│ (each depends on previous iteration's locking decisions) │
│ │
└─────────────────────────────────────────────────────────────────┘
```
### 10.2 Checkpointing
Checkpoints enable resume capability:
- Save after each step completion
- Save after configurable number of iterations
- Store: RunId, StepIndex, IterationIndex, Context, Solution
### 10.3 Scaling Guidelines
| Problem Size | Recommendation |
| ---------------- | ------------------------------------------------- |
| <10K demands | Monolithic (single iteration) |
| 10K-50K demands | Iterative with 100-demand batches |
| 50K-200K demands | Iterative + time-based decomposition |
| >200K demands | Hybrid decomposition + aggressive aggregation |
---
## 11. Self-Review & Recommendations
### 11.1 Architecture Completeness
| Aspect | Status | Notes |
| ---------------------------- | ------ | ------------------------------------------ |
| Component hierarchy | ✅ | Optimizer → Run → Steps → Iterations |
| Decomposition strategies | ✅ | Demand, supply, time, hybrid |
| Heuristics | ✅ | Greedy, local search |
| OR-Tools integration | ✅ | CP-SAT with abstraction layer |
| Iterative optimization | ✅ | Unified workflow, adaptive selection |
| KPI integration | ✅ | Composition with KPIs module |
| Execution modes | ✅ | Light, standard, full, what-if |
| Parallelization | ✅ | Subproblems, preprocessing, postprocessing |
| Checkpointing | ✅ | Resume capability |
### 11.2 Key Design Decisions Summary
| Decision | Rationale |
| ------------------------------ | --------------------------------------------------- |
| Unified iteration workflow | Monolithic = single iteration, no code branching |
| Adaptive-first selection | Priority via objective weights, not iteration order |
| Compose with KPIs module | No duplication, single source of truth for KPIs |
| Solver abstraction (ISolver) | Decouple from OR-Tools specifics |
| Locking satisfied demands | Reduce problem size progressively |
| Coverage guarantee | Ensure all demands get attention |
| Time allocation strategies | Flexible budget distribution across iterations |
### 11.3 Implementation Phases
**Phase 1 (MVP):**
- Standard mode with monolithic iteration
- Basic OR-Tools integration
- Heuristic warm start
**Phase 2:**
- Iterative mode with demand batching
- Adaptive selection algorithm
- Locking mechanism
**Phase 3:**
- Full KPI snapshot integration
- Multiple stopping criteria
- Iteration history visualization
**Phase 4:**
- Decomposition strategies
- Parallel subproblem solving
- Advanced time allocation
**Phase 5:**
- Incremental solving
- Checkpointing and resume
- ML-based selection tuning
---
## 12. Open Questions
| Question | Options | Recommendation |
| -------------------------- | ----------------------------- | ------------------------------------------ |
| Default batch size | 50 / 100 / 200 | 100 (balance between granularity and overhead) |
| Default locking mode | Strict / Normal / Relaxed | Normal (practical for most scenarios) |
| Coverage guarantee window | 5 / 10 / 20 iterations | 10 (ensure timely attention) |
| Time allocation default | Equal / Adaptive / Remaining | Remaining-Budget (self-adjusting) |
| Clustering dimensions | All / Resource-only / Custom | Resource + Time (most impactful) |
---
## 13. Glossary
| Term | Definition |
| ------------------- | ------------------------------------------------------------ |
| **Anchor** | Primary entity around which subproblems are partitioned |
| **Iteration** | Single solver execution on a demand subset |
| **Locking** | Fixing satisfied demand allocations for future iterations |
| **Snapshot** | Point-in-time capture of KPIs, solver state, constraints |
| **Coverage** | Ensuring all demands are visited within a window |
| **Warm Start** | Using previous solution as hint for solver |
| **Batch** | Set of demands selected for an iteration |
| **Gap** | Relative difference between current and optimal objective |
---
## 14. Changelog
| Date | Version | Changes |
| ---------- | ------- | ------------------------------------------------------ |
| 2026-01-05 | 1.0 | Initial architecture design |
| 2026-01-06 | 2.0 | Added iterative optimization framework (§7) |
| | | Updated unified workflow design |
| | | Added adaptive-first demand selection |
| | | Added KPI integration via composition |
| | | Added locking mechanism |
| | | Added stopping criteria |
| | | Reduced code examples, focused on architecture |
---
## 15. References
- `documents/Optimizer.md` — Mathematical model specification
- `documents/Planning.md` — Functional requirements
- `src/Server/Medhavi.Planning/KPIs/` — KPI module for composition
- `src/Server/Medhavi.Planning/Optimizer/` — Implementation
- Google OR-Tools documentation — CP-SAT, GLOP
## Medhāvī: AI-First Supply Chain Platform
**Medhāvī** is an intelligent, event-driven supply chain orchestration platform that leverages artificial intelligence, digital twins, and real-time analytics to provide end-to-end supply chain management for manufacturing companies.
## Augmented Intelligence Philosophy
Medhāvī emphasizes human-centric AI. Routine monitoring and recommendations are automated, while strategic decisions remain with humans. AI assistants provide contextual recommendations and clear explanations, with all critical actions human-validated. This aligns with trends of responsible AI and human-on-the-loop design.
---
### 1. Nexus (Operational Control Tower)
#### Vision:
- **Real-Time Operational Intelligence**: Sub-second visibility into supply chain operations
- **Event Orchestration Hub**: Central coordination point for all supply chain events
- **Master Data Enrichment**: Add contextual data to events for downstream planning systems
- **Operational Control Tower**: Unified dashboard for supply chain operations
- **Industry 4.0 Orchestration**: Smart factory coordination and real-time monitoring
**Architectural Note**: Nexus is the **Speed Layer** (operational, real-time) optimized for "What is happening now?" For historical analysis, ML model training, and deep analytics, see **Analytics Engine** (Batch Layer).
#### Key Features:
##### Event Processing & Enrichment
- **Event Ingestion**: Consume normalized events from Integrator via EventStoreDB
- **Master Data Enrichment**: Add product, BOM, routing, and capacity data to events
- **Event Routing**: Distribute enriched events to ProductionPlanning, DemandPlanner, Analytics Engine
- **Event Correlation**: Real-time pattern recognition across event streams
- **Event Quality Scoring**: Assess event completeness and accuracy in real-time
##### Real-Time Operational Intelligence
- **Real-Time KPI Calculation**: Streaming analytics with sub-second metric updates (operational KPIs)
- **Digital Twin Management**: Live, multi-layer digital twin of the supply chain (network, inventory, processes)
- **Predictive Alerting**: ML-based anomaly detection and early warning of disruptions (#PredictiveQuality)
- **Operational Dashboards**: Real-time visibility into supply chain health and performance
- **System Health Monitoring**: Component status, performance metrics, and availability tracking
##### Autonomous Operations
- **Self-Healing Workflows**: Automated disruption responses (reroute shipments, reschedule production)
- **Automated Exception Management**: Smart routing, escalation and automated resolution workflows
- **Cross-System Orchestration**: AI-coordinated API workflows and autonomous actions (#GenAI, #DemandSensing)
- **Autonomous Decision Making**: ML agents execute high-confidence operational tasks (under human oversight)
- **Multi-Agent Orchestration**: Coordinated AI agents handle complex tasks (e.g. autonomous procurement and fulfillment agents) (#GenAI)
##### Operational AI Features (Real-Time)
- **Real-Time Risk Scoring**: AI-scores supply-chain risks (geo-political, market) for prioritization
- **Predictive Maintenance Alerts**: Equipment failure prediction using IoT data (served by Analytics Engine models)
- **Supplier Performance Monitoring**: Real-time supplier reliability, quality, and risk tracking
- **Carbon Tracking**: Real-time emissions tracking (detailed analysis in Analytics Engine)
- **ESG Scorecards**: Integrated supplier ESG ratings and risk scoring for decision support (#Sustainability)
**Note**: Advanced ML model training, historical analysis, and causal AI root-cause analysis are handled by the **Analytics Engine** (see section 4). Nexus consumes model predictions and serves real-time operational intelligence.
##### Autonomous Operations
- **Self-Healing Workflows**: Automated disruption responses (reroute shipments, reschedule production)
- **Predictive Replanning**: AI-driven contingency plans for supply/demand shocks
- **Autonomous Decision Making**: ML agents execute high-confidence operational tasks (under human oversight)
- **Agentic AI Workflows**: Multi-agent GenAI systems coordinate end-to-end actions (e.g. auto-procurement contracts) (#GenAI)
### 2. ProductionPlanning (Planning Engine)
##### Capacity Planning & Scheduling
- **Finite Capacity Planning**: Constraint-based scheduling (machines, labor, tooling)
- **Campaign Optimization**: Minimize setups and changeovers
- **Multi-Resource Scheduling**: Optimize equipment, workforce, tool usage simultaneously
- **Dynamic Lead Time Management**: Recompute lead times from live data
- **Production Sequencing**: AI-assisted operation sequencing to balance lines
- **Parallel Batch Scheduling**: Optimize concurrent batches for process industries
##### Material Management
- **Advanced MRP**: Multi-level planning with real-time stock updates
- **Material Reservation**: AI-driven supply-demand matching and alerts
- **Lot Size & EOQ Optimization**: Probabilistic models for order quantities
- **Supplier Collaboration**: VMI and consignment inventory management
- **Inventory Optimization**: Multi-echelon inventory balancing with ML
##### Work Order Management
- **Automated Work Order Generation**: AI triggers based on demand signals and constraints
- **Priority-Based Planning**: ML-ranked job prioritization (customer impact, due dates)
- **Resource Allocation**: Intelligent assignment of tasks to machines/operators
- **Production Tracking**: Real-time WIP visibility; dynamic updates on shop-floor
- **Quality Integration**: Embedded QC checks and automated hold release
##### Advanced Optimization
- **Multi-Objective Optimization**: Optimize trade-offs (cost, delivery, carbon, quality)
- **Scenario Planning**: Automated what-if analysis for schedules
- **Robust Optimization**: Plans resilient to disruptions
- **Real-Time Replanning**: Immediate schedule updates on change events
- **Additive Manufacturing Planning**: Optimize 3D print jobs and queue (advanced use case)
##### Predictive Quality & Smart Manufacturing
- **Vision-Based Quality Inspection**: Integrate camera/vision analytics for defect detection on-line (#PredictiveQuality)
- **Sensor-Driven SPC**: Real-time Statistical Process Control with AI anomaly detection (#PredictiveQuality)
- **Predictive Yield Enhancement**: AI-adjustment of process parameters to maximize yield
- **Quality Digital Twin**: Simulate production process for defect prevention
- **Energy-Aware Scheduling**: Schedule considering energy tariffs and consumption (sustainability)
---
### 3. DemandPlanner (Forecasting Intelligence)
#### Vision:
- **GenAI Forecasting**: LLM analysis of market data and natural language queries (e.g. explain a forecast in text) (#GenAI).
- **Real-Time Demand Shaping**: AI-driven dynamic pricing and promotions, autonomous replenishment based on live signals (#DemandSensing)
- **Cross-Channel Sensing**: IoT/POS data integration for near-instant adjustments (aligns with 5G/edge data trends)
- **Sustainability Forecasting: Predict demand for eco-friendly product variants (#Sustainability)
- **Personalization at Scale**: Micro-segmentation forecasting for customized products
- **Digital Shelf Monitoring**: AI monitors shelf/fill-rate via cameras to sense demand
#### Key Features:
##### Statistical Forecasting
- **Machine Learning Forecasting**: Deep learning demand prediction (multivariate models)
- **Multi-Variant Analysis**: Incorporate promotions, price, macro factors
- **Hierarchical Forecasting**: Multi-level (product-location) models
- **Intermittent Demand**: Specialized algorithms for sparse demand
##### S&OP Process Management
- **Consensus Forecasting**: Collaborative planning across finance, marketing, operations
- **Scenario Planning**: Multiple forecast scenarios with LLM narrative summaries (#GenAI)
- **Promotion Impact Analysis**: ML quantification of marketing effects (lift, cannibalization)
- **New Product Forecasting**: AI-driven launch demand estimates (using analogs)
##### Market Intelligence
- **External Data Integration**: Real-time feeds (economic indices, weather, IoT, social media sentiment)
- **Competitor Analysis**: Automated market share & pricing intelligence
- **Customer Segmentation**: ML clustering of demand patterns
- **Trend Analysis**: Long-term pattern detection and anomaly alerts
##### Advanced Analytics
- **Causal Inference**: Explain drivers of demand changes
- **Forecast Value Added**: Track improvements from each planning step
- **Demand Sensing**: Real-time demand signal processing from POS, RFID, IoT sensors (#DemandSensing)
- **Predictive Modeling**: Advanced statistical and ML models
- **Adaptive Forecast Correction**: LLM-mediated forecast adjustments with human input (#GenAI, #DemandSensing)
---
### 4. Analytics Engine (Data Science & Analytics Platform)
#### Vision:
- **Unified Data Lake**: Single source of truth for historical supply chain events
- **ML Operations (MLOps)**: Complete lifecycle management for ML models (training, versioning, serving, monitoring)
- **Feature Store**: Centralized feature management for consistent ML model training and serving
- **Advanced Analytics**: OLAP queries, time-series analysis, and statistical modeling
- **Explainable AI**: Transparent model predictions with causal inference
- **Federated Learning**: Privacy-preserving ML across distributed supply chain sites
#### Key Features:
##### Data Infrastructure
- **Data Lake Architecture**: Historical event storage in Parquet/Delta Lake format for analytical queries
- **Event Stream Ingestion**: Subscribe to all Medhāvī event streams for comprehensive historical analysis
- **Schema Evolution**: Handle schema changes across event versions with backward compatibility
- **Data Partitioning**: Time-based and domain-based partitioning for efficient querying
- **Data Retention Policies**: Configurable retention periods for compliance and cost optimization
- **Data Quality Monitoring**: Automated data quality checks and anomaly detection
##### Machine Learning Operations (MLOps)
- **Model Training Pipeline**: Automated training workflows for forecasting, anomaly detection, and optimization models
- **Model Registry**: Version control and lifecycle management for ML models
- **Model Serving**: Real-time and batch prediction serving via REST APIs
- **A/B Testing Framework**: Compare model performance and gradually roll out new models
- **Model Monitoring**: Detect model drift, performance degradation, and data distribution shifts
- **Experiment Tracking**: Track hyperparameters, metrics, and artifacts for reproducibility
##### Feature Store
- **Feature Definitions**: Centralized feature catalog with versioning
- **Feature Computation**: Batch and streaming feature computation pipelines
- **Feature Serving**: Low-latency feature retrieval for model inference
- **Feature Lineage**: Track feature dependencies and data sources
- **Feature Validation**: Automated validation of feature quality and freshness
##### Advanced Analytics & Data Science
- **OLAP Engine**: Complex analytical queries with sub-second response times (DuckDB, ClickHouse)
- **Time-Series Analysis**: Statistical analysis of temporal patterns and trends
- **Causal Inference**: Explainable root-cause analysis using causal AI techniques
- **Statistical Modeling**: Hypothesis testing, correlation analysis, regression modeling
- **Notebook Integration**: Jupyter/Polyglot Notebooks for data scientist workflows
- **Ad-hoc Query Interface**: SQL and GraphQL interfaces for exploratory analysis
##### Business Intelligence & Reporting
- **BI Dashboards**: Pre-built dashboards for supply chain KPIs and metrics
- **Custom Reports**: Configurable report generation with scheduling
- **Data Visualization**: Interactive charts, graphs, and heat maps
- **Export Capabilities**: Export reports to Excel, PDF, CSV formats
- **Self-Service Analytics**: Business user-friendly query interfaces
##### Model Types & Use Cases
- **Demand Forecasting Models**: Transformer-based, ARIMA, Prophet models for DemandPlanner
- **Anomaly Detection Models**: Isolation Forest, Autoencoders for Nexus alerting
- **Optimization Models**: Reinforcement learning for ProductionPlanning scheduling
- **Quality Prediction Models**: Computer vision models for defect detection
- **Supplier Risk Models**: ML models for ESG scoring and risk assessment
- **Carbon Footprint Models**: Regression models for emissions prediction
##### Integration with Operational Contexts
- **Nexus Integration**:
- Subscribe to enriched events for historical storage
- Serve real-time predictions for anomaly detection
- Provide historical KPIs for operational dashboards
- **ProductionPlanning Integration**:
- Provide historical performance data for optimization algorithms
- Serve capacity prediction models
- Analyze schedule effectiveness over time
- **DemandPlanner Integration**:
- Store historical demand data for model training
- Serve forecast models and predictions
- Analyze forecast accuracy and bias
- **Integrator Integration**:
- Store raw events for complete audit trail
- Analyze data quality trends
- Detect integration issues and anomalies
##### Technology Stack
- **Data Lake**: Delta Lake or Apache Iceberg
- **Query Engine**: DuckDB, ClickHouse, or Apache Spark
- **Feature Store**: Feast, Tecton, or custom implementation
- **ML Platform**: MLflow, Kubeflow, or custom MLOps pipeline
- **Notebooks**: Polyglot Notebooks (.NET), Jupyter
- **Visualization**: Apache Superset, Grafana, or custom dashboards
- **Model Serving**: TorchServe, TensorFlow Serving, or custom API
---
### 5. Medhavi Integrator
#### Vision:
- **5G-Enabled Real-Time Data**: Ultra-low latency sensor data processing.
- **Edge Computing Integration**: Distributed data processing at source
- **Blockchain-Enabled Data Provenance**: Immutable data lineage tracking
- **Conversational Interfaces**: GenAI agents interpret free-text inputs and documents, translating them into events (#GenAI).
#### Key Features:
- **Multi-Source Data Ingestion**: ERP, WMS, MES, IoT, third-party APIs, unstructured data (emails/documents via GenAI parsing) (#GenAI).
- **Event Normalization**: Schema evolution, data transformation, RAG (Retrieval-Augmented Generation) for legacy document ingestion (#GenAI).
- **Real-Time Streaming**: Sub-100 ms event processing, 5G-enabled ultra-low-latency ingestion.
- **Data Quality Assurance**: Automated validation, cleansing, LLM-based anomaly inference (#GenAI, #PredictiveQuality).
- **Master Data Synchronization**: Cross-system data harmonization
- **IoT Sensor Integration**: Edge/IoT data collection from equipment, environment, assets; support for edge AI nodes.
- **API Orchestration**: REST, GraphQL, Webhooks, and GenAI-powered conversational APIs for ad-hoc queries (#GenAI).
- **Event Deduplication**: Two-tier dedup (LRU cache + persistence) for high-throughput messaging.
---
## Future Vision
Medhāvī is architected to evolve into a comprehensive, self-optimizing supply chain intelligence platform. Our future vision encompasses:
### Short-Term (1-2 Years)
- **Complete Bounded Context Separation**: Fully independent, scalable bounded contexts with clear domain boundaries
- **Unified Event Fabric**: Seamless event-driven communication across all contexts via EventStoreDB
- **Operational Excellence**: Sub-200ms event processing, 99.9% availability, zero data loss
- **Analytics Foundation**: Data lake infrastructure for historical analysis and ML model training
### Medium-Term (3-5 Years)
- **Advanced AI Integration**: Production-ready transformer models, causal AI, and federated learning
- **Edge Intelligence**: Distributed AI processing at production sites with minimal latency
- **Autonomous Operations**: Self-healing workflows with 95%+ automated resolution
- **Sustainability Intelligence**: Real-time carbon tracking, circular economy optimization, ESG compliance
### Long-Term (5+ Years)
- **Quantum-Ready Architecture**: Optimization algorithms prepared for quantum computing acceleration
- **Industry 4.0 Complete**: Full digital thread from design to delivery with AR/VR visualization
- **Predictive Supply Chain**: Proactive disruption prevention with 99%+ accuracy
- **Global Scale**: Multi-tenant, multi-region deployment supporting enterprise supply chains worldwide
### Architectural Evolution
The platform follows a **Lambda Architecture** pattern, combining:
- **Speed Layer** (Nexus): Real-time operational intelligence and event orchestration
- **Batch Layer** (Analytics Engine): Historical analysis, ML training, and deep insights
- **Serving Layer**: Unified APIs providing both real-time and analytical views
This dual-layer approach ensures we can answer both "What is happening now?" (operational) and "What happened over time and why?" (analytical) questions simultaneously.
## Solution domains & Architecture
Medhāvī is built on **Domain-Driven Design (DDD)** principles, with each major component forming a distinct **bounded context**. This architectural approach ensures clear solution domain boundaries, independent scalability, and maintainable codebases.
### Architecture Overview
```
┌─────────────────────────────────────────────────────────────────────────┐
│ MEDHĀVĪ PLATFORM │
│ (Event-Driven, CQRS Architecture) │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ External Systems (SAP, WMS, MES, IoT, ERP, PLM) │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────┐ │
│ │ Medhavi.Integrator │ │
│ │ (Integration Layer) │ │
│ └──────────────┬──────────────────────┘ │
│ │ EventStoreDB (External Streams) │
│ ▼ │
│ ┌─────────────────────────────────────┐ │
│ │ Medhāvī Nexus │ │
│ │ (Operational Control Tower) │ │
│ └──────────────┬──────────────────────┘ │
│ │ EventStoreDB (Enriched Streams) │
│ │ │
│ ┌─────────────┬───────────────┬──────────────────┐ │
│ │ │ │ │ │
│ ▼ ▼ ▼ ▼ │
│ ┌──────────┐ ┌──────────┐ ┌──────────────┐ ┌─────────────────────┐ │
│ │Production│ │Demand │ │ Analytics │ │ MES/Shop Floor │ │
│ │Planning │ │Planner │ │ Engine │ │ Systems │ │
│ │(APS) │ │(Forecast)│ │(Data Science)│ │ │ │
│ └──────────┘ └──────────┘ └──────────────┘ └─────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
### Solution Domain Responsibilities
#### 1. Medhavi.Integrator (Integration Layer)
**Role**: External system integration and event normalization
**Responsibilities**:
- **Multi-Source Data Ingestion**: Connect to ERP (SAP, Oracle), WMS, MES, IoT sensors, third-party APIs
- **Event Normalization**: Transform external data formats into standardized Medhāvī event schemas
- **Schema Evolution**: Handle backward-compatible schema changes
- **Data Quality Assurance**: Validate, cleanse, and enrich raw data before publishing
- **Event Publishing**: Write normalized events to EventStoreDB external streams
- **GenAI Document Processing**: Parse unstructured data (emails, documents) using LLMs
**Owns**:
- External system connectors and adapters
- Event schema definitions for external sources
- Data transformation pipelines
- Event deduplication logic
**Communicates Via**:
- Publishes events to EventStoreDB external streams
- Subscribes to no upstream contexts (entry point)
- Downstream: Nexus consumes its events
---
#### 2. Medhāvī Nexus (Operational Control Tower)
**Role**: Real-time event orchestration, enrichment, and operational intelligence
**Responsibilities**:
- **Event Ingestion**: Consume normalized events from Integrator via EventStoreDB
- **Master Data Enrichment**: Add contextual master data (products, BOMs, routings, capacity) to events
- **Event Routing**: Distribute enriched events to appropriate downstream contexts
- **Real-Time Monitoring**: System health, KPIs, and operational dashboards
- **Digital Twin Management**: Maintain live operational state of supply chain
- **Operational Alerting**: Real-time anomaly detection and alerting
- **Control Tower Visibility**: Unified view of supply chain operations
**Owns**:
- Master data repository (in-memory, thread-safe)
- Event enrichment rules and pipeline
- Real-time KPI calculations
- Operational dashboards and monitoring
**Communicates Via**:
- Subscribes to: Integrator events (EventStoreDB external streams)
- Publishes to: ProductionPlanning, DemandPlanner, Analytics Engine (EventStoreDB enriched streams)
- Real-time: SignalR/WebSocket for UI updates
**Key Distinction**: Nexus is the **Speed Layer** (operational, real-time) - optimized for "What is happening now?"
---
#### 3. ProductionPlanning (Advanced Planning & Scheduling)
**Role**: Tactical planning engine for production operations
**Responsibilities**:
- **Order Acceptance**: Evaluate and commit to customer orders considering constraints
- **Material Requirements Planning (MRP)**: Multi-level BOM planning with real-time inventory
- **Finite Capacity Scheduling**: Constraint-based resource allocation
- **Work Order Generation**: Create executable production orders
- **Campaign Management**: Optimize batch production with setup minimization
- **Real-Time Replanning**: Adjust schedules based on disruptions
**Owns**:
- Production order aggregates
- Resource capacity models
- Material inventory projections
- Work order execution plans
**Communicates Via**:
- Subscribes to: Nexus enriched events (demand signals, inventory updates)
- Publishes to: MES systems (work orders, schedules)
- Queries: Analytics Engine (historical performance data for optimization)
**Key Distinction**: ProductionPlanning is **tactical** (days to months horizon) - focuses on executable production plans
---
#### 4. DemandPlanner (Forecasting Intelligence)
**Role**: Demand forecasting and market intelligence
**Responsibilities**:
- **Statistical Forecasting**: ML-based demand prediction (multivariate models)
- **Hierarchical Forecasting**: Multi-level (product-location) forecasting
- **S&OP Process Management**: Consensus forecasting across stakeholders
- **Market Intelligence**: External data integration (economic, weather, social sentiment)
- **Demand Sensing**: Real-time POS, RFID, IoT signal processing
- **Scenario Planning**: What-if analysis with LLM narrative summaries
**Owns**:
- Forecast models and algorithms
- Historical demand data
- Market intelligence data
- Forecast accuracy metrics
**Communicates Via**:
- Subscribes to: Nexus enriched events (sales orders, market signals)
- Publishes to: ProductionPlanning (demand forecasts), Nexus (forecast updates)
- Queries: Analytics Engine (historical demand patterns, model training data)
**Key Distinction**: DemandPlanner is **strategic/tactical** (weeks to years horizon) - focuses on demand prediction
---
#### 5. Analytics Engine (Data Science & Analytics)
**Role**: Historical analysis, ML model training, and data science capabilities
**Responsibilities**:
- **Data Lake Management**: Store historical events in analytical format (Parquet/Delta Lake)
- **Feature Store**: Manage ML features for model training and serving
- **ML Model Training**: Train and version ML models (forecasting, anomaly detection, optimization)
- **Model Serving**: Serve predictions to operational contexts (Nexus, ProductionPlanning)
- **OLAP Analytics**: Complex analytical queries, aggregations, time-series analysis
- **BI & Reporting**: Business intelligence dashboards and reports
- **Experimentation**: A/B testing framework for ML models
- **Model Monitoring**: Detect model drift and performance degradation
**Owns**:
- Data lake (historical event storage)
- ML model registry and versions
- Feature definitions and transformations
- Analytical read models
- Training pipelines
**Communicates Via**:
- **Batch Ingestion**: Reads events from EventStoreDB in batches (scheduled, e.g., hourly) - PRIMARY PATH
- **Real-Time Subscription**: Optional subscription for real-time dashboards (last 24h only) - LIMITED USE
- **Data Lake Storage**: Transforms and stores events in Data Lake (Delta Lake/Iceberg) for analytical queries
- **Query Interface**: Serves analytical queries from Data Lake (NOT from EventStoreDB)
- **ML Predictions**: Serves model predictions to Nexus, ProductionPlanning, DemandPlanner via APIs
- **Publishes**: Model performance metrics, insights
**Key Distinction**: Analytics Engine is the **Batch Layer** (analytical, historical) - optimized for "What happened over time and why?"
**Critical Architecture Note**: Analytics Engine does NOT query EventStoreDB directly for analytics. Instead:
1. **Batch Ingestion**: Scheduled reads from EventStoreDB (e.g., hourly)
2. **Transform**: Convert events to Parquet format
3. **Store**: Write to Data Lake (Delta Lake/Iceberg)
4. **Query**: All analytical queries run against Data Lake, not EventStoreDB
This follows the Lambda Architecture pattern: EventStoreDB (Speed Layer) for operations, Data Lake (Batch Layer) for analytics.
# Medhāvī Supply Chain Event Hub - Project Description Document (PDD)
**Product**: Medhāvī Nexus
**Version**: 1.0
**Date**: September 2025
**Author**: Medhāvī Development Team
**Status**: Draft - Architecture Definition Phase
---
## Table of Contents
1. [Introduction](#1-introduction)
- 1.1 [Objectives](#11-objectives)
- [Stakeholder Perspective](#stakeholder-perspective)
- 1.2 [PDD Structure](#12-pdd-structure)
2. [Expectations](#2-expectations)
- 2.1 [Results](#21-results)
- 2.2 [In Scope](#22-in-scope)
- [Integration](#integration)
- [Real-Time Intelligence & Analytics](#real-time-intelligence--analytics)
- [AI/ML-Powered Features](#aiml-powered-features)
- [Advanced Visualization](#advanced-visualization)
- [Autonomous Operations](#autonomous-operations)
- [Sustainability & Circular Economy](#sustainability--circular-economy)
- [Augmented Intelligence & Human-Centric Design](#augmented-intelligence--human-centric-design)
- [Advanced Analytics & Intelligence](#advanced-analytics--intelligence)
- 2.3 [Performance/Usability](#23-performanceusability)
- 2.4 [Technology](#24-technology)
3. [Business Goals](#3-business-goals)
- 3.1 [Goal 1: Real-time Supply Chain Visibility](#31-goal-1-real-time-supply-chain-visibility)
- 3.2 [Goal 2: Intelligent Event Processing](#32-goal-2-intelligent-event-processing)
- 3.3 [Goal 3: Cross-System Integration](#33-goal-3-cross-system-integration)
4. [Integration](#4-integration)
- 4.1 [Overview](#41-overview)
- 4.1.1 [Data Flow](#411-data-flow)
- 4.1.2 [Processing Flow](#412-processing-flow)
- 4.1.3 [KPI Matrix](#413-kpi-matrix)
- 4.2 [Event Ingestion](#42-event-ingestion)
- 4.2.1 [Overview](#421-overview)
- 4.2.2 [Inputs, Process and Outputs](#422-inputs-process-and-outputs)
- 4.2.3 [Knowledge](#423-knowledge)
- 4.2.4 [Graphical User Interface](#424-graphical-user-interface)
- 4.2.5 [Use Cases](#425-use-cases)
- 4.3 [Master Data Enrichment](#43-master-data-enrichment)
- 4.3.1 [Description](#431-description)
- 4.3.2 [Inputs, Process and Outputs](#432-inputs-process-and-outputs)
- 4.3.3 [Graphical User Interface](#433-graphical-user-interface)
- 4.3.4 [Use Cases](#434-use-cases)
- 4.4 [Event Routing Decision](#44-event-routing-decision)
- 4.4.1 [Description](#441-description)
- 4.4.2 [Inputs, Process and Outputs](#442-inputs-process-and-outputs)
- 4.4.3 [Knowledge](#443-knowledge)
- 4.4.4 [Graphical User Interface](#444-graphical-user-interface)
- 4.4.5 [Use Cases](#445-use-cases)
- 4.5 [Real-time Monitoring](#45-real-time-monitoring)
- 4.5.1 [Description and Relevancy](#451-description-and-relevancy)
- 4.5.2 [Inputs, Process and Outputs](#452-inputs-process-and-outputs)
- 4.5.3 [Knowledge](#453-knowledge)
- 4.5.4 [Use Cases](#454-use-cases)
5. [AI-Powered Event Processing & Digital Twin](#5-ai-powered-event-processing--digital-twin)
- 5.1 [Real-Time Event Correlation Engine](#51-real-time-event-correlation-engine)
- 5.2 [Digital Twin Synchronization](#52-digital-twin-synchronization)
- 5.3 [Predictive Event Processing](#53-predictive-event-processing)
- 5.4 [Event Storm Detection](#54-event-storm-detection)
- 5.5 [Temporal Event Analysis](#55-temporal-event-analysis)
- 5.6 [Event Quality Scoring](#56-event-quality-scoring)
6. [GenAI-Enhanced Master Data Intelligence](#6-genai-enhanced-master-data-intelligence)
- 6.1 [Autonomous Data Enrichment](#61-autonomous-data-enrichment)
- 6.2 [Master Data Quality Prediction](#62-master-data-quality-prediction)
- 6.3 [Cross-Domain Data Correlation](#63-cross-domain-data-correlation)
- 6.4 [Master Data Evolution Tracking](#64-master-data-evolution-tracking)
- 6.5 [Semantic Data Understanding](#65-semantic-data-understanding)
- 6.6 [Master Data Anomaly Detection](#66-master-data-anomaly-detection)
7. [Immersive Real-Time Supply Chain Visibility](#7-immersive-real-time-supply-chain-visibility)
- 7.1 [3D Digital Twin Visualization](#71-3d-digital-twin-visualization)
- 7.2 [Real-Time KPI Forecasting](#72-real-time-kpi-forecasting)
- 7.3 [Supply Chain Heat Maps](#73-supply-chain-heat-maps)
- 7.4 [Event Stream Analytics](#74-event-stream-analytics)
- 7.5 [Predictive Performance Dashboards](#75-predictive-performance-dashboards)
- 7.6 [Collaborative AR Workspaces](#76-collaborative-ar-workspaces)
8. [Autonomous Orchestration & Self-Healing](#8-autonomous-orchestration--self-healing)
- 8.1 [Self-Healing Workflows](#81-self-healing-workflows)
- 8.2 [Predictive SLA Management](#82-predictive-sla-management)
- 8.3 [Autonomous Alert Routing](#83-autonomous-alert-routing)
- 8.4 [Dynamic Workflow Optimization](#84-dynamic-workflow-optimization)
- 8.5 [Autonomous Capacity Balancing](#85-autonomous-capacity-balancing)
- 8.6 [Predictive Maintenance Orchestration](#86-predictive-maintenance-orchestration)
9. [Advanced AI/ML Analytics & Intelligence](#9-advanced-aiml-analytics--intelligence)
- 9.1 [Transformer-Based Forecasting](#91-transformer-based-forecasting)
- 9.2 [Causal AI Root Cause Analysis](#92-causal-ai-root-cause-analysis)
- 9.3 [Generative Scenario Planning](#93-generative-scenario-planning)
- 9.4 [Federated Learning](#94-federated-learning)
- 9.5 [Edge AI Analytics](#95-edge-ai-analytics)
- 9.6 [Quantum-Ready Optimization](#96-quantum-ready-optimization)
10. [Industry 4.0 Integration & Smart Manufacturing](#10-industry-40-integration--smart-manufacturing)
- 10.1 [Smart Factory Orchestration](#101-smart-factory-orchestration)
- 10.2 [Digital Thread Management](#102-digital-thread-management)
- 10.3 [Predictive Equipment Analytics](#103-predictive-equipment-analytics)
- 10.4 [Quality 4.0 Automation](#104-quality-40-automation)
- 10.5 [Energy Optimization](#105-energy-optimization)
- 10.6 [Labor Productivity Intelligence](#106-labor-productivity-intelligence)
11. [Sustainability & Circular Economy Intelligence](#11-sustainability--circular-economy-intelligence)
- 11.1 [Carbon Footprint Tracking](#111-carbon-footprint-tracking)
- 11.2 [Sustainable Supplier Scoring](#112-sustainable-supplier-scoring)
- 11.3 [Circularity Planning](#113-circularity-planning)
- 11.4 [Green Route Optimization](#114-green-route-optimization)
- 11.5 [Waste Reduction Analytics](#115-waste-reduction-analytics)
- 11.6 [Regulatory Compliance AI](#116-regulatory-compliance-ai)
12. [Validation & Quality Assurance](#12-validation--quality-assurance)
- 12.1 [Validation Approach](#121-validation-approach)
- 12.2 [Development Best Practices](#122-development-best-practices)
13. [Additional Capabilities](#13-additional-capabilities)
- 13.1 [System Health Monitoring](#131-system-health-monitoring)
- 13.2 [Audit Logging](#132-audit-logging)
14. [Appendix A: Technical Architecture](#14-appendix-a-technical-architecture)
- 14.1 [System Components](#141-system-components)
- 14.2 [Data Flow Architecture](#142-data-flow-architecture)
- 14.3 [Scalability Analysis](#143-scalability-analysis)
- 14.4 [Integration & Communication Patterns](#144-integration--communication-patterns)
15. [Appendix B: Terminology](#15-appendix-b-terminology)
---
## 1. Introduction
### 1.1 Objectives
The main aim of this Project Description Document (PDD) is to describe the Medhāvī Supply Chain Event Hub (Nexus) system that will serve as the intelligent control tower for supply chain operations. This document translates our architectural vision into a technical implementation plan.
#### Stakeholder Perspective
Medhāvī Nexus provides a unified, real-time view of the supply chain. Stakeholders gain early warnings of delays or quality issues through predictive alerts and digital twins, enabling proactive decisions. Autonomous features reduce downtime and operational risk. Overall, Nexus delivers faster, more reliable supply chain execution and helps meet KPIs for cost, quality, and sustainability. Nexus is implemented on a modern event-sourcing foundation where all state changes are stored in EventStoreDB streams (append-only, ordered).
The Medhāvī Nexus will:
- Consume normalized events from Medhavi.Integrator integration layer
- Process events through persistent actors with event sourcing
- Enrich events with master data for downstream planning systems
- Provide real-time visibility and intelligent orchestration
- Enable seamless integration between supply chain bounded contexts
- Support AI/ML-driven optimization and decision-making
- Serve as the control tower for end-to-end supply chain visibility
### 1.2 PDD Structure
This PDD is organized around the core planning decisions and architectural components:
**Expectations**: Essential properties and requirements for the final solution
**Business Goals**: Measurable objectives that the system should achieve
**Scope Overview**: Functional architecture and event processing flows
**Planning Decisions**: Detailed specifications for each decision point
**Other Functionality**: Supporting capabilities and infrastructure
**Technical Architecture**: System design and scalability considerations
## 2. Expectations
### 2.1 Results
The Medhāvī Nexus should deliver:
- Sub-200ms event processing latency for 99th percentile
- 99.9% system availability with <4 hours annual downtime
- Zero data loss with guaranteed event persistence
- Real-time visibility across all supply chain events
- Seamless integration with existing ERP and WMS systems
### 2.2 In Scope
#### Integration
- **Multi-Source Data Ingestion**: ERP, WMS, MES, IoT, third-party APIs, unstructured data (emails/documents via GenAI parsing) (#GenAI).
- **Event Normalization**: Schema evolution, data transformation, RAG (Retrieval-Augmented Generation) for legacy document ingestion (#GenAI).
- **Real-Time Streaming**: Sub-100 ms event processing, 5G-enabled ultra-low-latency ingestion.
- **Data Quality Assurance**: Automated validation, cleansing, LLM-based anomaly inference (#GenAI, #PredictiveQuality).
- **Master Data Synchronization**: Cross-system data harmonization
- **IoT Sensor Integration**: Edge/IoT data collection from equipment, environment, assets; support for edge AI nodes.
- **API Orchestration**: REST, GraphQL, Webhooks, and GenAI-powered conversational APIs for ad-hoc queries (#GenAI).
- **Event Deduplication**: Two-tier dedup (LRU cache + persistence) for high-throughput messaging.
##### Real-Time Intelligence & Analytics
- **Event Correlation Engine**: AI-powered pattern recognition across events
- **Predictive Alerting**: ML-based anomaly detection and early warning of disruptions (#PredictiveQuality)
- **Digital Twin Management**: Live, multi-layer digital twin of the supply chain (network, inventory, processes)
- **Automated Exception Management**: Smart routing, escalation and automated resolution workflows
- **Cross-System Orchestration**: AI-coordinated API workflows and autonomous actions (#GenAI, #DemandSensing)
- **Real-Time KPI Calculation**: Streaming analytics with sub-second metric updates
- **Multi-Agent Orchestration**: Coordinated AI agents handle complex tasks (e.g. autonomous procurement and fulfillment agents) (#GenAI)
##### AI/ML-Powered Features
- **GenAI Scenario Planning**: Natural language "what-if" analysis with LLMs (#GenAI)
- **Autonomous Optimization**: Self-tuning, multi-objective optimization algorithms (cost, service, carbon) with human oversight
- **Predictive Maintenance**: Equipment failure prediction using IoT data
- **Quality Control Intelligence**: AI-based defect prediction and prevention (including vision analytics) (#PredictiveQuality)
- **Supplier Performance Prediction**: Forecast supplier reliability, quality, and risk
- **Carbon Optimization**: Real-time emissions tracking and reduction
- **ESG Scorecards**: Integrated supplier ESG ratings and risk scoring for decision support (#Sustainability)
##### Advanced Visualization
- **3D Digital Twin Visualization**: AR/VR-enhanced supply chain views with drill-down
- **Immersive Control Room**: Spatial dashboards, heat maps for supply chain health
- **Augmented Reality Mobile**: AR overlays for factory floors (inventory levels, quality)
- **Natural Language Interfaces**: Voice and visual query support (e.g. "Show all delayed orders by aisle" via voice) (#GenAI)
##### Autonomous Operations
- **Self-Healing Workflows**: Automated disruption responses (reroute shipments, reschedule production)
- **Intelligent Escalation**: Context-aware issue routing to teams or AI bots
- **Predictive Replanning**: AI-driven contingency plans for supply/demand shocks
- **Autonomous Decision Making**: ML agents execute high-confidence operational tasks (under human oversight)
- **Agentic AI Workflows**: Multi-agent GenAI systems coordinate end-to-end actions (e.g. auto-procurement contracts) (#GenAI)
##### Sustainability & Circular Economy
- **Scope 1–3 Emissions Tracking**: Full carbon footprint across operations and suppliers.
- **Recycled Content & Circularity**: Material re-use planning; LCA-driven design alternatives (#Sustainability).
- **Packaging Optimization**: AI-suggested eco-packaging designs.
- **Green Route Planning**: Carbon-aware transportation (green lanes, modal shifts).
- **Water & Biodiversity Metrics**: Supplier-level water usage and habitat impact; supports TNFD/ESRS reporting (#Sustainability)
- **Waste Reduction Intelligence**: Predictive waste generation models to minimize scrap.
##### Augmented Intelligence & Human-Centric Design
- **Human-in-the-Loop AI**: AI provides recommendations; humans retain final authority.
- **GenAI Assistants**: Conversational AI copilots for planners and buyers (#GenAI).
- **Low-Code Workflow Builder**: Custom process design by business users.
- **Voice/NLP Control**: Natural language supply chain commands (#GenAI).
- **Gesture-Based/AR Interfaces**: Hands-free data capture and control on the shop floor.
- **Adaptive Learning UI**: Interface that personalizes to user behavior.
##### Advanced Analytics & Intelligence
- **Causal AI Analysis**: Explainable root-cause analytics (why did demand spike or supply fail).
- **Predictive Scenario Planning**: LLM-driven multi-variant simulations for strategy.
- **Benchmarking Intelligence**: Peer and industry performance comparisons.
- **Real-Time Risk Command Center**: AI-scores supply-chain risks (geo-political, market) for prioritization.
- **Predictive ROI Modeling**: Forecast financial impacts of decisions.
### 2.3 Performance/Usability
**Latency Requirements**
- Event processing: <100ms average, <200ms 99th percentile
- Digital Twin updates: <50ms synchronization latency
- KPI calculations: <100ms for real-time metrics
- AI inference: <200ms for prediction requests
- Alert generation: <500ms from event to alert
- API response times: <100ms for queries, <2s for complex operations
- System startup: <30 seconds
**Throughput Requirements**
- Event processing: 10,000+ events/second
- Pattern recognition: 1,000+ patterns/second
- Digital Twin updates: 100+ state updates/second
- KPI calculations: 50+ metric updates/second
- AI inferences: 100+ predictions/second
**Scalability Requirements**
- Horizontal scaling: Support for multiple Nexus instances
- State management: Distributed digital twin state
- Model serving: Scalable AI/ML model inference
- Event streaming: High-throughput event processing
- Memory usage: <2GB under normal load
**User Experience**
- Intuitive web-based monitoring interface
- Real-time dashboards with WebSocket/SignalR updates
- AR/VR-enhanced visualization capabilities
### 2.4 Technology
- **Runtime**: .NET 10.0, F# primary language
- **Event Processing**: Akka.NET with persistent actors
- **Event Storage**: EventStoreDB
- **Web Framework**: ASP.NET Core
- **UI Framework**: Avalonia.UI
- **Real-time**: SignalR for WebSocket communication
- **Deployment**: Docker containers with Kubernetes orchestration
## 3. Business Goals
### 3.1 Goal 1: Real-time Supply Chain Visibility
**Definition**: Provide immediate visibility into supply chain events and status across all systems and processes.
**Motivation**: Traditional supply chain systems suffer from data silos and delayed information flow. Real-time visibility enables faster decision-making and problem resolution.
**Current State**: Manual data collection and reporting with 24-48 hour delays
**Target State**: Sub-second event visibility with automated alerting
**Success Metric**: 100% event visibility within 5 seconds of occurrence. End-to-End Visibility: Live tracking from raw materials to customer delivery.
**Technical Success Metrics**
- Event processing performance: <100ms latency, 10K+ events/second
- Digital twin synchronization: <50ms update latency
- System availability: 99.5% uptime for Nexus services
- Alert accuracy: 95% true positive rate for anomaly detection
### 3.2 Goal 2: Intelligent Event Processing
**Definition**: Automatically process and correlate supply chain events to identify patterns, anomalies, and optimization opportunities.
**Motivation**: Manual event processing is error-prone and slow. Intelligent processing enables proactive supply chain management.
**Current State**: Reactive, manual event handling
**Target State**:
- Predictive Intelligence: AI-powered early warnings and insights.
- Risk Command Center: Centralized risk monitoring and alerts.
**Success Metric**: 95% of events processed without manual intervention
### 3.3 Goal 3: Cross-System Integration
**Definition**: Enable seamless data flow and process integration between disparate supply chain systems.
**Motivation**: System integration is complex and costly. Standardized event-driven integration reduces complexity and maintenance costs.
**Current State**: Point-to-point integrations with custom code.
**Target State**: Event-driven integration with standardized schemas. Autonomous Orchestration: Self-healing workflows to maintain flow.
**Success Metric**: New system integration completed in <1 week.
**Integration Architecture:**
- Event-driven integration with EventStoreDB as primary event persistence and streaming
- IoT platforms for real-time sensor data integration
- ERP systems for master data and transaction integration
- MES systems for production execution data integration
- External APIs for third-party data source integration
- API orchestration with REST, GraphQL, and WebSocket support
## 4. Integration
### 4.1 Overview
#### 4.1.1 Data Flow
```
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Event Sources │───▶│Medhavi.Integrator│──▶│ Medhāvī │───▶│ EventStoreDB │
│ (SAP, WMS, IoT)│ │ (Integration) │ │ Nexus │ │ │
└─────────────────┘ └─────────────────┘ └─────────────────┘ └─────────────────┘
┌─────────────────┐
│ Real-time │
│ Dashboards │
└─────────────────┘
```
| Decision | Description | Automation Level | Frequency |
| ---------------------- | -------------------------------------- | ---------------- | ---------- |
| Event Ingestion | Accept and validate incoming events | Fully Automated | Continuous |
| Master Data Enrichment | Add contextual data to events | Semi-Automated | Per Event |
| Event Routing | Route events to appropriate processors | Fully Automated | Per Event |
| Real-time Monitoring | Monitor system health and performance | Fully Automated | Continuous |
#### 4.1.2 Processing Flow
1. **External Integration**: Medhavi.Integrator connects to SAP, WMS, IoT sensors
2. **Event Normalization**: Medhavi.Integrator transforms and standardizes external data
3. **Event Publishing**: Medhavi.Integrator publishes normalized events to EventStoreDB
4. **Event Ingestion**: Nexus consumes events from EventStoreDB via EventStoreReader
5. **Event Processing**: Nexus processes events through Akka.NET persistent actors
6. **Master Data Enrichment**: Nexus enriches events with planning-relevant master data
7. **Event Distribution**: Nexus publishes enriched events for ProductionPlanner consumption
8. **Real-time Updates**: Nexus provides WebSocket updates to UI clients
#### 4.1.3 KPI Matrix
1. **Event Processing Latency**: Time from event receipt to processing completion
2. **Event Success Rate**: Percentage of events processed without errors
3. **System Availability**: Percentage of time system is operational
4. **Data Accuracy**: Percentage of events with correct enrichment
### 4.2 Event Ingestion
#### 4.2.1 Overview
The event ingestion decision determines how external events are accepted, validated, and prepared for processing. This is the primary entry point for all supply chain data.
#### 4.2.2 Inputs, Process and Outputs
**Inputs:**
- Raw events from external systems (JSON/XML format)
- Event schemas and validation rules
- System configuration and routing rules
**Process:**
1. Receive event via HTTP/WebSocket
2. Validate event schema and structure
3. Enrich with metadata (timestamps, correlation IDs)
4. Route to appropriate processing queue
**Outputs:**
- Validated and enriched events
- Processing acknowledgments
- Error notifications for invalid events
#### 4.2.3 Knowledge
**Preconditions:**
- Event must conform to defined schema
- Source system must be authorized
- System must have available processing capacity
**Constraints:**
- Event size limit: 10MB
- Processing rate limit: 1000 events/second
- Schema validation: Strict mode
#### 4.2.4 Graphical User Interface
- Event monitoring dashboard
- Real-time event throughput graphs
- Error event queue viewer
- Event replay controls
#### 4.2.5 Use Cases
1. **Normal Event Processing**: System accepts valid event and processes immediately
2. **Invalid Event Handling**: System rejects malformed event with detailed error message
3. **High Volume Processing**: System handles traffic spikes with queuing
4. **Event Replay**: Administrator can replay failed events
### 4.3 Master Data Enrichment
#### 4.3.1 Description
Master data enrichment adds contextual information to events, enabling intelligent processing and cross-system correlation. The Nexus Event Enrichment System provides a comprehensive framework for enriching events with planning-relevant master data before publishing to downstream systems like ProductionPlanner.
#### 4.3.2 Technical Implementation
##### Core Components
- **MasterDataRepository.fs**: In-memory repository for master data storage with thread-safe concurrent collections
- **EventEnrichmentEngine.fs**: Rule-based enrichment engine with configurable enrichment rules
- **EventPublisher.fs**: Publishing system for enriched events to ProductionPlanner streams
- **MasterDataIngestionService.fs**: Service for loading master data from external sources
- **NexusEnrichmentCoordinator.fs**: Main orchestration coordinator with retry logic and monitoring
##### Enrichment Pipeline
```fsharp
// Complete enrichment flow
let enrichEvent (event: SupplyChainEvent) = async {
// 1. Load master data context
let! context = loadEnrichmentContext event
// 2. Apply enrichment rules
let! result = enrichmentEngine.EnrichEventAsync(event)
// 3. Publish enriched event
match result with
| Enriched enrichedPayload ->
let enrichedEvent = { event with Payload = enrichedPayload }
let! publishResult = publisher.PublishToProductionPlannerAsync(enrichedEvent)
return Success enrichedEvent
| _ -> return EnrichmentSkipped "No enrichment applied"
}
```
#### 4.3.3 Inputs, Process and Outputs
**Inputs:**
- Raw events from EventStoreDB external streams
- Master data from ERP/PLM systems (products, BOMs, routings, resources)
- Business rules for enrichment logic
- Configuration for enrichment rules and retry policies
**Process:**
1. **Event Reception**: Events consumed from EventStoreDB via EventStoreReader
2. **Context Loading**: Master data loaded for enrichment (products, routings, resources)
3. **Rule Application**: Configurable enrichment rules applied based on event type
4. **Payload Enrichment**: Event payload enhanced with planning-relevant data
5. **Validation**: Enriched event validated before publishing
6. **Publishing**: Enriched events published to ProductionPlanner streams
7. **Monitoring**: Enrichment metrics collected and monitored
**Outputs:**
- Enriched events with full planning context
- Enrichment success/failure metrics
- Master data quality reports
- Processing latency and throughput statistics
#### 4.3.4 Enrichment Rules Engine
##### Rule-Based Enrichment
```fsharp
type EnrichmentRule = {
Name: string
AppliesTo: string list // Event types this rule applies to
EnrichFunction: EnrichmentContext -> Task<EnrichmentResult>
}
// Example: Order Enrichment Rule
let createOrderEnrichmentRule () : EnrichmentRule = {
Name = "Order Enrichment"
AppliesTo = ["OrderCreated"; "OrderUpdated"]
EnrichFunction = fun context ->
task {
// Enrich order items with product master data
let enrichedItems = context.OriginalEvent
|> parseOrderItems
|> List.map (enrichWithProductData context.ProductData)
// Add enrichment metadata
let enrichedOrder = {
Items = enrichedItems
EnrichmentMetadata = {
EnrichedAt = DateTimeOffset.UtcNow
ProductsEnriched = enrichedItems.Length
RoutingDataAvailable = false
ResourceCapacityAvailable = false
}
}
return Enriched (Json.serialize enrichedOrder)
}
}
```
##### Production Order Enrichment
```fsharp
let createProductionOrderEnrichmentRule () : EnrichmentRule = {
Name = "Production Order Enrichment"
AppliesTo = ["ProductionOrderCreated"; "WorkOrderCreated"]
EnrichFunction = fun context ->
task {
// Add routing operations
let routingData = context.RoutingData
|> Map.tryFind context.OriginalEvent.AggregateId
// Add resource capacity information
let capacityData = context.ResourceCapacityData
|> Map.tryFind (getResourceId context.OriginalEvent)
let enrichedOrder = {
RoutingData = routingData
ResourceCapacityData = capacityData
EnrichmentMetadata = {
EnrichedAt = DateTimeOffset.UtcNow
RoutingEnriched = routingData.IsSome
CapacityEnriched = capacityData.IsSome
ProductDataAvailable = false
}
}
return Enriched (Json.serialize enrichedOrder)
}
}
```
#### 4.3.5 Master Data Repository
##### Interface Definition
```fsharp
type IMasterDataRepository =
// Product Master Data
abstract member GetProductAsync: ProductId -> Task<Nexus.Product option>
abstract member GetProductWithBOMAsync: ProductId -> Task<Nexus.ProductWithBOM option>
abstract member StoreProductAsync: Nexus.Product -> Task<unit>
// Routing Master Data
abstract member GetRoutingAsync: string -> Task<ProductionPlanning.Routing option>
abstract member StoreRoutingAsync: ProductionPlanning.Routing -> Task<unit>
// Resource Capacity Master Data
abstract member GetResourceCapacityAsync: string -> Task<Nexus.ResourceCapacityPeriod list>
abstract member StoreResourceCapacityAsync: Nexus.ResourceCapacityPeriod -> Task<unit>
```
##### In-Memory Implementation
- **ConcurrentDictionary**: Thread-safe storage for master data
- **Lazy Loading**: Master data loaded on-demand for performance
- **Comprehensive Logging**: Full audit trail for data operations
- **Type Safety**: Strong typing with domain-specific types
#### 4.3.6 Enrichment Examples
##### Before Enrichment (Original Event):
```json
{
"eventId": "order-123",
"eventType": "OrderCreated",
"payload": {
"orderId": "ORD-001",
"items": [
{
"productId": "PROD-456",
"quantity": 100
}
]
}
}
```
##### After Enrichment (Enriched Event):
```json
{
"eventId": "order-123",
"eventType": "OrderCreated",
"payload": {
"orderId": "ORD-001",
"items": [
{
"productId": "PROD-456",
"productName": "Widget Assembly",
"productGroup": "Finished Goods",
"quantity": 100,
"pieceWeight": 2.5,
"routing": {
"id": "ROUT-789",
"operations": [
{
"sequence": 10,
"workCenter": "WC-001",
"setupTime": 30,
"runTime": 45
}
]
},
"resourceCapacity": {
"resourceId": "WC-001",
"capacityPeriods": [
{
"startDate": "2025-01-01",
"endDate": "2025-12-31",
"availableCapacity": 480
}
]
}
}
],
"enrichmentMetadata": {
"enrichedAt": "2025-09-15T03:43:00Z",
"productsEnriched": 1,
"routingDataAvailable": true,
"resourceCapacityAvailable": true
}
},
"metadata": {
"enrichedBy": "NexusEnrichmentCoordinator",
"enrichmentTimestamp": "2025-09-15T03:43:00Z",
"publishedTo": "ProductionPlanner"
}
}
```
#### 4.3.7 Knowledge
**Preconditions:**
- Master data must be loaded and available in repository
- Event schema must be compatible with enrichment rules
- System must have sufficient memory for master data caching
**Business Rules:**
- **Product Enrichment**: Add full product hierarchy, specifications, and BOM data
- **Routing Enrichment**: Include operation sequences, work centers, and timing data
- **Resource Enrichment**: Add capacity periods, efficiency factors, and availability
- **Customer Enrichment**: Include customer segments, preferences, and historical data
- **Location Enrichment**: Add geographic context, transportation zones, and costs
**Constraints:**
- **Memory Limits**: Master data cache size limited to available system memory
- **Processing Timeouts**: Enrichment operations must complete within 30 seconds
- **Data Freshness**: Master data refreshed every 15 minutes from source systems
- **Fallback Behavior**: Events published without enrichment if master data unavailable
#### 4.3.8 Graphical User Interface
##### Master Data Management Dashboard
- **Data Loading Status**: Real-time view of master data ingestion progress
- **Data Quality Metrics**: Completeness, accuracy, and freshness indicators
- **Enrichment Rule Configuration**: Visual rule builder with drag-and-drop interface
- **Performance Monitoring**: Enrichment throughput and latency graphs
##### Enrichment Monitoring Interface
- **Real-time Enrichment Metrics**: Success rates, processing times, error counts
- **Event Flow Visualization**: Graphical representation of enrichment pipeline
- **Rule Performance Analysis**: Which rules are most/least effective
- **Data Quality Reports**: Automated reports on enrichment completeness
##### Alert Management
- **Enrichment Failures**: Alerts for failed enrichment operations
- **Data Quality Issues**: Notifications for missing or stale master data
- **Performance Degradation**: Warnings for slow enrichment processing
- **Capacity Issues**: Alerts for high memory usage or processing backlogs
#### 4.3.9 Use Cases
1. **Standard Enrichment**: Event enriched with all available master data
- **Trigger**: OrderCreated event received
- **Process**: Product, routing, and capacity data added
- **Result**: Fully enriched event published to ProductionPlanner
2. **Partial Enrichment**: Event enriched with available data, gaps logged
- **Trigger**: ProductionOrderCreated event with missing routing data
- **Process**: Available product and capacity data added, routing gap logged
- **Result**: Partially enriched event published with quality warnings
3. **Enrichment Failure**: Event processed without enrichment, alert generated
- **Trigger**: Master data repository unavailable
- **Process**: Event published in original form, system alert generated
- **Result**: Original event forwarded, incident ticket created
4. **Batch Enrichment**: Multiple events processed efficiently
- **Trigger**: Bulk order import from ERP system
- **Process**: Events processed in parallel with shared master data context
- **Result**: All events enriched and published with consolidated metrics
5. **Real-time Enrichment**: Events enriched with latest master data
- **Trigger**: Continuous event stream from manufacturing floor
- **Process**: Events enriched with real-time capacity and routing updates
- **Result**: ProductionPlanner receives current operational context
#### 4.3.10 Automation Level
**Fully Automated Components:**
- Event reception and routing to enrichment engine
- Master data lookup and caching
- Rule application and payload enrichment
- Publishing to downstream systems
- Metrics collection and health monitoring
**Semi-Automated Components:**
- Master data ingestion from external systems (scheduled)
- Enrichment rule configuration (admin interface)
- Alert response and incident management
**Manual Components:**
- Master data quality review and correction
- Enrichment rule development and testing
- Performance tuning and capacity planning
### 4.4 Event Routing Decision
#### 4.4.1 Description
Event routing determines how processed events are distributed to internal and external consumers.
#### 4.4.2 Inputs, Process and Outputs
**Inputs:**
- Processed events from enrichment
- Routing rules and subscriber lists
- System capacity and performance metrics
**Process:**
1. Evaluate routing rules against event content
2. Determine appropriate destinations
3. Apply filtering and transformation rules
4. Distribute to subscribers
**Outputs:**
- Routed events to appropriate destinations
- Routing success/failure metrics
- Subscriber health status
#### 4.4.3 Knowledge
**Routing Rules:**
- Event type-based routing
- Content-based filtering
- Priority-based queuing
- Geographic distribution rules
#### 4.4.4 Graphical User Interface
- Routing rule configuration interface
- Subscriber management dashboard
- Routing performance monitoring
- Message queue viewers
#### 4.4.5 Use Cases
1. **Standard Routing**: Event delivered to all relevant subscribers
2. **Filtered Routing**: Event delivered only to authorized subscribers
3. **Priority Routing**: High-priority events processed first
4. **Failed Routing**: Undeliverable events queued for retry
### 4.5 Real-time Monitoring
#### 4.5.1 Description and Relevancy
Real-time monitoring provides visibility into system health, performance, and event processing status.
#### 4.5.2 Inputs, Process and Outputs
**Inputs:**
- System metrics and logs
- Event processing statistics
- Infrastructure health data
**Process:**
1. Collect metrics from all system components
2. Analyze performance against thresholds
3. Generate alerts for anomalies
4. Update monitoring dashboards
**Outputs:**
- Real-time dashboards and alerts
- Performance reports and analytics
- System health assessments
#### 4.5.3 Knowledge
**Monitoring Rules:**
- Latency thresholds for different operations
- Error rate limits and escalation rules
- Capacity utilization warnings
- Data quality metrics
#### Graphical User Interface
- Real-time dashboard with KPIs
- Alert management interface
- Performance trend charts
- System topology visualization
#### 4.5.4 Use Cases
1. **Normal Monitoring**: System operating within parameters
2. **Performance Alert**: System generates alert for high latency
3. **Error Detection**: System identifies and reports processing errors
4. **Capacity Planning**: System provides utilization trends
## 5. AI-Powered Event Processing & Digital Twin
### 5.1 Real-Time Event Correlation Engine
#### 5.1.1 Description
AI-powered pattern recognition across 1000+ event types. Real-time event correlation and anomaly detection with predictive alerting for supply chain disruptions. Automated exception management and resolution.
#### 5.1.2 Goals
- AI-powered pattern recognition across 1000+ event types
- Real-time event correlation and anomaly detection
- Predictive alerting for supply chain disruptions
- Automated exception management and resolution
#### 5.1.3 Technical Implementation
- Event correlation engine with ML-based pattern recognition
- Pattern detection across event streams
- Anomaly detection with 95% accuracy target
- Real-time alert generation and routing
- Automated exception resolution workflows
#### 5.1.4 Integration Points
- Consumes events from EventStoreDB external streams
- Publishes correlation results to internal streams
- Integrates with alerting and notification systems
- Connects to digital twin for impact assessment
#### 5.1.5 Success Criteria
- Event correlation identifying patterns in 90% of events
- Anomaly detection with 95% accuracy
- Real-time alerting for critical issues
- Automated exception management operational
#### 5.1.6 Telemetry / KPIs
- Pattern recognition latency
- Anomaly detection accuracy
- Alert generation rate
- Exception resolution rate
### 5.2 Digital Twin Synchronization
#### 5.2.1 Description
Live, multi-layer digital twin of supply chain with real-time synchronization with physical operations. 3D visualization with AR/VR capabilities. Predictive simulation and scenario planning.
#### 5.2.2 Goals
- Live, multi-layer digital twin of supply chain
- Real-time synchronization with physical operations
- 3D visualization with AR/VR capabilities
- Predictive simulation and scenario planning
#### 5.2.3 Technical Implementation
- Digital twin state management with <50ms synchronization latency
- Multi-layer representation (network, inventory, processes)
- Real-time state updates from event streams
- 3D visualization engine with AR/VR rendering
- Simulation engine for scenario planning
#### 5.2.4 Integration Points
- Real-time event consumption for state updates
- IoT sensor data integration for physical sync
- AR/VR rendering for immersive visualization
- ProductionPlanning integration for planning scenarios
#### 5.2.5 Success Criteria
- Digital twin synchronized within 100ms (target: <50ms)
- 3D visualization rendering supply chain topology
- Real-time KPI calculations from twin state
- Predictive simulation capabilities operational
#### 5.2.6 Telemetry / KPIs
- Synchronization latency (<50ms target)
- State update throughput (100+ updates/second)
- Visualization rendering performance
- Simulation accuracy
### 5.3 Predictive Event Processing
#### 5.3.1 Description
ML-based event pattern prediction and proactive processing
### 5.4 Event Storm Detection
#### 5.4.1 Description
Automated identification of event cascades and system disruptions
### 5.5 Temporal Event Analysis
#### 5.5.1 Description
AI-driven analysis of event sequences and timing patterns
### 5.6 Event Quality Scoring
#### 5.6.1 Description
ML-based event completeness and accuracy assessment
## 6. GenAI-Enhanced Master Data Intelligence
### 6.1 Autonomous Data Enrichment
#### 6.1.1 Description
AI-powered contextual data addition using knowledge graphs
### 6.2 Master Data Quality Prediction
#### 6.2.1 Description
ML-based data quality assessment and auto-correction
### 6.3 Cross-Domain Data Correlation
#### 6.3.1 Description
AI linking of master data across products, customers, suppliers
### 6.4 Master Data Evolution Tracking
#### 6.4.1 Description
Automated detection of master data changes and impacts
### 6.5 Semantic Data Understanding
#### 6.5.1 Description
Natural language processing for unstructured master data
### 6.6 Master Data Anomaly Detection
#### 6.6.1 Description
AI identification of data inconsistencies and outliers
## 7. Immersive Real-Time Supply Chain Visibility
### 7.1 3D Digital Twin Visualization
#### 7.1.1 Description
AR/VR-enhanced supply chain topology with spatial analytics
### 7.2 Real-Time KPI Forecasting
#### 7.2.1 Description
AI-powered KPI prediction with confidence intervals
### 7.3 Supply Chain Heat Maps
#### 7.3.1 Description
Dynamic bottleneck identification and capacity visualization
### 7.4 Event Stream Analytics
#### 7.4.1 Description
Real-time event velocity, volume, and pattern analysis
### 7.5 Predictive Performance Dashboards
#### 7.5.1 Description
ML-driven performance trend forecasting
### 7.6 Collaborative AR Workspaces
#### 7.6.1 Description
Multi-user AR environments for remote collaboration
## 8. Autonomous Orchestration & Self-Healing
### 8.1 Self-Healing Workflows
#### 8.1.1 Description
Self-healing workflows for disruption response. Intelligent escalation and routing. Predictive replanning for supply/demand shocks. Autonomous decision making under human oversight.
#### 8.1.2 Goals
- Self-healing workflows for disruption response
- Intelligent escalation and routing
- Predictive replanning for supply/demand shocks
- Autonomous decision making under human oversight
#### 8.1.3 Technical Implementation
- Autonomous operations engine for disruption detection
- Self-healing workflow manager
- Automated response plan generation
- Human-in-the-loop oversight mechanisms
- Rollback and audit capabilities
#### 8.1.4 Integration Points
- Event correlation engine for disruption detection
- Digital twin for impact assessment
- External system APIs for automated responses
- ProductionPlanning for replanning coordination
#### 8.1.5 Success Criteria
- Disruption detection with 90% accuracy
- Automated response generation working
- Self-healing workflows operational
- Human oversight integration functional
#### 8.1.6 Telemetry / KPIs
- Disruption detection accuracy (90%+ target)
- Response generation latency
- Self-healing success rate
- Human intervention rate
### 8.2 Predictive SLA Management
#### 8.2.1 Description
ML-based SLA violation prediction and proactive mitigation
### 8.3 Autonomous Alert Routing
#### 8.3.1 Description
Intelligent alert prioritization and stakeholder targeting
### 8.4 Dynamic Workflow Optimization
#### 8.4.1 Description
Real-time workflow path optimization based on current conditions
### 8.5 Autonomous Capacity Balancing
#### 8.5.1 Description
AI-driven resource reallocation across supply chain nodes
### 8.6 Predictive Maintenance Orchestration
#### 8.6.1 Description
Automated maintenance scheduling coordination
## 9. Advanced AI/ML Analytics & Intelligence
### 9.1 Transformer-Based Forecasting
#### 9.1.1 Description
Deep learning demand prediction with 95%+ accuracy. Transformer-based forecasting models for time-series prediction with high accuracy and explainability.
#### 9.1.2 Goals
- Deep learning demand prediction with 95%+ accuracy
- Time-series forecasting with transformer models
- Multi-horizon prediction capabilities
- Explainable forecasting results
#### 9.1.3 Technical Implementation
- Transformer-based forecasting models
- Time-series data preprocessing
- Model training and deployment pipeline
- Real-time inference service
- Forecast accuracy monitoring
#### 9.1.4 Integration Points
- Historical demand data for model training
- Real-time demand signals for prediction
- ProductionPlanning for demand planning
- Dashboard systems for forecast visualization
#### 9.1.5 Success Criteria
- Forecasting accuracy 95%+ for demand prediction
- Real-time inference operational
- Multi-horizon forecasting working
- Model explainability functional
#### 9.1.6 Telemetry / KPIs
- Forecasting accuracy (95%+ target)
- Model inference latency
- Forecast horizon coverage
- Model version performance tracking
### 9.2 Causal AI Root Cause Analysis
#### 9.2.1 Description
Explainable AI for supply chain disruption diagnosis
### 9.3 Generative Scenario Planning
#### 9.3.1 Description
AI-powered "what-if" analysis with natural language interface
### 9.4 Federated Learning
#### 9.4.1 Description
Privacy-preserving ML across distributed supply chain sites
### 9.5 Edge AI Analytics
#### 9.5.1 Description
Real-time intelligence at production sites without data centralization
### 9.6 Quantum-Ready Optimization
#### 9.6.1 Description
Future-proofed algorithms for quantum computing acceleration
## 10. Industry 4.0 Integration & Smart Manufacturing
### 10.1 Smart Factory Orchestration
#### 10.1.1 Description
MES, PLC, and IoT device coordination. Smart factory orchestration enables real-time coordination between manufacturing execution systems, programmable logic controllers, and IoT devices for seamless production operations.
#### 10.1.2 Goals
- MES, PLC, and IoT device coordination
- Real-time production floor integration
- Equipment data collection and analysis
- Automated production workflow orchestration
#### 10.1.3 Technical Implementation
- MES integration for production order management
- PLC connectivity for equipment control
- IoT sensor network integration
- Real-time data collection and processing
- Production workflow orchestration engine
#### 10.1.4 Integration Points
- MES systems for production execution data
- PLC systems for equipment control
- IoT platforms for sensor data
- ProductionPlanning for production orders
#### 10.1.5 Success Criteria
- Real-time MES/PLC/IoT coordination operational
- Equipment data collection working
- Production workflow orchestration functional
- Integration latency <100ms
### 10.2 Digital Thread Management
#### 10.2.1 Description
End-to-end traceability from design to delivery
### 10.3 Predictive Equipment Analytics
#### 10.3.1 Description
Equipment failure prediction using IoT data. Predictive maintenance scheduling optimization. Real-time equipment health monitoring. Automated maintenance work order generation.
#### 10.3.2 Goals
- Equipment failure prediction using IoT data
- Predictive maintenance scheduling optimization
- Real-time equipment health monitoring
- Automated maintenance work order generation
#### 10.3.3 Technical Implementation
- IoT data integration for sensor data collection
- Predictive maintenance models (95% accuracy target)
- Equipment health analysis engine
- Maintenance schedule optimization
- Automated work order generation
#### 10.3.4 Integration Points
- IoT sensor network for real-time data collection
- Equipment master data for health analysis
- Maintenance scheduling system integration
- ProductionPlanning for maintenance coordination
#### 10.3.5 Success Criteria
- Equipment failure prediction with 95% accuracy
- Real-time health monitoring operational
- Automated maintenance scheduling working
- IoT sensor integration functional
#### 10.3.6 Telemetry / KPIs
- Prediction accuracy (95%+ target)
- Health monitoring latency
- Maintenance schedule optimization rate
- IoT data collection throughput
### 10.4 Quality 4.0 Automation
#### 10.4.1 Description
Computer vision-based defect detection and classification
### 10.5 Energy Optimization
#### 10.5.1 Description
AI-driven consumption reduction and renewable integration
### 10.6 Labor Productivity Intelligence
#### 10.6.1 Description
Workforce optimization with skill matching
## 11. Sustainability & Circular Economy Intelligence
### 11.1 Carbon Footprint Tracking
#### 11.1.1 Description
Real-time emissions calculation across entire supply chain
### 11.2 Sustainable Supplier Scoring
#### 11.2.1 Description
ESG-based supplier evaluation and ranking
### 11.3 Circularity Planning
#### 11.3.1 Description
AI-driven scrap reuse and material recovery optimization
### 11.4 Green Route Optimization
#### 11.4.1 Description
Carbon-aware transportation and logistics planning
### 11.5 Waste Reduction Analytics
#### 11.5.1 Description
Predictive waste generation and minimization
### 11.6 Regulatory Compliance AI
#### 11.6.1 Description
Automated compliance monitoring and reporting
## 13. Additional Capabilities
### 13.1 System Health Monitoring
#### 13.1.1 Description
System health monitoring ensures the reliability and availability of the Medhāvī Nexus platform.
#### 13.1.2 Inputs, Process and Outputs
**Inputs:** System metrics, logs, and performance data
**Process:** Automated health checks and alerting
**Outputs:** Health status reports and maintenance notifications
#### 13.1.3 Knowledge
Health check rules and thresholds for all system components.
#### 13.1.4 Graphical User Interface
Health dashboard with component status indicators.
#### 13.1.5 Use Cases
1. System startup health verification
2. Continuous health monitoring
3. Automated recovery procedures
#### 13.1.6 Automation
Fully automated health monitoring with manual intervention capabilities.
### 13.2 Audit Logging
#### 13.2.1 Description
Audit logging provides complete traceability of system activities and changes.
#### 13.2.2 Inputs, Process and Outputs
**Inputs:** System events and user actions
**Process:** Structured logging with retention policies
**Outputs:** Audit reports and compliance documentation
#### 13.2.3 Knowledge
Audit requirements and retention policies.
#### 13.2.4 Graphical User Interface
Audit log viewer with filtering and search capabilities.
#### 13.2.5 Use Cases
1. Security incident investigation
2. Compliance reporting
3. System troubleshooting
#### 13.2.6 Automation
Fully automated logging with configurable retention.
## 14. Appendix A: Technical Architecture
### 14.1 System Components
#### Event Ingestion Layer
- HTTP/WebSocket endpoints for event reception
- Schema validation and transformation
- Event buffering and queuing
- Real-time event correlation engine with AI-powered pattern recognition
- Event quality scoring and anomaly detection
#### Master Data Enrichment Layer
- **MasterDataRepository.fs**: In-memory master data storage with concurrent collections
- **EventEnrichmentEngine.fs**: Rule-based enrichment engine with configurable rules
- **EventPublisher.fs**: Publishing system for enriched events to ProductionPlanner
- **MasterDataIngestionService.fs**: Service for loading master data from external sources
- **NexusEnrichmentCoordinator.fs**: Main orchestration with retry logic and monitoring
##### Enrichment Pipeline Components:
```fsharp
// Core enrichment interfaces
type IMasterDataRepository = // Master data storage operations
type IEventEnrichmentEngine = // Rule-based enrichment logic
type IEventPublisher = // Publishing to downstream systems
type IMasterDataIngestionService = // External data loading
type INexusEnrichmentCoordinator = // Main orchestration
```
##### Enrichment Rules Engine:
```fsharp
type EnrichmentRule = {
Name: string
AppliesTo: string list // Event types this rule applies to
EnrichFunction: EnrichmentContext -> Task<EnrichmentResult>
}
// Built-in rules for common scenarios
let orderEnrichmentRule = createOrderEnrichmentRule()
let productionOrderEnrichmentRule = createProductionOrderEnrichmentRule()
```
#### Processing Layer
- Akka.NET actor system for event processing with persistent actors
- Event sourcing with EventStoreDB streams (append-only, ordered)
- Business rule engine integration for automated decision-making
- AI/ML pipeline integration for predictive analytics
- Saga patterns for complex workflow orchestration
- **Nexus Enrichment Coordinator**: Orchestrates enrichment pipeline with monitoring
#### Storage Layer
- EventStoreDB for event persistence and CQRS implementation
- Read model projections for optimized query performance
- **Master Data Repository**: In-memory storage with thread-safe concurrent collections
- Time-series databases for KPI and analytics data
- Distributed caching for high-performance data access
- **Master Data Ingestion**: Automated loading from ERP/PLM systems
#### Integration Layer
- REST APIs for external systems with GraphQL support
- WebSocket/SignalR for real-time bidirectional communication
- Message queues (Kafka/EventStore) for async communication
- **Event Publishing System**: Dedicated publishing to ProductionPlanner streams
- API mesh architecture for intelligent orchestration
- Multi-protocol support (MQTT, OPC-UA) for IoT integration
#### AI/ML Layer
- Transformer-based forecasting models with 95%+ accuracy
- Causal AI for explainable root cause analysis
- Generative AI for natural language scenario planning
- Federated learning for distributed model training
- Edge AI capabilities for on-site intelligence
- Bias detection and mitigation frameworks
#### Visualization Layer
- 3D digital twin visualization with AR/VR support
- Real-time KPI dashboards with predictive forecasting
- Spatial analytics and heat maps for bottleneck identification
- Voice-activated natural language interfaces
- Gesture-based and multi-modal interaction systems
### 14.2 Data Flow Architecture
```
External Systems (SAP, WMS, IoT)
Medhavi.Integrator (Integration Layer)
↓ (EventStoreDB - External Stream)
Medhāvī Nexus (Control Tower)
┌─────────────────────────────────────┐
│ NEXUS ENRICHMENT PIPELINE │
├─────────────────────────────────────┤
│ 1. EventStoreReader │
│ → Consumes external events │
│ │
│ 2. NexusEnrichmentCoordinator │
│ → Orchestrates enrichment │
│ │
│ 3. MasterDataRepository │
│ → Provides master data context │
│ │
│ 4. EventEnrichmentEngine │
│ → Applies enrichment rules │
│ │
│ 5. EventPublisher │
│ → Publishes to ProductionPlanner │
└─────────────────────────────────────┘
↓ (EventStoreDB - Enriched Stream)
ProductionPlanner (APS System)
Avalonia UI (User Interface)
```
#### Detailed Enrichment Data Flow:
```mermaid
graph TD
A[External Event<br/>SAP OrderCreated] --> B[EventStoreDB<br/>External Stream]
B --> C[EventStoreReader<br/>Consumes Event]
C --> D[NexusEnrichmentCoordinator<br/>Orchestrates Pipeline]
D --> E[MasterDataRepository<br/>Load Context]
E --> F[EventEnrichmentEngine<br/>Apply Rules]
F --> G{Enrichment<br/>Result}
G -->|Enriched| H[EventPublisher<br/>Publish Enriched]
G -->|Not Enriched| I[EventPublisher<br/>Publish Original]
H --> J[EventStoreDB<br/>ProductionPlanner Stream]
I --> J
J --> K[ProductionPlanner<br/>Consume Enriched Events]
K --> L[Planning Algorithms<br/>Use Enriched Data]
```
#### Master Data Flow:
```mermaid
graph TD
A[ERP/PLM Systems] --> B[MasterDataIngestionService]
B --> C[MasterDataRepository<br/>Store Master Data]
C --> D[EventEnrichmentEngine<br/>Load Context]
D --> E[Enrichment Rules<br/>Apply to Events]
E --> F[Enriched Events<br/>With Master Data]
F --> G[ProductionPlanner<br/>Enhanced Planning]
```
#### Key Data Flow Characteristics:
1. **Event-Driven Pipeline**: All enrichment triggered by incoming events
2. **Master Data Context**: Loaded on-demand for each enrichment operation
3. **Rule-Based Processing**: Configurable rules determine enrichment logic
4. **Fault-Tolerant Publishing**: Original events published if enrichment fails
5. **Real-Time Enrichment**: Sub-second processing with in-memory master data
6. **Observable Pipeline**: Comprehensive metrics and monitoring throughout
### 14.3 Scalability Analysis
**Current Load:** 1000 events/second
**Target Load:** 10,000 events/second
**Scaling Strategy:** Horizontal scaling with Kubernetes
**Performance Baseline:** Sub-200ms processing latency
**Scalability Requirements:**
- **Horizontal Scaling**: Support for multiple Nexus instances
- **State Management**: Distributed digital twin state
- **Model Serving**: Scalable AI/ML model inference
- **Event Streaming**: High-throughput event processing (10,000+ events/second)
- **Pattern Recognition**: 1,000+ patterns/second
- **Digital Twin Updates**: 100+ state updates/second
- **KPI Calculations**: 50+ metric updates/second
- **AI Inferences**: 100+ predictions/second
### 14.4 Integration & Communication Patterns
#### Event-Driven Fabric
All communication is event-based. Medhavi.Integrator writes events into EventStore streams (one stream per aggregate or logical source). Nexus and other modules subscribe to the relevant streams. For example, each Production Planning aggregate might listen for InventoryUpdated or SalesOrderConfirmed events. This decoupling allows teams to work independently on each module's code.
#### CQRS & APIs
The design follows CQRS: command handlers process incoming commands (or events), append events to the store, and read-side projections answer queries. RESTful and GraphQL APIs are exposed for front-end apps or external queries (e.g. a dashboard fetches the current schedule). Webhooks or messaging (Kafka, SignalR) can push critical events out to partners.
#### Bounded Contexts
Nexus, ProductionPlanner, and Integrator each form a distinct bounded context in the DDD sense. Their data models and events are encapsulated. Integration between them happens only through well-defined events and APIs. For instance, a ReplenishmentSuggested event from Nexus would be the trigger for Production Planning to create a PurchaseOrder internally. This clear separation simplifies maintenance and scaling.
#### Real-Time Event Flow
In practice, incoming data is received by an actor or service, normalized, validated, and then appended to the event store. Downstream services (like Nexus) subscribe to these event streams. For example, when Integrator writes InventoryAdjusted into the event store, Nexus can immediately consume it to update KPIs or trigger re-planning.
#### Nexus Event Enrichment Communication Patterns
##### Enrichment Pipeline Communication:
```mermaid
sequenceDiagram
participant ESR as EventStoreReader
participant NEC as NexusEnrichmentCoordinator
participant MDR as MasterDataRepository
participant EEE as EventEnrichmentEngine
participant EP as EventPublisher
participant PP as ProductionPlanner
ESR->>NEC: SupplyChainEvent (from external stream)
NEC->>MDR: Load master data context
MDR-->>NEC: Master data context
NEC->>EEE: Enrich event with rules
EEE-->>NEC: Enriched payload
NEC->>EP: Publish enriched event
EP->>PP: Enriched event (to internal stream)
EP-->>NEC: Publishing confirmation
NEC-->>ESR: Processing complete
```
##### Master Data Synchronization:
```mermaid
graph TD
A[ERP/PLM Systems] --> B[MasterDataIngestionService]
B --> C[JSON Deserialization]
C --> D[Domain Object Conversion]
D --> E[MasterDataRepository]
E --> F[ConcurrentDictionary Storage]
F --> G[EventEnrichmentEngine]
G --> H[Enrichment Rules]
H --> I[Enriched Events]
```
##### Error Handling & Resilience:
- **Circuit Breaker Pattern**: Protects against master data repository failures
- **Retry Logic**: Configurable retry attempts for transient failures
- **Fallback Publishing**: Original events published if enrichment fails
- **Dead Letter Queues**: Failed events queued for manual review
- **Health Monitoring**: Real-time monitoring of enrichment pipeline health
##### Performance Optimization:
- **Concurrent Processing**: Parallel enrichment of multiple events
- **Lazy Loading**: Master data loaded on-demand to reduce memory usage
- **Caching Strategy**: In-memory caching of frequently accessed master data
- **Batch Operations**: Efficient bulk processing for high-throughput scenarios
- **Async Processing**: Non-blocking operations throughout the pipeline
---
## 15. Appendix B: Terminology
| Term | Description |
| -------------------------- | -------------------------------------------------------------------------- |
| Medhavi.Integrator | Integration layer that connects to external systems (SAP, WMS, IoT) |
| Event | A structured message representing a business occurrence |
| Event Sourcing | Persistence pattern storing state changes as events |
| CQRS | Command Query Responsibility Segregation pattern |
| Bounded Context | A boundary within which a domain model is consistent |
| Actor | Concurrent computation primitive in Akka.NET |
| EventStoreDB | Purpose-built database for event storage |
| Nexus | The central control tower component for supply chain orchestration |
| ProductionPlanner | Advanced Planning & Scheduling system (separate bounded context) |
| Master Data | Reference data shared across bounded contexts |
| Control Tower | Central orchestration and visibility platform |
| Event Enrichment | Process of adding contextual master data to events for enhanced processing |
| MasterDataRepository | In-memory storage system for master data with thread-safe operations |
| EventEnrichmentEngine | Rule-based engine that applies enrichment logic to events |
| EnrichmentRule | Configurable rule defining how to enrich specific event types |
| EnrichmentContext | Runtime context containing master data for enrichment operations |
| EnrichmentCoordinator | Main orchestrator managing the complete enrichment pipeline |
| EnrichmentPipeline | End-to-end process from event reception to enriched event publishing |
| EnrichmentResult | Outcome of enrichment operation (Enriched, NotEnriched, or Error) |
| EventPublisher | Component responsible for publishing enriched events to downstream systems |
| MasterDataIngestionService | Service for loading master data from external sources into repository |
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment