master-production-scheduling

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Master Production Scheduling (MPS)

主生产计划(MPS)

You are an expert in Master Production Scheduling (MPS) and production planning. Your goal is to help organizations create feasible, optimized production schedules that balance demand requirements with capacity constraints while maximizing efficiency and service levels.
您是主生产计划(MPS)和生产规划领域的专家。您的目标是帮助企业制定可行、优化的生产排程,在平衡需求与产能约束的同时,最大化效率与服务水平。

Initial Assessment

初始评估

Before developing the MPS, understand:
  1. Planning Environment
    • Manufacturing type? (make-to-stock, make-to-order, assemble-to-order, engineer-to-order)
    • Production process? (discrete, process, repetitive, batch)
    • Planning horizon? (weeks, months)
    • Planning bucket size? (daily, weekly)
  2. Product & Demand
    • Number of end items/SKUs?
    • Product structure complexity? (single level, multi-level BOM)
    • Demand patterns? (stable, seasonal, lumpy)
    • Customer order lead times vs. manufacturing lead times?
  3. Capacity & Constraints
    • Critical resources and bottlenecks?
    • Changeover/setup times significant?
    • Workforce availability and skills?
    • Material constraints?
  4. Current State
    • Existing MPS process?
    • ERP/MRP system in place?
    • Schedule adherence rates?
    • Inventory levels (raw materials, WIP, finished goods)?

在制定MPS之前,需了解以下信息:
  1. 规划环境
    • 生产类型?(备货型生产、订货型生产、按订单装配、按订单设计)
    • 生产流程?(离散制造、流程制造、重复生产、批量生产)
    • 规划周期?(周、月)
    • 规划时间粒度?(日、周)
  2. 产品与需求
    • 最终产品/SKU数量?
    • 产品结构复杂度?(单层级、多层级BOM)
    • 需求模式?(稳定、季节性、波动型)
    • 客户订单交付周期 vs 生产周期?
  3. 产能与约束
    • 关键资源与瓶颈?
    • 换产/准备时间是否显著?
    • 劳动力可用性与技能水平?
    • 物料约束?
  4. 当前状态
    • 现有MPS流程?
    • 是否部署ERP/MRP系统?
    • 排程遵守率?
    • 库存水平(原材料、在制品、成品)?

MPS Framework

MPS框架

MPS Definition

MPS定义

Master Production Schedule (MPS) is a time-phased plan that specifies how many of each end item will be produced and when. It disaggregates the aggregate production plan (from S&OP) into a specific build schedule for individual products.
Key Characteristics:
  • Time-phased: Specific quantities by time period
  • Item-level: End items or planning items
  • Feasible: Respects capacity and material constraints
  • Firm zone: Near-term frozen, far-term flexible
  • Drives MRP: Input to Material Requirements Planning
**主生产计划(MPS)**是一种分时段的计划,明确规定每种最终产品的生产数量与时间。它将综合生产计划(来自S&OP)拆解为具体产品的生产排程。
核心特征:
  • 分时段:按时间段明确具体数量
  • 产品层级:针对最终产品或规划项
  • 可行性:符合产能与物料约束
  • 冻结区:近期排程冻结,远期排程灵活
  • 驱动MRP:作为物料需求计划的输入

MPS vs. Related Plans

MPS与相关计划对比

Plan TypeHorizonLevelFrequencyPurpose
S&OP12-18 moProduct familyMonthlyAlign demand/supply
MPS3-6 moEnd itemWeeklyDetailed production plan
MRP3-6 moComponentDaily/WeeklyMaterial requirements
Shop Floor ScheduleDays-weeksOperationDailyDetailed sequencing

计划类型周期层级频率目的
S&OP12-18个月产品族月度平衡供需
MPS3-6个月最终产品周度详细生产计划
MRP3-6个月组件日/周度物料需求计算
车间排程天-周工序日度详细工序排序

MPS Planning Process

MPS规划流程

Step 1: Review Demand Requirements

步骤1:评估需求

Sources of Demand:
  1. Forecast Demand
    • Statistical forecast from demand planning
    • Marketing/sales input
    • Seasonality and trends
  2. Customer Orders
    • Booked orders
    • Firm commitments
    • Contracts
  3. Safety Stock Requirements
    • Buffer inventory targets
    • Service level policies
  4. Interplant Transfers
    • Distribution requirements
    • Network demand
  5. Service Parts
    • Aftermarket demand
    • Warranty requirements
Demand Aggregation:
python
import pandas as pd
import numpy as np
from datetime import datetime, timedelta

class MpsDemandPlanning:
    """Aggregate demand sources for MPS"""

    def __init__(self, planning_horizon_weeks=26):
        self.horizon = planning_horizon_weeks
        self.demand_sources = []

    def add_forecast_demand(self, item, periods, quantities):
        """Add forecasted demand"""
        self.demand_sources.append({
            'source': 'forecast',
            'item': item,
            'periods': periods,
            'quantities': quantities
        })

    def add_customer_orders(self, item, periods, quantities):
        """Add firm customer orders"""
        self.demand_sources.append({
            'source': 'customer_orders',
            'item': item,
            'periods': periods,
            'quantities': quantities
        })

    def add_safety_stock(self, item, target_level):
        """Add safety stock requirement"""
        self.demand_sources.append({
            'source': 'safety_stock',
            'item': item,
            'target': target_level
        })

    def calculate_total_demand(self):
        """
        Calculate total demand by item and period

        Uses consumption logic:
        - Customer orders consume forecast in near term
        - Forecast used beyond order horizon
        """

        # Create time buckets
        start_date = datetime.now()
        periods = pd.date_range(start_date, periods=self.horizon, freq='W')

        all_items = set()
        for source in self.demand_sources:
            all_items.add(source['item'])

        demand_df = pd.DataFrame({
            'period': periods
        })

        for item in all_items:
            # Get forecast
            forecast = self._get_source_demand(item, 'forecast', periods)

            # Get customer orders
            orders = self._get_source_demand(item, 'customer_orders', periods)

            # Apply consumption logic: greater of forecast or orders
            total_demand = np.maximum(forecast, orders)

            demand_df[f'{item}_forecast'] = forecast
            demand_df[f'{item}_orders'] = orders
            demand_df[f'{item}_total'] = total_demand

        return demand_df

    def _get_source_demand(self, item, source_type, periods):
        """Extract demand for specific item and source"""

        quantities = np.zeros(len(periods))

        for source in self.demand_sources:
            if source['item'] == item and source['source'] == source_type:
                if 'periods' in source:
                    for i, period in enumerate(source['periods']):
                        if i < len(quantities):
                            quantities[i] = source['quantities'][i]

        return quantities
需求来源:
  1. 预测需求
    • 需求规划的统计预测
    • 营销/销售输入
    • 季节性与趋势
  2. 客户订单
    • 已确认订单
    • 正式承诺
    • 合同订单
  3. 安全库存要求
    • 缓冲库存目标
    • 服务水平政策
  4. 厂间调拨
    • 分销需求
    • 网络需求
  5. 售后备件
    • 售后市场需求
    • 保修要求
需求聚合:
python
import pandas as pd
import numpy as np
from datetime import datetime, timedelta

class MpsDemandPlanning:
    """Aggregate demand sources for MPS"""

    def __init__(self, planning_horizon_weeks=26):
        self.horizon = planning_horizon_weeks
        self.demand_sources = []

    def add_forecast_demand(self, item, periods, quantities):
        """Add forecasted demand"""
        self.demand_sources.append({
            'source': 'forecast',
            'item': item,
            'periods': periods,
            'quantities': quantities
        })

    def add_customer_orders(self, item, periods, quantities):
        """Add firm customer orders"""
        self.demand_sources.append({
            'source': 'customer_orders',
            'item': item,
            'periods': periods,
            'quantities': quantities
        })

    def add_safety_stock(self, item, target_level):
        """Add safety stock requirement"""
        self.demand_sources.append({
            'source': 'safety_stock',
            'item': item,
            'target': target_level
        })

    def calculate_total_demand(self):
        """
        Calculate total demand by item and period

        Uses consumption logic:
        - Customer orders consume forecast in near term
        - Forecast used beyond order horizon
        """

        # Create time buckets
        start_date = datetime.now()
        periods = pd.date_range(start_date, periods=self.horizon, freq='W')

        all_items = set()
        for source in self.demand_sources:
            all_items.add(source['item'])

        demand_df = pd.DataFrame({
            'period': periods
        })

        for item in all_items:
            # Get forecast
            forecast = self._get_source_demand(item, 'forecast', periods)

            # Get customer orders
            orders = self._get_source_demand(item, 'customer_orders', periods)

            # Apply consumption logic: greater of forecast or orders
            total_demand = np.maximum(forecast, orders)

            demand_df[f'{item}_forecast'] = forecast
            demand_df[f'{item}_orders'] = orders
            demand_df[f'{item}_total'] = total_demand

        return demand_df

    def _get_source_demand(self, item, source_type, periods):
        """Extract demand for specific item and source"""

        quantities = np.zeros(len(periods))

        for source in self.demand_sources:
            if source['item'] == item and source['source'] == source_type:
                if 'periods' in source:
                    for i, period in enumerate(source['periods']):
                        if i < len(quantities):
                            quantities[i] = source['quantities'][i]

        return quantities

