Manufacturing Example

A semiconductor fabrication simulation with multi-step routing and quality constraints.

Overview

This example models a semiconductor fab inspired by the Winter Simulation Conference (WSC) 2023 challenge. It demonstrates:

  • Multi-step production routes

  • Quality Time (QT) constraints between steps

  • Lot-based batch processing

  • Multiple workstations with tool capacity

  • Work-in-progress (WIP) monitoring

  • Constraint breach detection

The Manufacturing Process

Lot Release → Step 1 → Step 2 → ... → Step N → Completion
                 │         │
                 └── QT ───┘  (Quality Time constraint)

Key concepts:

  • Lot: A batch of wafers moving through the fab

  • Step: A processing operation at a workstation

  • Workstation: A group of parallel tools performing the same operation

  • Quality Time (QT): Maximum allowed time between certain steps

Code Walkthrough

Domain Entities

@dataclass
class ProductType:
    """Definition of a product's manufacturing route."""
    name: str
    steps: List[Step]
    qt_constraints: List[QTConstraint]

@dataclass
class Lot(TimedEntity):
    """A lot (batch) moving through the fab."""
    lot_id: str
    product_type: ProductType
    quantity: int = 25  # wafers per lot
    current_step: int = 0
    qt_breach: bool = False

Workstation with Tools

class Workstation:
    """A workstation with multiple parallel tools."""

    def __init__(self, sim, name: str, num_tools: int, process_time: float):
        self.server = Server(
            sim=sim,
            capacity=num_tools,
            service_time=lambda: sim.rng.exponential(process_time),
            name=name,
        )
        self.wip = TimeSeries(sim, name=f"{name}_WIP")

Quality Time Constraints

@dataclass
class QTConstraint:
    """Quality time constraint between steps."""
    from_step: int
    to_step: int
    max_time: float

def check_qt_constraint(self, lot: Lot) -> bool:
    """Check if lot has violated any QT constraints."""
    for qt in lot.product_type.qt_constraints:
        if lot.current_step == qt.to_step:
            elapsed = self.now - lot.step_times[qt.from_step]
            if elapsed > qt.max_time:
                return True  # Breach!
    return False

Lot Flow

def on_init(self):
    """Start lot releases."""
    self.schedule(self.release_lot, delay=0)

def release_lot(self):
    """Release a new lot into the fab."""
    lot = Lot(
        lot_id=f"LOT-{self.lots_released}",
        product_type=self.product_types[0],
    )
    lot.record_entry(self.now)
    self.lots_released += 1

    # Start processing at first workstation
    self.process_step(lot)

    # Schedule next release
    self.schedule(self.release_lot, delay=self.release_interval)

def process_step(self, lot: Lot):
    """Process lot at current step."""
    step = lot.product_type.steps[lot.current_step]
    workstation = self.workstations[step.workstation]

    # Track WIP
    workstation.wip.observe_change(1)

    # Enter workstation queue
    workstation.server.enqueue(lot)

def on_step_complete(self, lot: Lot):
    """Handle step completion."""
    # Check QT constraint
    if self.check_qt_constraint(lot):
        lot.qt_breach = True
        self.breaches += 1

    # Update WIP
    workstation.wip.observe_change(-1)

    # Move to next step or complete
    lot.current_step += 1
    if lot.current_step < len(lot.product_type.steps):
        self.process_step(lot)
    else:
        self.complete_lot(lot)

Running the Example

from simcraft.examples.manufacturing import ManufacturingSimulation

# Create simulation
sim = ManufacturingSimulation(
    num_workstations=5,
    tools_per_workstation=3,
    release_interval=10.0,
)

# Run for one week (168 hours)
sim.run(until=168 * 60)  # in minutes

# Print results
sim.report()

Sample Output

Manufacturing Simulation Results
============================================================

Production Metrics:
  Lots released: 1008
  Lots completed: 987
  QT breaches: 23 (2.3%)

Cycle Time:
  Average: 142.3 minutes
  Min: 98.7 minutes
  Max: 312.5 minutes

Workstation Utilization:
  WS-1: 78.2%
  WS-2: 82.5%
  WS-3: 71.3%
  WS-4: 85.1%
  WS-5: 69.8%

Bottleneck: WS-4 (highest utilization)

Key Patterns Demonstrated

Multi-Step Routing

Lots follow a predefined sequence of steps, each at a specific workstation.

Time Constraints

Quality Time (QT) constraints enforce maximum elapsed time between steps. Violations are tracked as breaches.

Parallel Resources

Each workstation has multiple tools (parallel servers) that can process lots simultaneously.

WIP Tracking

Work-in-progress is tracked at each workstation using TimeSeries statistics.

Extensions

Priority Dispatching

# Prioritize lots close to QT breach
def get_priority(lot: Lot) -> float:
    remaining_qt = self.get_remaining_qt(lot)
    return -remaining_qt  # Lower remaining time = higher priority

Machine Breakdowns

def schedule_breakdown(self, workstation):
    """Schedule random machine breakdown."""
    mtbf = self.rng.exponential(1000)  # Mean time between failures
    self.schedule(
        self.machine_failure,
        delay=mtbf,
        args=(workstation,)
    )

def machine_failure(self, workstation):
    """Handle machine failure."""
    workstation.server.set_down()
    repair_time = self.rng.exponential(30)
    self.schedule(
        self.machine_repair,
        delay=repair_time,
        args=(workstation,)
    )

Multiple Product Types

product_a = ProductType(
    name="ProductA",
    steps=[Step("WS-1"), Step("WS-2"), Step("WS-3")],
    qt_constraints=[QTConstraint(0, 2, max_time=60)]
)

product_b = ProductType(
    name="ProductB",
    steps=[Step("WS-1"), Step("WS-3"), Step("WS-4"), Step("WS-5")],
    qt_constraints=[QTConstraint(1, 3, max_time=90)]
)

Source Code

The complete source code is in simcraft/examples/manufacturing.py.