DDMRP Dynamic Buffer Management

Electronics Division — As of 2026-02-21 08:00 UTC
Buffer Zone Overview — Net Flow Position by SKU
R Code
# DDMRP Buffer Zone Visualization in R (ggplot2) library(ggplot2) library(dplyr) library(tidyr) # Define SKU data skus <- data.frame( sku = c("PCB-100","DSP-200","BAT-300","HSG-400","CAM-500","CHG-600","SPK-700","ANT-800"), adu = c(45, 40, 60, 50, 35, 55, 42, 38), dlt = c(12, 18, 8, 10, 20, 6, 9, 15), ltf = c(0.35, 0.40, 0.30, 0.30, 0.45, 0.25, 0.30, 0.40), vf = c(0.50, 0.60, 0.40, 0.35, 0.55, 0.30, 0.40, 0.55), moq = c(200, 150, 500, 300, 100, 1000, 200, 100), oh = c(380, 120, 850, 520, 95, 1200, 280, 60), oo = c(500, 400, 0, 300, 350, 0, 200, 0), qd = c(210, 195, 180, 240, 175, 165, 130, 190) ) # Calculate buffer zones skus <- skus %>% mutate( red_base = adu * dlt * ltf * vf, red_safety = red_base * ltf, total_red = red_base + red_safety, yellow = adu * dlt, green = pmax(adu * dlt * ltf, moq), tog = total_red + yellow + green, toy = total_red + yellow, tor = total_red, nfp = oh + oo - qd, status = case_when(nfp <= tor ~ "Red", nfp <= toy ~ "Yellow", TRUE ~ "Green") ) # Reshape for stacked bar chart zones <- skus %>% select(sku, total_red, yellow, green, nfp, oh) %>% pivot_longer(cols = c(total_red, yellow, green), names_to = "zone", values_to = "value") %>% mutate(zone = factor(zone, levels = c("green","yellow","total_red"))) ggplot(zones, aes(x = sku, y = value, fill = zone)) + geom_col(position = "stack", width = 0.6) + geom_point(data = skus, aes(x = sku, y = nfp, fill = NULL), shape = 23, size = 3, fill = "black") + scale_fill_manual(values = c( total_red = "#E74C3C", yellow = "#F0AD4E", green = "#2ECC71" )) + labs(title = "Buffer Zone Overview", y = "Units") + theme_minimal() + theme(panel.grid.major.x = element_blank(), legend.position = "top")
Buffer Detail — Zone Calculations & Replenishment Signals
R Code
# Buffer detail table with kableExtra library(kableExtra) skus %>% mutate( order_qty = ifelse(nfp < toy, ceiling(tog - nfp), 0), pct_of_tog = round(nfp / tog * 100, 1) ) %>% select(sku, adu, dlt, red_base, red_safety, total_red, yellow, green, tog, toy, tor, oh, oo, qd, nfp, status, order_qty) %>% kbl(col.names = c("SKU","ADU","DLT","Red Base","Red Safety", "Total Red","Yellow","Green","TOG","TOY", "TOR","On-Hand","On-Order","Qual.Dem", "NFP","Status","Order Qty"), digits = 0) %>% kable_styling(bootstrap_options = c("striped","condensed")) %>% row_spec(which(skus$status == "Red"), background = "#FDF2F0") %>% row_spec(which(skus$status == "Yellow"), background = "#FFF9F0")
Dynamic Buffer Adjustment — Normal vs. Seasonal Peak (DAF = 1.4)
R Code
# Dynamic Adjustment Factor comparison library(ggplot2) library(patchwork) daf <- 1.4 selected <- skus %>% filter(sku %in% c("PCB-100","DSP-200","CAM-500")) # Calculate peak zones with DAF applied to ADU selected_peak <- selected %>% mutate( adu_peak = adu * daf, red_base_p = adu_peak * dlt * ltf * vf, red_safety_p = red_base_p * ltf, total_red_p = red_base_p + red_safety_p, yellow_p = adu_peak * dlt, green_p = pmax(adu_peak * dlt * ltf, moq), tog_p = total_red_p + yellow_p + green_p ) # Side-by-side stacked bars (Normal vs Peak) # Use facet_wrap with scenario column bind_normal <- selected %>% select(sku, total_red, yellow, green) %>% mutate(scenario = "Normal") bind_peak <- selected_peak %>% transmute(sku, total_red = total_red_p, yellow = yellow_p, green = green_p, scenario = "Peak (DAF=1.4)") combined <- bind_rows(bind_normal, bind_peak) ggplot(combined %>% pivot_longer(total_red:green), aes(x = scenario, y = value, fill = name)) + geom_col(width = 0.5) + facet_wrap(~sku, scales = "free_y") + scale_fill_manual(values = c( total_red="#E74C3C", yellow="#F0AD4E", green="#2ECC71")) + theme_minimal()
Net Flow Position Timeline — PCB-100 Main Circuit Board (30 Days)
R Code
# NFP Timeline for PCB-100 over 30 days library(ggplot2) # Simulate daily NFP with consumption and order arrivals set.seed(42) days <- 1:30 nfp_series <- numeric(30) oh <- 670 # starting NFP for PCB-100 tor <- 127.6; toy <- 667.6; tog <- 867.6 orders_placed <- c() orders_arrive <- c() for (d in days) { consumption <- rpois(1, 45) # ~ADU arrival <- ifelse(d %in% orders_arrive, tog - oh, 0) # simplified oh <- oh - consumption + arrival if (oh < toy) { orders_placed <- c(orders_placed, d) orders_arrive <- c(orders_arrive, d + 12) } nfp_series[d] <- oh } df <- data.frame(day = days, nfp = nfp_series) ggplot(df, aes(x = day, y = nfp)) + annotate("rect", xmin=0, xmax=31, ymin=0, ymax=tor, fill="#E74C3C", alpha=0.2) + annotate("rect", xmin=0, xmax=31, ymin=tor, ymax=toy, fill="#F0AD4E", alpha=0.2) + annotate("rect", xmin=0, xmax=31, ymin=toy, ymax=tog, fill="#2ECC71", alpha=0.15) + geom_line(color = "#2C3E50", size = 0.8) + geom_hline(yintercept = c(tor, toy, tog), linetype = "dashed", color = "#666") + geom_point(data = df[df$day %in% orders_placed,], shape = 24, size = 3, fill = "#C0392B") + theme_minimal() + labs(title = "PCB-100 NFP Over 30 Days", x = "Day", y = "Net Flow Position")
Buffer Penetration Heat Map — Last 4 Weeks
R Code
# Buffer Penetration Heatmap library(ggplot2) library(reshape2) # penetration = (TOG - NFP) / TOG * 100 # 0% = full buffer, 100% = empty buffer pen_data <- data.frame( sku = rep(skus$sku, each = 4), week = rep(paste("Wk", 1:4), 8), penetration = c( 18, 22, 20, 23, # PCB-100 55, 62, 68, 74, # DSP-200 28, 30, 35, 36, # BAT-300 25, 28, 30, 33, # HSG-400 60, 65, 72, 79, # CAM-500 15, 18, 20, 24, # CHG-600 38, 42, 44, 45, # SPK-700 72, 78, 88, 113 # ANT-800 (past 100% = negative NFP) ) ) pen_data$zone <- cut(pen_data$penetration, breaks = c(-Inf, 33, 66, Inf), labels = c("Green", "Yellow", "Red")) ggplot(pen_data, aes(x = week, y = sku, fill = zone)) + geom_tile(color = "white", size = 1) + geom_text(aes(label = paste0(penetration, "%")), size = 3.5) + scale_fill_manual(values = c( Green="#D5EDDB", Yellow="#FFF0CC", Red="#F8D7DA")) + theme_minimal() + labs(title = "Buffer Penetration Heatmap")
Replenishment Signal Board — Prioritized Actions
R Code
# Replenishment signal board signals <- skus %>% mutate( order_qty = ifelse(nfp < toy, ceiling(tog - nfp), 0), priority = case_when( nfp <= tor ~ "CRITICAL", nfp <= toy ~ "WARNING", TRUE ~ "OK" ), delivery_date = Sys.Date() + dlt ) %>% filter(order_qty > 0) %>% arrange(desc(priority == "CRITICAL"), order_qty) # Display as formatted table or card layout signals %>% select(sku, priority, order_qty, nfp, tog, delivery_date) %>% kbl() %>% kable_styling()
How DDMRP Buffers Work
Red Base
ADU x DLT x Lead Time Factor x Variability Factor
Red Safety
Red Base x Lead Time Factor
Total Red Zone (TOR)
Red Base + Red Safety
Yellow Zone
ADU x DLT
Green Zone
MAX(ADU x DLT x LT Factor, MOQ, Imposed Order Cycle)
Top of Green (TOG) = Order-Up-To Level
Red + Yellow + Green
Net Flow Position (NFP)
On-Hand + On-Order - Qualified Demand
Replenishment Order
When NFP < TOY, order (TOG - NFP) units