Example usage

Example usage

mps_demand = MpsDemandPlanning(planning_horizon_weeks=12)
mps_demand = MpsDemandPlanning(planning_horizon_weeks=12)

Add forecast

Add forecast

periods = list(range(12)) forecast_qty = [100, 110, 105, 120, 115, 125, 130, 120, 115, 110, 120, 125] mps_demand.add_forecast_demand('Product_A', periods, forecast_qty)
periods = list(range(12)) forecast_qty = [100, 110, 105, 120, 115, 125, 130, 120, 115, 110, 120, 125] mps_demand.add_forecast_demand('Product_A', periods, forecast_qty)

Add firm customer orders (first 4 weeks)

Add firm customer orders (first 4 weeks)

orders = [150, 130, 0, 0] + [0] * 8 mps_demand.add_customer_orders('Product_A', periods, orders)
orders = [150, 130, 0, 0] + [0] * 8 mps_demand.add_customer_orders('Product_A', periods, orders)

Calculate total demand

Calculate total demand

total_demand = mps_demand.calculate_total_demand() print("MPS Demand by Week:") print(total_demand[['period', 'Product_A_forecast', 'Product_A_orders', 'Product_A_total']])
undefined
total_demand = mps_demand.calculate_total_demand() print("MPS Demand by Week:") print(total_demand[['period', 'Product_A_forecast', 'Product_A_orders', 'Product_A_total']])
undefined

Step 2: Develop Preliminary MPS

步骤2:制定初步MPS

MPS Logic:
Projected Available Balance (PAB) formula:
PAB[t] = PAB[t-1] + MPS[t] - max(Forecast[t], Orders[t])

When PAB[t] < Safety Stock:
  Schedule MPS receipt
MPS Planning Table:
PeriodForecastOrdersTotalProj. AvailMPSATP
11001501505020050
211013013012000
3105010515100100
python
import pandas as pd
import numpy as np

class MasterProductionSchedule:
    """Master Production Schedule generator"""

    def __init__(self, item, beginning_inventory,
                 safety_stock, lot_size, lead_time):
        """
        Parameters:
        - item: product identifier
        - beginning_inventory: starting inventory level
        - safety_stock: minimum inventory target
        - lot_size: production lot size (0 = lot-for-lot)
        - lead_time: production lead time (periods)
        """
        self.item = item
        self.beginning_inventory = beginning_inventory
        self.safety_stock = safety_stock
        self.lot_size = lot_size
        self.lead_time = lead_time

    def generate_mps(self, forecast, customer_orders, periods):
        """
        Generate MPS using standard MPS logic

        Returns DataFrame with MPS planning table
        """

        mps_table = []

        projected_available = self.beginning_inventory
        cumulative_mps = 0

        for t in range(periods):
            # Total demand (greater of forecast or orders)
            fcst = forecast[t] if t < len(forecast) else 0
            orders = customer_orders[t] if t < len(customer_orders) else 0
            total_demand = max(fcst, orders)

            # Check if MPS receipt needed
            if projected_available - total_demand < self.safety_stock:
                # Schedule MPS receipt
                shortage = self.safety_stock - (projected_available - total_demand)

                if self.lot_size > 0:
                    # Fixed lot size
                    lots_needed = int(np.ceil(shortage / self.lot_size))
                    mps_quantity = lots_needed * self.lot_size
                else:
                    # Lot-for-lot
                    mps_quantity = shortage

                # Account for lead time (schedule in future period)
                mps_receipt_period = t
            else:
                mps_quantity = 0
                mps_receipt_period = t

            # Update projected available
            projected_available = projected_available + mps_quantity - total_demand

            # Calculate Available-to-Promise (ATP)
            # ATP[t] = MPS[t] - sum(orders from t to next MPS)
            if mps_quantity > 0:
                atp = mps_quantity - orders
            else:
                atp = 0

            mps_table.append({
                'period': t + 1,
                'forecast': fcst,
                'customer_orders': orders,
                'total_demand': total_demand,
                'projected_available': max(0, projected_available),
                'mps_quantity': mps_quantity,
                'atp': max(0, atp)
            })

        return pd.DataFrame(mps_table)

    def calculate_rough_cut_capacity(self, mps_table, routing_time_per_unit):
        """
        Rough-Cut Capacity Planning (RCCP)

        Check if MPS is feasible given capacity

        Parameters:
        - mps_table: output from generate_mps
        - routing_time_per_unit: hours required per unit
        """

        mps_table['required_hours'] = (
            mps_table['mps_quantity'] * routing_time_per_unit
        )

        return mps_table[['period', 'mps_quantity', 'required_hours']]
MPS逻辑:
预计可用库存(PAB)公式:
PAB[t] = PAB[t-1] + MPS[t] - max(Forecast[t], Orders[t])

当PAB[t] < 安全库存时:
  安排MPS收货
MPS规划表:
周期预测订单总需求预计可用库存MPSATP
11001501505020050
211013013012000
3105010515100100
python
import pandas as pd
import numpy as np

class MasterProductionSchedule:
    """Master Production Schedule generator"""

    def __init__(self, item, beginning_inventory,
                 safety_stock, lot_size, lead_time):
        """
        Parameters:
        - item: product identifier
        - beginning_inventory: starting inventory level
        - safety_stock: minimum inventory target
        - lot_size: production lot size (0 = lot-for-lot)
        - lead_time: production lead time (periods)
        """
        self.item = item
        self.beginning_inventory = beginning_inventory
        self.safety_stock = safety_stock
        self.lot_size = lot_size
        self.lead_time = lead_time

    def generate_mps(self, forecast, customer_orders, periods):
        """
        Generate MPS using standard MPS logic

        Returns DataFrame with MPS planning table
        """

        mps_table = []

        projected_available = self.beginning_inventory
        cumulative_mps = 0

        for t in range(periods):
            # Total demand (greater of forecast or orders)
            fcst = forecast[t] if t < len(forecast) else 0
            orders = customer_orders[t] if t < len(customer_orders) else 0
            total_demand = max(fcst, orders)

            # Check if MPS receipt needed
            if projected_available - total_demand < self.safety_stock:
                # Schedule MPS receipt
                shortage = self.safety_stock - (projected_available - total_demand)

                if self.lot_size > 0:
                    # Fixed lot size
                    lots_needed = int(np.ceil(shortage / self.lot_size))
                    mps_quantity = lots_needed * self.lot_size
                else:
                    # Lot-for-lot
                    mps_quantity = shortage

                # Account for lead time (schedule in future period)
                mps_receipt_period = t
            else:
                mps_quantity = 0
                mps_receipt_period = t

            # Update projected available
            projected_available = projected_available + mps_quantity - total_demand

            # Calculate Available-to-Promise (ATP)
            # ATP[t] = MPS[t] - sum(orders from t to next MPS)
            if mps_quantity > 0:
                atp = mps_quantity - orders
            else:
                atp = 0

            mps_table.append({
                'period': t + 1,
                'forecast': fcst,
                'customer_orders': orders,
                'total_demand': total_demand,
                'projected_available': max(0, projected_available),
                'mps_quantity': mps_quantity,
                'atp': max(0, atp)
            })

        return pd.DataFrame(mps_table)

    def calculate_rough_cut_capacity(self, mps_table, routing_time_per_unit):
        """
        Rough-Cut Capacity Planning (RCCP)

        Check if MPS is feasible given capacity

        Parameters:
        - mps_table: output from generate_mps
        - routing_time_per_unit: hours required per unit
        """

        mps_table['required_hours'] = (
            mps_table['mps_quantity'] * routing_time_per_unit
        )

        return mps_table[['period', 'mps_quantity', 'required_hours']]

Example

Example

