master-production-scheduling
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseMaster 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:
-
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)
-
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?
-
Capacity & Constraints
- Critical resources and bottlenecks?
- Changeover/setup times significant?
- Workforce availability and skills?
- Material constraints?
-
Current State
- Existing MPS process?
- ERP/MRP system in place?
- Schedule adherence rates?
- Inventory levels (raw materials, WIP, finished goods)?
在制定MPS之前,需了解以下信息:
-
规划环境
- 生产类型?(备货型生产、订货型生产、按订单装配、按订单设计)
- 生产流程?(离散制造、流程制造、重复生产、批量生产)
- 规划周期?(周、月)
- 规划时间粒度?(日、周)
-
产品与需求
- 最终产品/SKU数量?
- 产品结构复杂度?(单层级、多层级BOM)
- 需求模式?(稳定、季节性、波动型)
- 客户订单交付周期 vs 生产周期?
-
产能与约束
- 关键资源与瓶颈?
- 换产/准备时间是否显著?
- 劳动力可用性与技能水平?
- 物料约束?
-
当前状态
- 现有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 Type | Horizon | Level | Frequency | Purpose |
|---|---|---|---|---|
| S&OP | 12-18 mo | Product family | Monthly | Align demand/supply |
| MPS | 3-6 mo | End item | Weekly | Detailed production plan |
| MRP | 3-6 mo | Component | Daily/Weekly | Material requirements |
| Shop Floor Schedule | Days-weeks | Operation | Daily | Detailed sequencing |
| 计划类型 | 周期 | 层级 | 频率 | 目的 |
|---|---|---|---|---|
| S&OP | 12-18个月 | 产品族 | 月度 | 平衡供需 |
| MPS | 3-6个月 | 最终产品 | 周度 | 详细生产计划 |
| MRP | 3-6个月 | 组件 | 日/周度 | 物料需求计算 |
| 车间排程 | 天-周 | 工序 | 日度 | 详细工序排序 |
MPS Planning Process
MPS规划流程
Step 1: Review Demand Requirements
步骤1:评估需求
Sources of Demand:
-
Forecast Demand
- Statistical forecast from demand planning
- Marketing/sales input
- Seasonality and trends
-
Customer Orders
- Booked orders
- Firm commitments
- Contracts
-
Safety Stock Requirements
- Buffer inventory targets
- Service level policies
-
Interplant Transfers
- Distribution requirements
- Network demand
-
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需求来源:
-
预测需求
- 需求规划的统计预测
- 营销/销售输入
- 季节性与趋势
-
客户订单
- 已确认订单
- 正式承诺
- 合同订单
-
安全库存要求
- 缓冲库存目标
- 服务水平政策
-
厂间调拨
- 分销需求
- 网络需求
-
售后备件
- 售后市场需求
- 保修要求
需求聚合:
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 quantitiesExample 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']])
undefinedtotal_demand = mps_demand.calculate_total_demand()
print("MPS Demand by Week:")
print(total_demand[['period', 'Product_A_forecast', 'Product_A_orders', 'Product_A_total']])
undefinedStep 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 receiptMPS Planning Table:
| Period | Forecast | Orders | Total | Proj. Avail | MPS | ATP |
|---|---|---|---|---|---|---|
| 1 | 100 | 150 | 150 | 50 | 200 | 50 |
| 2 | 110 | 130 | 130 | 120 | 0 | 0 |
| 3 | 105 | 0 | 105 | 15 | 100 | 100 |
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规划表:
| 周期 | 预测 | 订单 | 总需求 | 预计可用库存 | MPS | ATP |
|---|---|---|---|---|---|---|
| 1 | 100 | 150 | 150 | 50 | 200 | 50 |
| 2 | 110 | 130 | 130 | 120 | 0 | 0 |
| 3 | 105 | 0 | 105 | 15 | 100 | 100 |
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)
undefinedrccp = mps.calculate_rough_cut_capacity(mps_plan, routing_time_per_unit=2.5)
print("\nRough-Cut Capacity Requirements:")
print(rccp)
undefinedStep 3: Rough-Cut Capacity Planning (RCCP)
步骤3:粗能力计划(RCCP)
Purpose: Validate MPS is feasible given capacity constraints
Process:
- Calculate resource requirements from MPS
- Compare to available capacity
- Identify overloads
- 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在产能约束下的可行性
流程:
- 根据MPS计算资源需求
- 与可用产能对比
- 识别过载情况
- 调整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 figExample
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)
undefinedrccp.plot_capacity_profile(capacity_check)
undefinedStep 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, NoneExample
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")
undefinedcan_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")
undefinedStep 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 onlyFreeze 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_releasedExample
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)
undefinedforecast = [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)
undefinedMake-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)
undefinedorders = [
{'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)
undefinedAssemble-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:
- Component/module level (forecast-driven)
- 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:
- 组件/模块层级(预测驱动)
- 最终装配(订单驱动)
- 规划物料清单
- 最终装配计划(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 mpsExample
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}")
undefinedtotal_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}")
undefinedChase 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 mpsExample
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}")
undefineddemand = [100, 120, 90, 150, 110, 130, 95, 140]
chase_mps = chase_strategy_mps(demand, lot_size=50)
print(f"Chase Strategy MPS: {chase_mps}")
undefinedMixed 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 solutionExample
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 InventoryDays 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 metricsExample
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:
- : Linear programming for MPS optimization
pulp - : Advanced optimization
pyomo - : Optimization algorithms
scipy.optimize
Data Management:
- : Time series and data manipulation
pandas - : Numerical computations
numpy
Visualization:
- ,
matplotlib: Gantt charts, capacity profilesseaborn - : Interactive schedules
plotly
优化:
- : 用于MPS优化的线性规划库
pulp - : 高级优化库
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:
| Week | Forecast | Customer Orders | Total Demand | Proj. Available | MPS | ATP | Zone |
|---|---|---|---|---|---|---|---|
| 1 | 100 | 150 | 150 | 50 | 200 | 50 | Frozen |
| 2 | 110 | 130 | 130 | 120 | 0 | 0 | Frozen |
| 3 | 105 | 80 | 105 | 15 | 100 | 20 | Frozen |
| 4 | 120 | 60 | 120 | -5 | 200 | 140 | Slushy |
| 5 | 115 | 0 | 115 | 80 | 0 | 0 | Slushy |
| 6 | 125 | 0 | 125 | -45 | 200 | 200 | Liquid |
Capacity Load Summary:
| Work Center | Week 1 | Week 2 | Week 3 | Week 4 | Available | Status |
|---|---|---|---|---|---|---|
| Assembly | 145 | 120 | 130 | 160 | 160 | OK |
| Testing | 95 | 85 | 90 | 110 | 120 | OK |
| Packaging | 70 | 60 | 65 | 80 | 80 | OK |
MPS Changes from Last Week:
| Item | Week | Old Quantity | New Quantity | Change | Zone | Reason |
|---|---|---|---|---|---|---|
| Product_A | 4 | 150 | 200 | +50 | Slushy | New order |
| Product_B | 6 | 100 | 0 | -100 | Liquid | Forecast reduced |
ATP Summary:
| Item | Week 1 | Week 2 | Week 3 | Week 4 | Total ATP |
|---|---|---|---|---|---|
| Product_A | 50 | 0 | 20 | 140 | 210 |
| Product_B | 30 | 0 | 0 | 100 | 130 |
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规划表:
| 周 | 预测 | 客户订单 | 总需求 | 预计可用库存 | MPS | ATP | 区域 |
|---|---|---|---|---|---|---|---|
| 1 | 100 | 150 | 150 | 50 | 200 | 50 | 冻结区 |
| 2 | 110 | 130 | 130 | 120 | 0 | 0 | 冻结区 |
| 3 | 105 | 80 | 105 | 15 | 100 | 20 | 冻结区 |
| 4 | 120 | 60 | 120 | -5 | 200 | 140 | 半冻结区 |
| 5 | 115 | 0 | 115 | 80 | 0 | 0 | 半冻结区 |
| 6 | 125 | 0 | 125 | -45 | 200 | 200 | 灵活区 |
产能负载汇总:
| 工作中心 | 第1周 | 第2周 | 第3周 | 第4周 | 可用产能 | 状态 |
|---|---|---|---|---|---|---|
| 装配 | 145 | 120 | 130 | 160 | 160 | 正常 |
| 测试 | 95 | 85 | 90 | 110 | 120 | 正常 |
| 包装 | 70 | 60 | 65 | 80 | 80 | 正常 |
MPS较上周变更:
| 产品 | 周 | 原数量 | 新数量 | 变更 | 区域 | 原因 |
|---|---|---|---|---|---|---|
| Product_A | 4 | 150 | 200 | +50 | 半冻结区 | 新订单 |
| Product_B | 6 | 100 | 0 | -100 | 灵活区 | 预测下调 |
ATP汇总:
| 产品 | 第1周 | 第2周 | 第3周 | 第4周 | 总ATP |
|---|---|---|---|---|---|
| Product_A | 50 | 0 | 20 | 140 | 210 |
| Product_B | 30 | 0 | 0 | 100 | 130 |
异常报告:
- 逾期订单:超过交付日期的订单列表
- 产能过载:负载超过100%的资源
- 物料短缺:无法获取的组件
- 排程变更:冻结区的排程变更
Questions to Ask
需询问的问题
If you need more context:
- What manufacturing environment? (MTS, MTO, ATO, ETO)
- What's the production process? (discrete, process, batch)
- Planning horizon and bucket size? (weeks, months)
- Number of end items to schedule?
- Current MPS process and tools?
- Critical capacity constraints?
- Time fence policies?
- Schedule adherence rates?
如需更多上下文,请询问:
- 生产环境类型?(MTS、MTO、ATO、ETO)
- 生产流程?(离散、流程、批量)
- 规划周期与时间粒度?(周、月)
- 需排程的最终产品数量?
- 当前MPS流程与工具?
- 关键产能约束?
- 时间fence政策?
- 排程遵守率?
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: 用于换产时间减少与均衡生产