Demand Driven MRP decouples the supply chain by placing strategic inventory buffers at key positions. Unlike traditional MRP which pushes forecasted demand through every level of the BOM, DDMRP uses actual demand signals to drive replenishment.

The buffer is divided into three zones. The Green Zone controls order frequency -- a wider green zone means less frequent but larger orders. The Yellow Zone represents the normal demand coverage during lead time. The Red Zone is safety stock that absorbs variability in both demand and supply.

When the Net Flow Position (on-hand + on-order - qualified demand) drops below the Top of Yellow, a replenishment order is generated to bring the NFP back to the Top of Green. This creates a natural pull-based replenishment system that responds to actual consumption.

R Code
# Annotated DDMRP buffer diagram library(ggplot2) # Single buffer diagram with annotations zones <- data.frame( zone = factor(c("Red Base","Red Safety","Yellow","Green"), levels = c("Red Base","Red Safety","Yellow","Green")), value = c(95, 33, 540, 200) # PCB-100 example ) ggplot(zones, aes(x = "Buffer", y = value, fill = zone)) + geom_col(width = 0.4) + scale_fill_manual(values = c( "Red Base" = "#C0392B", "Red Safety" = "#E74C3C", "Yellow" = "#F0AD4E", "Green" = "#2ECC71" )) + geom_hline(yintercept = cumsum(zones$value)[c(2,3,4)], linetype = "dashed") + annotate("text", x = 1.4, y = cumsum(zones$value)[c(2,3,4)], label = c("TOR","TOY (Order Point)","TOG (Order-Up-To)"), hjust = 0, size = 3.5) + coord_flip() + theme_minimal() + labs(title = "DDMRP Buffer Structure", y = "Units")