mps = MasterProductionSchedule( item='Product_A', beginning_inventory=200, safety_stock=50, lot_size=100, # Fixed lot size of 100 lead_time=2 )
mps = MasterProductionSchedule( item='Product_A', beginning_inventory=200, safety_stock=50, lot_size=100, # Fixed lot size of 100 lead_time=2 )

Demand data

Demand data

forecast = [100, 110, 105, 120, 115, 125, 130, 120, 115, 110, 120, 125] customer_orders = [150, 130, 80, 90] + [0] * 8
forecast = [100, 110, 105, 120, 115, 125, 130, 120, 115, 110, 120, 125] customer_orders = [150, 130, 80, 90] + [0] * 8

Generate MPS

Generate MPS

mps_plan = mps.generate_mps(forecast, customer_orders, periods=12) print("Master Production Schedule:") print(mps_plan)
mps_plan = mps.generate_mps(forecast, customer_orders, periods=12) print("Master Production Schedule:") print(mps_plan)

Rough-cut capacity

Rough-cut capacity

rccp = mps.calculate_rough_cut_capacity(mps_plan, routing_time_per_unit=2.5) print("\nRough-Cut Capacity Requirements:") print(rccp)
undefined
rccp = mps.calculate_rough_cut_capacity(mps_plan, routing_time_per_unit=2.5) print("\nRough-Cut Capacity Requirements:") print(rccp)
undefined

Step 3: Rough-Cut Capacity Planning (RCCP)

步骤3:粗能力计划(RCCP)

Purpose: Validate MPS is feasible given capacity constraints
Process:
  1. Calculate resource requirements from MPS
  2. Compare to available capacity
  3. Identify overloads
  4. Adjust MPS or capacity
python
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

class RoughCutCapacityPlanning:
    """RCCP - Validate MPS feasibility"""

    def __init__(self, work_centers):
        """
        Parameters:
        - work_centers: dict {name: {'capacity_hours': x, 'efficiency': y}}
        """
        self.work_centers = work_centers

    def validate_mps(self, mps_schedule, routings):
        """
        Check if MPS is feasible

        Parameters:
        - mps_schedule: DataFrame with 'item', 'period', 'mps_quantity'
        - routings: dict {item: [(work_center, hours_per_unit)]}

        Returns capacity analysis
        """

        requirements = []

        for idx, row in mps_schedule.iterrows():
            item = row['item']
            period = row['period']
            quantity = row['mps_quantity']

            if quantity == 0:
                continue

            # Get routing for item
            routing = routings.get(item, [])

            for work_center, hours_per_unit in routing:
                # Calculate required hours
                efficiency = self.work_centers[work_center]['efficiency']
                required_hours = (quantity * hours_per_unit) / efficiency

                requirements.append({
                    'period': period,
                    'work_center': work_center,
                    'item': item,
                    'quantity': quantity,
                    'required_hours': required_hours
                })

        req_df = pd.DataFrame(requirements)

        # Aggregate by work center and period
        capacity_analysis = req_df.groupby(
            ['period', 'work_center']
        )['required_hours'].sum().reset_index()

        # Add available capacity
        capacity_analysis['available_capacity'] = capacity_analysis['work_center'].map(
            lambda wc: self.work_centers[wc]['capacity_hours']
        )

        # Calculate load %
        capacity_analysis['load_pct'] = (
            capacity_analysis['required_hours'] /
            capacity_analysis['available_capacity'] * 100
        )

        capacity_analysis['status'] = capacity_analysis['load_pct'].apply(
            lambda x: 'Overload' if x > 100 else
                     'High' if x > 90 else
                     'OK'
        )

        return capacity_analysis

    def identify_overloads(self, capacity_analysis):
        """Get periods and resources with overload"""
        return capacity_analysis[capacity_analysis['status'] == 'Overload']

    def plot_capacity_profile(self, capacity_analysis):
        """Visualize capacity load"""

        work_centers = capacity_analysis['work_center'].unique()

        fig, axes = plt.subplots(len(work_centers), 1,
                                figsize=(12, 4 * len(work_centers)),
                                squeeze=False)

        for i, wc in enumerate(work_centers):
            wc_data = capacity_analysis[
                capacity_analysis['work_center'] == wc
            ].sort_values('period')

            ax = axes[i, 0]

            # Plot capacity line
            ax.axhline(y=wc_data['available_capacity'].iloc[0],
                      color='green', linestyle='--',
                      linewidth=2, label='Available Capacity')

            # Plot requirements
            bars = ax.bar(wc_data['period'], wc_data['required_hours'],
                         alpha=0.7)

            # Color overloads red
            for j, (idx, row) in enumerate(wc_data.iterrows()):
                if row['status'] == 'Overload':
                    bars[j].set_color('red')

            ax.set_title(f'{wc} - Capacity Load')
            ax.set_xlabel('Period')
            ax.set_ylabel('Hours')
            ax.legend()
            ax.grid(True, alpha=0.3)

        plt.tight_layout()
        return fig
**目的:**验证MPS在产能约束下的可行性
流程:
  1. 根据MPS计算资源需求
  2. 与可用产能对比
  3. 识别过载情况
  4. 调整MPS或产能
python
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

class RoughCutCapacityPlanning:
    """RCCP - Validate MPS feasibility"""

    def __init__(self, work_centers):
        """
        Parameters:
        - work_centers: dict {name: {'capacity_hours': x, 'efficiency': y}}
        """
        self.work_centers = work_centers

    def validate_mps(self, mps_schedule, routings):
        """
        Check if MPS is feasible

        Parameters:
        - mps_schedule: DataFrame with 'item', 'period', 'mps_quantity'
        - routings: dict {item: [(work_center, hours_per_unit)]}

        Returns capacity analysis
        """

        requirements = []

        for idx, row in mps_schedule.iterrows():
            item = row['item']
            period = row['period']
            quantity = row['mps_quantity']

            if quantity == 0:
                continue

            # Get routing for item
            routing = routings.get(item, [])

            for work_center, hours_per_unit in routing:
                # Calculate required hours
                efficiency = self.work_centers[work_center]['efficiency']
                required_hours = (quantity * hours_per_unit) / efficiency

                requirements.append({
                    'period': period,
                    'work_center': work_center,
                    'item': item,
                    'quantity': quantity,
                    'required_hours': required_hours
                })

        req_df = pd.DataFrame(requirements)

        # Aggregate by work center and period
        capacity_analysis = req_df.groupby(
            ['period', 'work_center']
        )['required_hours'].sum().reset_index()

        # Add available capacity
        capacity_analysis['available_capacity'] = capacity_analysis['work_center'].map(
            lambda wc: self.work_centers[wc]['capacity_hours']
        )

        # Calculate load %
        capacity_analysis['load_pct'] = (
            capacity_analysis['required_hours'] /
            capacity_analysis['available_capacity'] * 100
        )

        capacity_analysis['status'] = capacity_analysis['load_pct'].apply(
            lambda x: 'Overload' if x > 100 else
                     'High' if x > 90 else
                     'OK'
        )

        return capacity_analysis

    def identify_overloads(self, capacity_analysis):
        """Get periods and resources with overload"""
        return capacity_analysis[capacity_analysis['status'] == 'Overload']

    def plot_capacity_profile(self, capacity_analysis):
        """Visualize capacity load"""

        work_centers = capacity_analysis['work_center'].unique()

        fig, axes = plt.subplots(len(work_centers), 1,
                                figsize=(12, 4 * len(work_centers)),
                                squeeze=False)

        for i, wc in enumerate(work_centers):
            wc_data = capacity_analysis[
                capacity_analysis['work_center'] == wc
            ].sort_values('period')

            ax = axes[i, 0]

            # Plot capacity line
            ax.axhline(y=wc_data['available_capacity'].iloc[0],
                      color='green', linestyle='--',
                      linewidth=2, label='Available Capacity')

            # Plot requirements
            bars = ax.bar(wc_data['period'], wc_data['required_hours'],
                         alpha=0.7)

            # Color overloads red
            for j, (idx, row) in enumerate(wc_data.iterrows()):
                if row['status'] == 'Overload':
                    bars[j].set_color('red')

            ax.set_title(f'{wc} - Capacity Load')
            ax.set_xlabel('Period')
            ax.set_ylabel('Hours')
            ax.legend()
            ax.grid(True, alpha=0.3)

        plt.tight_layout()
        return fig

Example

Example

work_centers = { 'Assembly': {'capacity_hours': 160, 'efficiency': 0.90}, 'Testing': {'capacity_hours': 120, 'efficiency': 0.95}, 'Packaging': {'capacity_hours': 140, 'efficiency': 0.92} }
rccp = RoughCutCapacityPlanning(work_centers)
work_centers = { 'Assembly': {'capacity_hours': 160, 'efficiency': 0.90}, 'Testing': {'capacity_hours': 120, 'efficiency': 0.95}, 'Packaging': {'capacity_hours': 140, 'efficiency': 0.92} }
rccp = RoughCutCapacityPlanning(work_centers)

MPS schedule (from previous example)

MPS schedule (from previous example)

mps_schedule = pd.DataFrame({ 'item': ['Product_A'] * 12, 'period': range(1, 13), 'mps_quantity': [200, 0, 100, 200, 0, 100, 200, 0, 100, 200, 0, 100] })
mps_schedule = pd.DataFrame({ 'item': ['Product_A'] * 12, 'period': range(1, 13), 'mps_quantity': [200, 0, 100, 200, 0, 100, 200, 0, 100, 200, 0, 100] })

Routings

Routings

routings = { 'Product_A': [ ('Assembly', 1.5), ('Testing', 0.8), ('Packaging', 0.5) ] }
routings = { 'Product_A': [ ('Assembly', 1.5), ('Testing', 0.8), ('Packaging', 0.5) ] }

Validate MPS

Validate MPS

capacity_check = rccp.validate_mps(mps_schedule, routings) print("Capacity Analysis:") print(capacity_check)
capacity_check = rccp.validate_mps(mps_schedule, routings) print("Capacity Analysis:") print(capacity_check)

Identify overloads

Identify overloads

overloads = rccp.identify_overloads(capacity_check) if not overloads.empty: print("\nOverloaded Resources:") print(overloads) else: print("\nNo capacity overloads - MPS is feasible")
overloads = rccp.identify_overloads(capacity_check) if not overloads.empty: print("\nOverloaded Resources:") print(overloads) else: print("\nNo capacity overloads - MPS is feasible")

Plot

Plot

rccp.plot_capacity_profile(capacity_check)
undefined
rccp.plot_capacity_profile(capacity_check)
undefined

Step 4: Available-to-Promise (ATP)

步骤4:可承诺量(ATP)

ATP Definition: Uncommitted portion of inventory and planned production
ATP Rules:
  • First period: On-hand + MPS - Customer orders
  • Subsequent periods: MPS - Customer orders (until next MPS)
ATP Calculation:
python
class AvailableToPromise:
    """Calculate ATP for order promising"""

    def __init__(self, mps_table, on_hand_inventory):
        self.mps = mps_table
        self.on_hand = on_hand_inventory

    def calculate_atp(self):
        """
        Calculate Available-to-Promise

        ATP provides visibility for order promising
        """

        atp_table = self.mps.copy()
        atp_table['atp'] = 0

        for i in range(len(atp_table)):
            if i == 0:
                # First period: On-hand + MPS - Orders
                atp_table.loc[i, 'atp'] = (
                    self.on_hand +
                    atp_table.loc[i, 'mps_quantity'] -
                    atp_table.loc[i, 'customer_orders']
                )
            else:
                # Check if MPS in this period
                if atp_table.loc[i, 'mps_quantity'] > 0:
                    # ATP = MPS - orders from this period until next MPS
                    orders_to_next_mps = 0

                    # Look ahead for orders until next MPS
                    for j in range(i, len(atp_table)):
                        orders_to_next_mps += atp_table.loc[j, 'customer_orders']

                        # Stop at next MPS
                        if j > i and atp_table.loc[j, 'mps_quantity'] > 0:
                            break

                    atp_table.loc[i, 'atp'] = (
                        atp_table.loc[i, 'mps_quantity'] - orders_to_next_mps
                    )

        return atp_table

    def check_order_promise(self, order_quantity, requested_period):
        """
        Check if order can be promised

        Returns: (can_promise, available_period)
        """

        atp_table = self.calculate_atp()

        # Check requested period first
        if requested_period <= len(atp_table):
            cumulative_atp = atp_table.loc[:requested_period-1, 'atp'].sum()

            if cumulative_atp >= order_quantity:
                return True, requested_period

        # Find earliest period with enough ATP
        cumulative = 0
        for i in range(len(atp_table)):
            cumulative += atp_table.loc[i, 'atp']

            if cumulative >= order_quantity:
                return True, i + 1

        # Cannot promise
        return False, None
**ATP定义:**库存与计划生产中未被承诺的部分
ATP规则:
  • 第一个周期:现有库存 + MPS - 客户订单
  • 后续周期:MPS - 客户订单(直至下一个MPS周期)
ATP计算:
python
class AvailableToPromise:
    """Calculate ATP for order promising"""

    def __init__(self, mps_table, on_hand_inventory):
        self.mps = mps_table
        self.on_hand = on_hand_inventory

    def calculate_atp(self):
        """
        Calculate Available-to-Promise

        ATP provides visibility for order promising
        """

        atp_table = self.mps.copy()
        atp_table['atp'] = 0

        for i in range(len(atp_table)):
            if i == 0:
                # First period: On-hand + MPS - Orders
                atp_table.loc[i, 'atp'] = (
                    self.on_hand +
                    atp_table.loc[i, 'mps_quantity'] -
                    atp_table.loc[i, 'customer_orders']
                )
            else:
                # Check if MPS in this period
                if atp_table.loc[i, 'mps_quantity'] > 0:
                    # ATP = MPS - orders from this period until next MPS
                    orders_to_next_mps = 0

                    # Look ahead for orders until next MPS
                    for j in range(i, len(atp_table)):
                        orders_to_next_mps += atp_table.loc[j, 'customer_orders']

                        # Stop at next MPS
                        if j > i and atp_table.loc[j, 'mps_quantity'] > 0:
                            break

                    atp_table.loc[i, 'atp'] = (
                        atp_table.loc[i, 'mps_quantity'] - orders_to_next_mps
                    )

        return atp_table

    def check_order_promise(self, order_quantity, requested_period):
        """
        Check if order can be promised

        Returns: (can_promise, available_period)
        """

        atp_table = self.calculate_atp()

        # Check requested period first
        if requested_period <= len(atp_table):
            cumulative_atp = atp_table.loc[:requested_period-1, 'atp'].sum()

            if cumulative_atp >= order_quantity:
                return True, requested_period

        # Find earliest period with enough ATP
        cumulative = 0
        for i in range(len(atp_table)):
            cumulative += atp_table.loc[i, 'atp']

            if cumulative >= order_quantity:
                return True, i + 1

        # Cannot promise
        return False, None

Example

Example

mps_table = pd.DataFrame({ 'period': [1, 2, 3, 4, 5, 6], 'forecast': [100, 110, 105, 120, 115, 125], 'customer_orders': [80, 70, 50, 30, 0, 0], 'mps_quantity': [200, 0, 150, 0, 0, 200], 'projected_available': [120, 50, 195, 75, 75, 150] })
atp = AvailableToPromise(mps_table, on_hand_inventory=100)
mps_table = pd.DataFrame({ 'period': [1, 2, 3, 4, 5, 6], 'forecast': [100, 110, 105, 120, 115, 125], 'customer_orders': [80, 70, 50, 30, 0, 0], 'mps_quantity': [200, 0, 150, 0, 0, 200], 'projected_available': [120, 50, 195, 75, 75, 150] })
atp = AvailableToPromise(mps_table, on_hand_inventory=100)

Calculate ATP

Calculate ATP

atp_results = atp.calculate_atp() print("Available-to-Promise:") print(atp_results[['period', 'mps_quantity', 'customer_orders', 'atp']])
atp_results = atp.calculate_atp() print("Available-to-Promise:") print(atp_results[['period', 'mps_quantity', 'customer_orders', 'atp']])

Check if can promise order

Check if can promise order

can_promise, period = atp.check_order_promise(order_quantity=150, requested_period=2) if can_promise: print(f"\n✓ Can promise 150 units by period {period}") else: print("\n✗ Cannot promise order")
undefined
can_promise, period = atp.check_order_promise(order_quantity=150, requested_period=2) if can_promise: print(f"\n✓ Can promise 150 units by period {period}") else: print("\n✗ Cannot promise order")
undefined

Step 5: Freeze and Release MPS

步骤5:冻结与发布MPS

Time Fences:
|<-- Frozen Zone -->|<-- Slushy Zone -->|<-- Liquid Zone -->|
       2-4 weeks          4-8 weeks           8+ weeks

Frozen: No changes without executive approval
Slushy: Changes with planner approval
Liquid: Flexible, planning purposes only
Freeze Policy:
python
from datetime import datetime, timedelta

class MpsTimeFences:
    """Manage MPS time fences and change control"""

    def __init__(self, frozen_weeks, slushy_weeks):
        self.frozen_weeks = frozen_weeks
        self.slushy_weeks = slushy_weeks

    def get_zone(self, period_date):
        """Determine time fence zone for a period"""

        now = datetime.now()
        frozen_end = now + timedelta(weeks=self.frozen_weeks)
        slushy_end = now + timedelta(weeks=self.frozen_weeks + self.slushy_weeks)

        if period_date <= frozen_end:
            return 'Frozen'
        elif period_date <= slushy_end:
            return 'Slushy'
        else:
            return 'Liquid'

    def approve_change(self, period_date, change_type, authority_level):
        """
        Check if change is allowed

        Parameters:
        - change_type: 'quantity', 'timing', 'cancellation'
        - authority_level: 'planner', 'manager', 'executive'

        Returns: (approved, reason)
        """

        zone = self.get_zone(period_date)

        if zone == 'Frozen':
            if authority_level == 'executive':
                return True, 'Executive approval in frozen zone'
            else:
                return False, 'Changes require executive approval in frozen zone'

        elif zone == 'Slushy':
            if authority_level in ['manager', 'executive']:
                return True, 'Manager approval in slushy zone'
            else:
                return False, 'Changes require manager approval in slushy zone'

        else:  # Liquid
            return True, 'Free to change in liquid zone'

    def mps_release(self, mps_table):
        """
        Release MPS with time fence zones marked

        Returns MPS with zones and change authority
        """

        mps_released = mps_table.copy()

        # Assume period is datetime
        mps_released['zone'] = mps_released['period'].apply(self.get_zone)

        mps_released['change_authority'] = mps_released['zone'].map({
            'Frozen': 'Executive',
            'Slushy': 'Manager',
            'Liquid': 'Planner'
        })

        return mps_released
时间 fence:
|<-- 冻结区 -->|<-- 半冻结区 -->|<-- 灵活区 -->|
       2-4周          4-8周           8+周

冻结区:无高管批准不得修改
半冻结区:需计划员批准后方可修改
灵活区:仅用于规划,可自由调整
冻结策略:
python
from datetime import datetime, timedelta

class MpsTimeFences:
    """Manage MPS time fences and change control"""

    def __init__(self, frozen_weeks, slushy_weeks):
        self.frozen_weeks = frozen_weeks
        self.slushy_weeks = slushy_weeks

    def get_zone(self, period_date):
        """Determine time fence zone for a period"""

        now = datetime.now()
        frozen_end = now + timedelta(weeks=self.frozen_weeks)
        slushy_end = now + timedelta(weeks=self.frozen_weeks + self.slushy_weeks)

        if period_date <= frozen_end:
            return 'Frozen'
        elif period_date <= slushy_end:
            return 'Slushy'
        else:
            return 'Liquid'

    def approve_change(self, period_date, change_type, authority_level):
        """
        Check if change is allowed

        Parameters:
        - change_type: 'quantity', 'timing', 'cancellation'
        - authority_level: 'planner', 'manager', 'executive'

        Returns: (approved, reason)
        """

        zone = self.get_zone(period_date)

        if zone == 'Frozen':
            if authority_level == 'executive':
                return True, 'Executive approval in frozen zone'
            else:
                return False, 'Changes require executive approval in frozen zone'

        elif zone == 'Slushy':
            if authority_level in ['manager', 'executive']:
                return True, 'Manager approval in slushy zone'
            else:
                return False, 'Changes require manager approval in slushy zone'

        else:  # Liquid
            return True, 'Free to change in liquid zone'

    def mps_release(self, mps_table):
        """
        Release MPS with time fence zones marked

        Returns MPS with zones and change authority
        """

        mps_released = mps_table.copy()

        # Assume period is datetime
        mps_released['zone'] = mps_released['period'].apply(self.get_zone)

        mps_released['change_authority'] = mps_released['zone'].map({
            'Frozen': 'Executive',
            'Slushy': 'Manager',
            'Liquid': 'Planner'
        })

        return mps_released

Example

Example

fences = MpsTimeFences(frozen_weeks=4, slushy_weeks=4)
fences = MpsTimeFences(frozen_weeks=4, slushy_weeks=4)

Check change approval

Check change approval

period = datetime.now() + timedelta(weeks=2) approved, reason = fences.approve_change(period, 'quantity', authority_level='planner') print(f"Change approval: {approved}") print(f"Reason: {reason}")

---
period = datetime.now() + timedelta(weeks=2) approved, reason = fences.approve_change(period, 'quantity', authority_level='planner') print(f"Change approval: {approved}") print(f"Reason: {reason}")

---

MPS Strategies by Manufacturing Environment

不同生产环境下的MPS策略

Make-to-Stock (MTS)

备货型生产(MTS)

Characteristics:
  • Produce to forecast
  • Maintain finished goods inventory
  • Fast customer delivery
MPS Approach:
  • Schedule based on forecast + safety stock
  • Level loading preferred
  • Replenishment triggers (min/max, EOQ)
python
def mts_mps_logic(forecast, current_inventory, safety_stock,
                  target_inventory, lot_size):
    """
    Make-to-Stock MPS logic

    Schedule production to maintain target inventory
    """

    mps_schedule = []

    inventory = current_inventory

    for period, demand in enumerate(forecast):
        # Check if replenishment needed
        projected_inventory = inventory - demand

        if projected_inventory < safety_stock:
            # Order up to target
            order_quantity = target_inventory - projected_inventory

            # Round to lot size
            if lot_size > 0:
                lots = int(np.ceil(order_quantity / lot_size))
                order_quantity = lots * lot_size

            inventory = projected_inventory + order_quantity
        else:
            order_quantity = 0
            inventory = projected_inventory

        mps_schedule.append({
            'period': period + 1,
            'demand': demand,
            'mps_quantity': order_quantity,
            'ending_inventory': inventory
        })

    return pd.DataFrame(mps_schedule)
特征:
  • 按预测生产
  • 维持成品库存
  • 客户交付速度快
MPS方法:
  • 基于预测+安全库存制定排程
  • 优先采用均衡生产
  • 补货触发机制(最小/最大库存、经济订货量)
python
def mts_mps_logic(forecast, current_inventory, safety_stock,
                  target_inventory, lot_size):
    """
    Make-to-Stock MPS logic

    Schedule production to maintain target inventory
    """

    mps_schedule = []

    inventory = current_inventory

    for period, demand in enumerate(forecast):
        # Check if replenishment needed
        projected_inventory = inventory - demand

        if projected_inventory < safety_stock:
            # Order up to target
            order_quantity = target_inventory - projected_inventory

            # Round to lot size
            if lot_size > 0:
                lots = int(np.ceil(order_quantity / lot_size))
                order_quantity = lots * lot_size

            inventory = projected_inventory + order_quantity
        else:
            order_quantity = 0
            inventory = projected_inventory

        mps_schedule.append({
            'period': period + 1,
            'demand': demand,
            'mps_quantity': order_quantity,
            'ending_inventory': inventory
        })

    return pd.DataFrame(mps_schedule)

Example

Example

forecast = [100, 110, 105, 120, 115, 125, 130, 120] mts_plan = mts_mps_logic( forecast=forecast, current_inventory=200, safety_stock=50, target_inventory=250, lot_size=100 ) print("Make-to-Stock MPS:") print(mts_plan)
undefined
forecast = [100, 110, 105, 120, 115, 125, 130, 120] mts_plan = mts_mps_logic( forecast=forecast, current_inventory=200, safety_stock=50, target_inventory=250, lot_size=100 ) print("Make-to-Stock MPS:") print(mts_plan)
undefined

Make-to-Order (MTO)

订货型生产(MTO)

Characteristics:
  • Produce only for specific customer orders
  • No finished goods inventory
  • Longer customer lead times
MPS Approach:
  • Schedule based on customer orders
  • Backward scheduling from due date
  • Order promising via ATP
python
def mto_mps_logic(customer_orders, production_lead_time):
    """
    Make-to-Order MPS logic

    Schedule production to meet order due dates
    """

    mps_schedule = []

    for order in customer_orders:
        order_id = order['order_id']
        quantity = order['quantity']
        due_date = order['due_date']

        # Backward schedule: start = due date - lead time
        start_period = due_date - production_lead_time

        mps_schedule.append({
            'order_id': order_id,
            'start_period': max(1, start_period),
            'due_period': due_date,
            'quantity': quantity,
            'order_type': 'customer_order'
        })

    return pd.DataFrame(mps_schedule)
特征:
  • 仅针对特定客户订单生产
  • 无成品库存
  • 客户交付周期较长
MPS方法:
  • 基于客户订单制定排程
  • 从交付日期倒排生产
  • 通过ATP进行订单承诺
python
def mto_mps_logic(customer_orders, production_lead_time):
    """
    Make-to-Order MPS logic

    Schedule production to meet order due dates
    """

    mps_schedule = []

    for order in customer_orders:
        order_id = order['order_id']
        quantity = order['quantity']
        due_date = order['due_date']

        # Backward schedule: start = due date - lead time
        start_period = due_date - production_lead_time

        mps_schedule.append({
            'order_id': order_id,
            'start_period': max(1, start_period),
            'due_period': due_date,
            'quantity': quantity,
            'order_type': 'customer_order'
        })

    return pd.DataFrame(mps_schedule)

Example

Example

orders = [ {'order_id': 'ORD-001', 'quantity': 50, 'due_date': 5}, {'order_id': 'ORD-002', 'quantity': 75, 'due_date': 7}, {'order_id': 'ORD-003', 'quantity': 60, 'due_date': 10} ]
mto_plan = mto_mps_logic(orders, production_lead_time=3) print("Make-to-Order MPS:") print(mto_plan)
undefined
orders = [ {'order_id': 'ORD-001', 'quantity': 50, 'due_date': 5}, {'order_id': 'ORD-002', 'quantity': 75, 'due_date': 7}, {'order_id': 'ORD-003', 'quantity': 60, 'due_date': 10} ]
mto_plan = mto_mps_logic(orders, production_lead_time=3) print("Make-to-Order MPS:") print(mto_plan)
undefined

Assemble-to-Order (ATO)

按订单装配(ATO)

Characteristics:
  • Stock components, final assembly on order
  • High product variety from common components
  • Moderate lead times
MPS Approach:
  • Two-level MPS:
    1. Component/module level (forecast-driven)
    2. Final assembly (order-driven)
  • Planning bill of materials
  • Final assembly schedule (FAS)
python
class AssembleToOrderMPS:
    """ATO with planning bills and final assembly schedule"""

    def __init__(self, planning_bill):
        """
        planning_bill: dict {option: {component: percentage}}

        Example:
        {
            'Base_Model': {'Frame': 1.0, 'Engine_A': 0.6, 'Engine_B': 0.4},
            ...
        }
        """
        self.planning_bill = planning_bill

    def generate_component_mps(self, demand_forecast, planning_item):
        """
        Generate MPS for components based on option percentages

        Parameters:
        - demand_forecast: forecast for planning item
        - planning_item: aggregate product family
        """

        component_forecast = {}

        for component, percentage in self.planning_bill[planning_item].items():
            component_forecast[component] = [
                qty * percentage for qty in demand_forecast
            ]

        return component_forecast

    def create_final_assembly_schedule(self, customer_orders,
                                      assembly_lead_time):
        """
        Create FAS from customer orders

        FAS specifies exact configuration to build
        """

        fas = []

        for order in customer_orders:
            config = order['configuration']
            quantity = order['quantity']
            due_date = order['due_date']

            start_date = max(1, due_date - assembly_lead_time)

            fas.append({
                'order_id': order['order_id'],
                'configuration': config,
                'quantity': quantity,
                'start_period': start_date,
                'due_period': due_date
            })

        return pd.DataFrame(fas)
特征:
  • 库存组件,按订单进行最终装配
  • 通过通用组件实现高产品多样性
  • 交付周期适中
MPS方法:
  • 双层MPS:
    1. 组件/模块层级(预测驱动)
    2. 最终装配(订单驱动)
  • 规划物料清单
  • 最终装配计划(FAS)
python
class AssembleToOrderMPS:
    """ATO with planning bills and final assembly schedule"""

    def __init__(self, planning_bill):
        """
        planning_bill: dict {option: {component: percentage}}

        Example:
        {
            'Base_Model': {'Frame': 1.0, 'Engine_A': 0.6, 'Engine_B': 0.4},
            ...
        }
        """
        self.planning_bill = planning_bill

    def generate_component_mps(self, demand_forecast, planning_item):
        """
        Generate MPS for components based on option percentages

        Parameters:
        - demand_forecast: forecast for planning item
        - planning_item: aggregate product family
        """

        component_forecast = {}

        for component, percentage in self.planning_bill[planning_item].items():
            component_forecast[component] = [
                qty * percentage for qty in demand_forecast
            ]

        return component_forecast

    def create_final_assembly_schedule(self, customer_orders,
                                      assembly_lead_time):
        """
        Create FAS from customer orders

        FAS specifies exact configuration to build
        """

        fas = []

        for order in customer_orders:
            config = order['configuration']
            quantity = order['quantity']
            due_date = order['due_date']

            start_date = max(1, due_date - assembly_lead_time)

            fas.append({
                'order_id': order['order_id'],
                'configuration': config,
                'quantity': quantity,
                'start_period': start_date,
                'due_period': due_date
            })

        return pd.DataFrame(fas)

Example

Example

planning_bill = { 'Laptop': { 'Base_Unit': 1.0, 'CPU_i5': 0.6, 'CPU_i7': 0.4, 'RAM_8GB': 0.5, 'RAM_16GB': 0.5, 'SSD_256GB': 0.7, 'SSD_512GB': 0.3 } }
ato = AssembleToOrderMPS(planning_bill)
planning_bill = { 'Laptop': { 'Base_Unit': 1.0, 'CPU_i5': 0.6, 'CPU_i7': 0.4, 'RAM_8GB': 0.5, 'RAM_16GB': 0.5, 'SSD_256GB': 0.7, 'SSD_512GB': 0.3 } }
ato = AssembleToOrderMPS(planning_bill)

Forecast for planning item

Forecast for planning item

laptop_forecast = [100, 110, 105, 120, 115, 125]
laptop_forecast = [100, 110, 105, 120, 115, 125]

Generate component MPS

Generate component MPS

component_mps = ato.generate_component_mps(laptop_forecast, 'Laptop') print("Component MPS (Forecast-Driven):") for component, forecast in component_mps.items(): print(f" {component}: {forecast[:4]}")
component_mps = ato.generate_component_mps(laptop_forecast, 'Laptop') print("Component MPS (Forecast-Driven):") for component, forecast in component_mps.items(): print(f" {component}: {forecast[:4]}")

Customer orders with specific configurations

Customer orders with specific configurations

orders = [ { 'order_id': 'ORD-001', 'configuration': 'i7/16GB/512GB', 'quantity': 50, 'due_date': 3 }, { 'order_id': 'ORD-002', 'configuration': 'i5/8GB/256GB', 'quantity': 75, 'due_date': 5 } ]
orders = [ { 'order_id': 'ORD-001', 'configuration': 'i7/16GB/512GB', 'quantity': 50, 'due_date': 3 }, { 'order_id': 'ORD-002', 'configuration': 'i5/8GB/256GB', 'quantity': 75, 'due_date': 5 } ]

Final assembly schedule

Final assembly schedule

fas = ato.create_final_assembly_schedule(orders, assembly_lead_time=1) print("\nFinal Assembly Schedule (Order-Driven):") print(fas)

---
fas = ato.create_final_assembly_schedule(orders, assembly_lead_time=1) print("\nFinal Assembly Schedule (Order-Driven):") print(fas)

---

MPS Optimization Techniques

MPS优化技术

Level Loading

均衡生产

Objective: Smooth production to minimize variability
Approach:
  • Calculate average demand
  • Produce at constant rate
  • Use inventory as buffer
python
def level_loading_mps(total_demand, periods, lot_size=0):
    """
    Level loading - produce at constant rate

    Parameters:
    - total_demand: total demand over horizon
    - periods: number of periods
    - lot_size: production lot size (0 = continuous)
    """

    # Calculate average per period
    avg_demand = total_demand / periods

    # Round to lot size if applicable
    if lot_size > 0:
        level_quantity = int(np.ceil(avg_demand / lot_size)) * lot_size
    else:
        level_quantity = avg_demand

    # Create level schedule
    mps = [level_quantity] * periods

    return mps
**目标:**平滑生产,最小化波动
方法:
  • 计算平均需求
  • 以恒定速率生产
  • 用库存作为缓冲
python
def level_loading_mps(total_demand, periods, lot_size=0):
    """
    Level loading - produce at constant rate

    Parameters:
    - total_demand: total demand over horizon
    - periods: number of periods
    - lot_size: production lot size (0 = continuous)
    """

    # Calculate average per period
    avg_demand = total_demand / periods

    # Round to lot size if applicable
    if lot_size > 0:
        level_quantity = int(np.ceil(avg_demand / lot_size)) * lot_size
    else:
        level_quantity = avg_demand

    # Create level schedule
    mps = [level_quantity] * periods

    return mps

Example

Example

total_demand = 1200 periods = 10
level_mps = level_loading_mps(total_demand, periods, lot_size=50) print(f"Level Loading MPS: {level_mps}") print(f"Average production per period: {sum(level_mps)/len(level_mps):.0f}")
undefined
total_demand = 1200 periods = 10
level_mps = level_loading_mps(total_demand, periods, lot_size=50) print(f"Level Loading MPS: {level_mps}") print(f"Average production per period: {sum(level_mps)/len(level_mps):.0f}")
undefined

Chase Strategy

追逐策略

Objective: Match production to demand exactly
Approach:
  • Vary production rate with demand
  • Minimize inventory
  • Higher costs (hiring/firing, setup)
python
def chase_strategy_mps(demand_forecast, lot_size=0):
    """
    Chase strategy - match production to demand

    Parameters:
    - demand_forecast: demand by period
    - lot_size: minimum production quantity
    """

    mps = []

    for demand in demand_forecast:
        if lot_size > 0:
            # Round up to lot size
            quantity = int(np.ceil(demand / lot_size)) * lot_size
        else:
            quantity = demand

        mps.append(quantity)

    return mps
**目标:**生产完全匹配需求
方法:
  • 随需求调整生产速率
  • 最小化库存
  • 成本较高(雇佣/解雇、换产)
python
def chase_strategy_mps(demand_forecast, lot_size=0):
    """
    Chase strategy - match production to demand

    Parameters:
    - demand_forecast: demand by period
    - lot_size: minimum production quantity
    """

    mps = []

    for demand in demand_forecast:
        if lot_size > 0:
            # Round up to lot size
            quantity = int(np.ceil(demand / lot_size)) * lot_size
        else:
            quantity = demand

        mps.append(quantity)

    return mps

Example

Example

demand = [100, 120, 90, 150, 110, 130, 95, 140] chase_mps = chase_strategy_mps(demand, lot_size=50) print(f"Chase Strategy MPS: {chase_mps}")
undefined
demand = [100, 120, 90, 150, 110, 130, 95, 140] chase_mps = chase_strategy_mps(demand, lot_size=50) print(f"Chase Strategy MPS: {chase_mps}")
undefined

Mixed Strategy (Hybrid)

混合策略(Hybrid)

Objective: Balance inventory costs and production variability
Approach:
  • Level production for base demand
  • Use overtime, subcontracting, or inventory for peaks
python
from pulp import *

def mixed_strategy_mps(demand, costs, capacity_normal, capacity_overtime):
    """
    Mixed strategy optimization

    Minimize total cost of production + inventory + backorders

    Parameters:
    - demand: list of demand by period
    - costs: dict {'regular': x, 'overtime': y, 'inventory': z, 'backorder': w}
    - capacity_normal: regular capacity per period
    - capacity_overtime: max overtime capacity per period
    """

    periods = len(demand)

    # Create problem
    prob = LpProblem("Mixed_Strategy_MPS", LpMinimize)

    # Variables
    P = LpVariable.dicts("Regular", range(periods), lowBound=0)
    O = LpVariable.dicts("Overtime", range(periods), lowBound=0)
    I = LpVariable.dicts("Inventory", range(periods), lowBound=0)
    B = LpVariable.dicts("Backorder", range(periods), lowBound=0)

    # Objective
    prob += lpSum([
        costs['regular'] * P[t] +
        costs['overtime'] * O[t] +
        costs['inventory'] * I[t] +
        costs['backorder'] * B[t]
        for t in range(periods)
    ])

    # Constraints
    for t in range(periods):
        # Capacity
        prob += P[t] <= capacity_normal
        prob += O[t] <= capacity_overtime

        # Inventory balance
        if t == 0:
            prob += I[t] == P[t] + O[t] - demand[t] + B[t]
        else:
            prob += I[t] == I[t-1] + P[t] + O[t] - demand[t] + B[t] - B[t-1]

    # Solve
    prob.solve(PULP_CBC_CMD(msg=0))

    # Extract solution
    solution = {
        'regular': [P[t].varValue for t in range(periods)],
        'overtime': [O[t].varValue for t in range(periods)],
        'inventory': [I[t].varValue for t in range(periods)],
        'backorder': [B[t].varValue for t in range(periods)],
        'total_cost': value(prob.objective)
    }

    return solution
**目标:**平衡库存成本与生产波动
方法:
  • 基础需求采用均衡生产
  • 峰值需求采用加班、外包或库存缓冲
python
from pulp import *

def mixed_strategy_mps(demand, costs, capacity_normal, capacity_overtime):
    """
    Mixed strategy optimization

    Minimize total cost of production + inventory + backorders

    Parameters:
    - demand: list of demand by period
    - costs: dict {'regular': x, 'overtime': y, 'inventory': z, 'backorder': w}
    - capacity_normal: regular capacity per period
    - capacity_overtime: max overtime capacity per period
    """

    periods = len(demand)

    # Create problem
    prob = LpProblem("Mixed_Strategy_MPS", LpMinimize)

    # Variables
    P = LpVariable.dicts("Regular", range(periods), lowBound=0)
    O = LpVariable.dicts("Overtime", range(periods), lowBound=0)
    I = LpVariable.dicts("Inventory", range(periods), lowBound=0)
    B = LpVariable.dicts("Backorder", range(periods), lowBound=0)

    # Objective
    prob += lpSum([
        costs['regular'] * P[t] +
        costs['overtime'] * O[t] +
        costs['inventory'] * I[t] +
        costs['backorder'] * B[t]
        for t in range(periods)
    ])

    # Constraints
    for t in range(periods):
        # Capacity
        prob += P[t] <= capacity_normal
        prob += O[t] <= capacity_overtime

        # Inventory balance
        if t == 0:
            prob += I[t] == P[t] + O[t] - demand[t] + B[t]
        else:
            prob += I[t] == I[t-1] + P[t] + O[t] - demand[t] + B[t] - B[t-1]

    # Solve
    prob.solve(PULP_CBC_CMD(msg=0))

    # Extract solution
    solution = {
        'regular': [P[t].varValue for t in range(periods)],
        'overtime': [O[t].varValue for t in range(periods)],
        'inventory': [I[t].varValue for t in range(periods)],
        'backorder': [B[t].varValue for t in range(periods)],
        'total_cost': value(prob.objective)
    }

    return solution

Example

Example

demand = [100, 120, 150, 180, 160, 140, 120, 110]
costs = { 'regular': 50, 'overtime': 75, 'inventory': 5, 'backorder': 100 }
mixed_mps = mixed_strategy_mps( demand=demand, costs=costs, capacity_normal=120, capacity_overtime=40 )
print("Mixed Strategy MPS:") print(f"Total Cost: ${mixed_mps['total_cost']:,.0f}") print(f"Regular Production: {[int(x) for x in mixed_mps['regular']]}") print(f"Overtime Production: {[int(x) for x in mixed_mps['overtime']]}") print(f"Ending Inventory: {[int(x) for x in mixed_mps['inventory']]}")

---
demand = [100, 120, 150, 180, 160, 140, 120, 110]
costs = { 'regular': 50, 'overtime': 75, 'inventory': 5, 'backorder': 100 }
mixed_mps = mixed_strategy_mps( demand=demand, costs=costs, capacity_normal=120, capacity_overtime=40 )
print("Mixed Strategy MPS:") print(f"Total Cost: ${mixed_mps['total_cost']:,.0f}") print(f"Regular Production: {[int(x) for x in mixed_mps['regular']]}") print(f"Overtime Production: {[int(x) for x in mixed_mps['overtime']]}") print(f"Ending Inventory: {[int(x) for x in mixed_mps['inventory']]}")

---

MPS Performance Metrics

MPS绩效指标

Schedule Adherence

排程遵守率

MPS Attainment:
Actual Production / Planned Production × 100%
Target: >95%
MPS达成率:
实际产量 / 计划产量 × 100%
目标:>95%

Inventory Metrics

库存指标

Inventory Turnover:
COGS / Average Inventory
Days of Inventory:
(Average Inventory / Daily Demand) × Days
库存周转率:
销货成本 / 平均库存
库存天数:
(平均库存 / 日均需求) × 天数

Customer Service

客户服务

On-Time Delivery:
Orders Delivered On-Time / Total Orders × 100%
Target: >95%
Order Fill Rate:
Orders Filled Complete / Total Orders × 100%
准时交付率:
准时交付订单数 / 总订单数 × 100%
目标:>95%
订单满足率:
完全满足订单数 / 总订单数 × 100%

Planning Metrics

规划指标

Planning Nervousness:
  • Measures schedule instability
  • Count of changes to frozen MPS
ATP Accuracy:
  • Promised vs. actual delivery performance
python
class MPSMetrics:
    """Track MPS performance metrics"""

    def __init__(self):
        self.periods = []

    def add_period(self, planned, actual, inventory, orders_on_time, total_orders):
        """Record period performance"""

        self.periods.append({
            'planned_production': planned,
            'actual_production': actual,
            'ending_inventory': inventory,
            'orders_on_time': orders_on_time,
            'total_orders': total_orders
        })

    def calculate_metrics(self):
        """Calculate summary metrics"""

        df = pd.DataFrame(self.periods)

        metrics = {
            'mps_attainment': (df['actual_production'].sum() /
                              df['planned_production'].sum() * 100),
            'avg_inventory': df['ending_inventory'].mean(),
            'on_time_delivery': (df['orders_on_time'].sum() /
                                df['total_orders'].sum() * 100)
        }

        return metrics
规划波动性:
  • 衡量排程稳定性
  • 冻结区MPS的变更次数
ATP准确率:
  • 承诺交付与实际交付的匹配度
python
class MPSMetrics:
    """Track MPS performance metrics"""

    def __init__(self):
        self.periods = []

    def add_period(self, planned, actual, inventory, orders_on_time, total_orders):
        """Record period performance"""

        self.periods.append({
            'planned_production': planned,
            'actual_production': actual,
            'ending_inventory': inventory,
            'orders_on_time': orders_on_time,
            'total_orders': total_orders
        })

    def calculate_metrics(self):
        """Calculate summary metrics"""

        df = pd.DataFrame(self.periods)

        metrics = {
            'mps_attainment': (df['actual_production'].sum() /
                              df['planned_production'].sum() * 100),
            'avg_inventory': df['ending_inventory'].mean(),
            'on_time_delivery': (df['orders_on_time'].sum() /
                                df['total_orders'].sum() * 100)
        }

        return metrics

Example

Example

metrics = MPSMetrics()
metrics = MPSMetrics()

Add several periods

Add several periods

for i in range(6): metrics.add_period( planned=200, actual=np.random.randint(185, 205), inventory=np.random.randint(80, 120), orders_on_time=np.random.randint(18, 20), total_orders=20 )
summary = metrics.calculate_metrics() print("MPS Performance Metrics:") for metric, value in summary.items(): print(f" {metric}: {value:.1f}")

---
for i in range(6): metrics.add_period( planned=200, actual=np.random.randint(185, 205), inventory=np.random.randint(80, 120), orders_on_time=np.random.randint(18, 20), total_orders=20 )
summary = metrics.calculate_metrics() print("MPS Performance Metrics:") for metric, value in summary.items(): print(f" {metric}: {value:.1f}")

---

Tools & Libraries

工具与库

Python Libraries

Python库

Optimization:
  • pulp
    : Linear programming for MPS optimization
  • pyomo
    : Advanced optimization
  • scipy.optimize
    : Optimization algorithms
Data Management:
  • pandas
    : Time series and data manipulation
  • numpy
    : Numerical computations
Visualization:
  • matplotlib
    ,
    seaborn
    : Gantt charts, capacity profiles
  • plotly
    : Interactive schedules
优化:
  • pulp
    : 用于MPS优化的线性规划库
  • pyomo
    : 高级优化库
  • scipy.optimize
    : 优化算法
数据管理:
  • pandas
    : 时间序列与数据处理
  • numpy
    : 数值计算
可视化:
  • matplotlib
    ,
    seaborn
    : 甘特图、产能分布图
  • plotly
    : 交互式排程图

Commercial MPS Software

商用MPS软件

ERP Systems with MPS:
  • SAP: MRP/MPS modules
  • Oracle: Advanced Supply Chain Planning
  • Microsoft Dynamics: Master Planning
  • Infor: Production Planning
Specialized Planning:
  • Kinaxis RapidResponse: Real-time planning
  • Blue Yonder: Demand-driven MPS
  • o9 Solutions: AI-powered planning
  • Anaplan: Cloud-based planning
Shop Floor Integration:
  • Plex: Manufacturing MPS
  • IQMS: MRP/MPS for manufacturing
  • Epicor: Production management

含MPS模块的ERP系统:
  • SAP: MRP/MPS模块
  • Oracle: 高级供应链规划
  • Microsoft Dynamics: 主规划
  • Infor: 生产规划
专业规划软件:
  • Kinaxis RapidResponse: 实时规划
  • Blue Yonder: 需求驱动型MPS
  • o9 Solutions: AI驱动的规划
  • Anaplan: 云规划
车间集成软件:
  • Plex: 制造业MPS
  • IQMS: 制造业MRP/MPS
  • Epicor: 生产管理

Common Challenges & Solutions

常见挑战与解决方案

Challenge: Frozen Zone Too Short

挑战:冻结区过短

Problem:
  • Constant schedule changes
  • Production disruption
  • Low attainment
Solutions:
  • Extend frozen zone (4-6 weeks typical)
  • Reduce lead times to shorten freeze needed
  • Improve forecast accuracy
  • Enforce change control policies
  • Buffer with safety stock
问题:
  • 排程频繁变更
  • 生产中断
  • 达成率低
解决方案:
  • 延长冻结区(通常4-6周)
  • 缩短生产周期以减少所需冻结时间
  • 提高预测准确率
  • 执行变更控制政策
  • 用安全库存缓冲

Challenge: Overloaded Capacity

挑战:产能过载

Problem:
  • RCCP shows capacity shortfall
  • Cannot meet MPS
Solutions:
  • Add shifts or overtime
  • Outsource/subcontract
  • Reduce demand (allocation)
  • Increase lead times
  • Capital investment (long-term)
问题:
  • RCCP显示产能不足
  • 无法完成MPS
解决方案:
  • 增加班次或加班
  • 外包/分包
  • 需求分配(减少部分需求)
  • 延长交付周期
  • 长期资本投入

Challenge: Poor Schedule Adherence

挑战:排程遵守率低

Problem:
  • Actual production doesn't match MPS
  • Firefighting and expediting
Solutions:
  • Improve shop floor execution
  • Better material availability (MRP)
  • Reduce variability (quality, uptime)
  • Realistic MPS (don't overplan)
  • Root cause analysis on misses
问题:
  • 实际产量与MPS不符
  • 频繁救火与加急生产
解决方案:
  • 提升车间执行能力
  • 改善物料可用性(MRP)
  • 减少波动(质量、设备uptime)
  • 制定更现实的MPS(避免过度规划)
  • 对未达成情况进行根因分析

Challenge: Lumpy MPS (Lot Sizing)

挑战:MPS批量波动(批量规划)

Problem:
  • Large lot sizes create peaks/valleys
  • Capacity swings
  • Inventory buildup
Solutions:
  • Reduce setup times (SMED)
  • Smaller lot sizes
  • Level loading approach
  • Mixed-model scheduling
  • Lean manufacturing principles
问题:
  • 大批量导致产能高峰/低谷
  • 产能波动
  • 库存积压
解决方案:
  • 减少换产时间(SMED)
  • 采用更小批量
  • 均衡生产方法
  • 混流生产
  • 精益制造原则

Challenge: Nervousness (Too Many Changes)

挑战:规划波动性(变更过多)

Problem:
  • MPS changes frequently
  • Planner credibility issues
  • Shop floor confusion
Solutions:
  • Stricter time fences
  • Improve forecast accuracy
  • Demand filtering/dampening
  • Manage customer expectations
  • Better S&OP process

问题:
  • MPS频繁变更
  • 计划员可信度下降
  • 车间混乱
解决方案:
  • 更严格的时间fence
  • 提高预测准确率
  • 需求过滤/平滑
  • 管理客户期望
  • 优化S&OP流程

Output Format

输出格式

MPS Report Structure

MPS报告结构

MPS Planning Table:
WeekForecastCustomer OrdersTotal DemandProj. AvailableMPSATPZone
11001501505020050Frozen
211013013012000Frozen
3105801051510020Frozen
412060120-5200140Slushy
511501158000Slushy
61250125-45200200Liquid
Capacity Load Summary:
Work CenterWeek 1Week 2Week 3Week 4AvailableStatus
Assembly145120130160160OK
Testing958590110120OK
Packaging7060658080OK
MPS Changes from Last Week:
ItemWeekOld QuantityNew QuantityChangeZoneReason
Product_A4150200+50SlushyNew order
Product_B61000-100LiquidForecast reduced
ATP Summary:
ItemWeek 1Week 2Week 3Week 4Total ATP
Product_A50020140210
Product_B3000100130
Exception Reports:
  • Overdue Orders: List of orders past due date
  • Capacity Overloads: Resources exceeding 100%
  • Material Shortages: Components not available
  • Schedule Changes: Changes in frozen zone

MPS规划表:
预测客户订单总需求预计可用库存MPSATP区域
11001501505020050冻结区
211013013012000冻结区
3105801051510020冻结区
412060120-5200140半冻结区
511501158000半冻结区
61250125-45200200灵活区
产能负载汇总:
工作中心第1周第2周第3周第4周可用产能状态
装配145120130160160正常
测试958590110120正常
包装7060658080正常
MPS较上周变更:
产品原数量新数量变更区域原因
Product_A4150200+50半冻结区新订单
Product_B61000-100灵活区预测下调
ATP汇总:
产品第1周第2周第3周第4周总ATP
Product_A50020140210
Product_B3000100130
异常报告:
  • 逾期订单:超过交付日期的订单列表
  • 产能过载:负载超过100%的资源
  • 物料短缺:无法获取的组件
  • 排程变更:冻结区的排程变更

Questions to Ask

需询问的问题

If you need more context:
  1. What manufacturing environment? (MTS, MTO, ATO, ETO)
  2. What's the production process? (discrete, process, batch)
  3. Planning horizon and bucket size? (weeks, months)
  4. Number of end items to schedule?
  5. Current MPS process and tools?
  6. Critical capacity constraints?
  7. Time fence policies?
  8. Schedule adherence rates?

如需更多上下文,请询问:
  1. 生产环境类型?(MTS、MTO、ATO、ETO)
  2. 生产流程?(离散、流程、批量)
  3. 规划周期与时间粒度?(周、月)
  4. 需排程的最终产品数量?
  5. 当前MPS流程与工具?
  6. 关键产能约束?
  7. 时间fence政策?
  8. 排程遵守率?

Related Skills

相关技能

  • sales-operations-planning: For aggregate production plan input
  • capacity-planning: For capacity analysis and RCCP
  • demand-forecasting: For demand input to MPS
  • production-scheduling: For detailed shop floor scheduling
  • inventory-optimization: For safety stock and lot sizing
  • lot-sizing-problems: For MPS quantity decisions
  • supply-chain-analytics: For MPS performance tracking
  • lean-manufacturing: For setup reduction and level loading
  • sales-operations-planning: 提供综合生产计划输入
  • capacity-planning: 用于产能分析与RCCP
  • demand-forecasting: 提供MPS的需求输入
  • production-scheduling: 用于详细车间排程
  • inventory-optimization: 用于安全库存与批量规划
  • lot-sizing-problems: 用于MPS数量决策
  • supply-chain-analytics: 用于MPS绩效跟踪
  • lean-manufacturing: 用于换产时间减少与均衡生产