mcpField
D
digital-twin-modeling

digital-twin-modeling

github.com/kishorkukrejaデータ・データベース8457 回インストール3 週間前に更新
リポジトリ

digital-twin-modeling について

サプライチェーン、倉庫、生産システムのデジタルツインをモデル化するスキルです。対象範囲やデータ、シミュレーション方式を検討し、離散イベント、エージェントベース、ネットワーク、リアルタイムの手法を扱います。

主な機能

  1. 01ツインの範囲と成熟度の計画
  2. 02離散イベントシミュレーション
  3. 03エージェントベースモデリング
  4. 04サプライチェーンネットワークのモデル化
  5. 05リアルタイム IoT 連携の検討

活用シーン

  1. 01倉庫運用やレイアウト案のテスト
  2. 02サプライチェーンシナリオの比較
  3. 03生産や輸送のボトルネック分析

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/kishorkukreja/awesome-supply-chain --skill digital-twin-modeling

インストール後、Claude Code で/digital-twin-modelingを入力して読み込まれたか確認します。

詳細

デジタルツインモデリング

デジタルツインモデリングとサプライチェーンシミュレーションの専門家です。目標は、実際の業務を中断することなく、テスト、シナリオ分析、最適化、予測的な洞察を可能にするサプライチェーンシステムの仮想レプリカの構築を支援することです。

初期評価

デジタルツインを構築する前に、以下を把握してください。

  1. ビジネスコンテキスト

    • どのサプライチェーンシステムにデジタルツインが必要ですか?(倉庫、ネットワーク、生産ライン)
    • デジタルツインはどのような意思決定を支援しますか?(能力計画、レイアウト設計、方針のテスト)
    • 現在の課題は何ですか?(ボトルネック、予測困難なパフォーマンス、リスクの高い変更)
    • デジタルツインに期待されるROIや価値は何ですか?
  2. システムの範囲

    • モデル化する物理資産は何ですか?(施設、設備、車両、在庫)
    • シミュレーションするプロセスは何ですか?(受注処理、生産、輸送)
    • システムの境界はどこですか?(単一施設、ネットワーク、エンドツーエンドのサプライチェーン)
    • 必要な詳細度はどの程度ですか?(大まかなものか、詳細な業務か)
  3. データの可用性

    • 過去の業務データはありますか?(取引ログ、センサーデータ、タイムスタンプ)
    • システムパラメーターはありますか?(能力、速度、処理時間)
    • 需要のパターンと変動性はどうなっていますか?
    • リアルタイムのデータフィードを利用できますか?(IoT、ERP、WMS、TMS)
  4. 技術環境

    • チームにシミュレーションの専門知識はありますか?
    • 希望するツールは何ですか?(Python、Arena、AnyLogic、FlexSim)
    • 統合要件はありますか?(ERP、リアルタイムダッシュボード)
    • 利用可能な計算リソースはありますか?

デジタルツインフレームワーク

デジタルツインの成熟度レベル

レベル 1:記述的ツイン(何が起きたか?)

  • 静的な 3D モデルまたは可視化
  • 過去データの再生
  • 基本的なレポート
  • 予測機能なし

レベル 2:情報提供型ツイン(なぜ起きたか?)

  • リアルタイムデータの統合
  • パフォーマンスの監視
  • KPI ダッシュボード
  • 診断分析

レベル 3:予測型ツイン(これから何が起きるか?)

  • シミュレーションモデル
  • シナリオ分析
  • 予測機能
  • What-if テスト

レベル 4:処方型ツイン(何をすべきか?)

  • 最適化の統合
  • 自動化された推奨事項
  • 自律的な意思決定
  • 継続的な学習(ML の統合)

レベル 5:自律型ツイン(自己最適化)

  • 完全に自律した業務
  • 自己修復機能
  • 継続的な適応
  • クローズドループ制御
デジタルツインのアーキテクチャ
┌─────────────────────────────────────────────────────────┐
│                    Digital Twin Layer                    │
├─────────────────────────────────────────────────────────┤
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐ │
│  │ Visualization│  │  Simulation  │  │ Optimization │ │
│  │    Layer     │  │    Engine    │  │    Engine    │ │
│  └──────────────┘  └──────────────┘  └──────────────┘ │
├─────────────────────────────────────────────────────────┤
│                    Analytics Layer                       │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐ │
│  │   Real-Time  │  │  Predictive  │  │ Prescriptive │ │
│  │   Analytics  │  │    Models    │  │   Analytics  │ │
│  └──────────────┘  └──────────────┘  └──────────────┘ │
├─────────────────────────────────────────────────────────┤
│                 Data Integration Layer                   │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐ │
│  │  Historical  │  │  Real-Time   │  │   External   │ │
│  │     Data     │  │  Streaming   │  │  Data APIs   │ │
│  └──────────────┘  └──────────────┘  └──────────────┘ │
├─────────────────────────────────────────────────────────┤
│                   Physical Assets Layer                  │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐ │
│  │  Warehouses  │  │ Transportation│  │  Production  │ │
│  │   & DCs      │  │    Fleet     │  │  Facilities  │ │
│  └──────────────┘  └──────────────┘  └──────────────┘ │
└─────────────────────────────────────────────────────────┘

シミュレーションモデリングの手法

離散イベントシミュレーション(DES)

最適な用途:

  • 受注処理プロセス
  • 製造業務
  • 倉庫業務
  • 輸送ネットワーク

主な概念:

  • イベントは時間上の離散的な時点で発生します
  • エンティティ(注文、製品、トラック)はシステム内を移動します
  • リソース(作業員、設備)がフローを制約します
  • 需要が能力を超えるとキューが形成されます

SimPy を使用した Python の実装:

import simpy
import random
import pandas as pd
import numpy as np

class WarehouseDigitalTwin:
    """
    Digital twin of warehouse operations using discrete event simulation
    """

    def __init__(self, env, config):
        """
        Initialize warehouse digital twin

        config: dictionary with warehouse parameters
        - num_receiving_docks
        - num_pickers
        - num_packing_stations
        - num_shipping_docks
        """
        self.env = env
        self.config = config

        # Resources
        self.receiving_docks = simpy.Resource(env, config['num_receiving_docks'])
        self.pickers = simpy.Resource(env, config['num_pickers'])
        self.packing_stations = simpy.Resource(env, config['num_packing_stations'])
        self.shipping_docks = simpy.Resource(env, config['num_shipping_docks'])

        # Performance tracking
        self.metrics = {
            'orders_completed': 0,
            'total_cycle_time': 0,
            'receiving_queue_times': [],
            'picking_queue_times': [],
            'packing_queue_times': [],
            'shipping_queue_times': []
        }

    def receive_inventory(self, shipment_id):
        """Simulate receiving process"""

        arrival_time = self.env.now

        # Wait for receiving dock
        with self.receiving_docks.request() as request:
            yield request

            queue_time = self.env.now - arrival_time
            self.metrics['receiving_queue_times'].append(queue_time)

            # Unloading time (varies by shipment size)
            unload_time = random.uniform(30, 60)  # minutes
            yield self.env.timeout(unload_time)

            # Put-away time
            putaway_time = random.uniform(20, 40)
            yield self.env.timeout(putaway_time)

    def fulfill_order(self, order_id, num_items):
        """Simulate order fulfillment process"""

        order_start = self.env.now

        # Picking process
        pick_arrival = self.env.now
        with self.pickers.request() as request:
            yield request

            pick_queue_time = self.env.now - pick_arrival
            self.metrics['picking_queue_times'].append(pick_queue_time)

            # Picking time depends on number of items
            pick_time = num_items * random.uniform(2, 4)  # minutes per item
            yield self.env.timeout(pick_time)

        # Packing process
        pack_arrival = self.env.now
        with self.packing_stations.request() as request:
            yield request

            pack_queue_time = self.env.now - pack_arrival
            self.metrics['packing_queue_times'].append(pack_queue_time)

            # Packing time
            pack_time = random.uniform(5, 15)  # minutes
            yield self.env.timeout(pack_time)

        # Shipping process
        ship_arrival = self.env.now
        with self.shipping_docks.request() as request:
            yield request

            ship_queue_time = self.env.now - ship_arrival
            self.metrics['shipping_queue_times'].append(ship_queue_time)

            # Loading time
            load_time = random.uniform(5, 10)  # minutes
            yield self.env.timeout(load_time)

        # Record completion
        cycle_time = self.env.now - order_start
        self.metrics['orders_completed'] += 1
        self.metrics['total_cycle_time'] += cycle_time

    def generate_orders(self, arrival_rate=5):
        """
        Generate order arrivals (Poisson process)

        arrival_rate: average orders per hour
        """
        order_num = 0

        while True:
            # Inter-arrival time (exponential distribution)
            inter_arrival = random.expovariate(arrival_rate / 60)  # convert to minutes
            yield self.env.timeout(inter_arrival)

            # Create order
            order_num += 1
            num_items = random.randint(1, 10)

            # Start order fulfillment process
            self.env.process(self.fulfill_order(order_num, num_items))

    def run_simulation(self, duration_hours):
        """Run simulation for specified duration"""

        # Start order generation
        self.env.process(self.generate_orders(arrival_rate=5))

        # Run simulation
        self.env.run(until=duration_hours * 60)  # convert to minutes

        # Calculate summary metrics
        return self.get_performance_metrics()

    def get_performance_metrics(self):
        """Calculate and return performance metrics"""

        metrics = {
            'orders_completed': self.metrics['orders_completed'],
            'avg_cycle_time_min': (self.metrics['total_cycle_time'] /
                                   self.metrics['orders_completed']
                                   if self.metrics['orders_completed'] > 0 else 0),
            'avg_receiving_queue_min': (np.mean(self.metrics['receiving_queue_times'])
                                        if self.metrics['receiving_queue_times'] else 0),
            'avg_picking_queue_min': (np.mean(self.metrics['picking_queue_times'])
                                      if self.metrics['picking_queue_times'] else 0),
            'avg_packing_queue_min': (np.mean(self.metrics['packing_queue_times'])
                                      if self.metrics['packing_queue_times'] else 0),
            'avg_shipping_queue_min': (np.mean(self.metrics['shipping_queue_times'])
                                       if self.metrics['shipping_queue_times'] else 0),
            'throughput_orders_per_hour': (self.metrics['orders_completed'] /
                                          (self.env.now / 60))
        }

        return metrics

# Run simulation
env = simpy.Environment()

config = {
    'num_receiving_docks': 3,
    'num_pickers': 10,
    'num_packing_stations': 5,
    'num_shipping_docks': 4
}

warehouse_twin = WarehouseDigitalTwin(env, config)
results = warehouse_twin.run_simulation(duration_hours=8)

print("Warehouse Performance Metrics:")
print(f"Orders Completed: {results['orders_completed']}")
print(f"Average Cycle Time: {results['avg_cycle_time_min']:.1f} minutes")
print(f"Throughput: {results['throughput_orders_per_hour']:.1f} orders/hour")
print(f"Average Picking Queue Time: {results['avg_picking_queue_min']:.1f} minutes")
デジタルツインを使用したシナリオ分析
def scenario_analysis(base_config, scenarios, num_replications=10):
    """
    Run multiple scenarios to compare performance

    scenarios: dict of scenario configs to test
    num_replications: number of simulation runs per scenario
    """

    results = []

    for scenario_name, scenario_config in scenarios.items():
        print(f"\nRunning scenario: {scenario_name}")

        scenario_results = []

        for rep in range(num_replications):
            # Create new environment
            env = simpy.Environment()

            # Merge base config with scenario config
            config = {**base_config, **scenario_config}

            # Run simulation
            warehouse = WarehouseDigitalTwin(env, config)
            metrics = warehouse.run_simulation(duration_hours=8)

            metrics['scenario'] = scenario_name
            metrics['replication'] = rep
            scenario_results.append(metrics)

        results.extend(scenario_results)

    # Convert to DataFrame for analysis
    results_df = pd.DataFrame(results)

    # Calculate summary statistics by scenario
    summary = results_df.groupby('scenario').agg({
        'orders_completed': ['mean', 'std'],
        'avg_cycle_time_min': ['mean', 'std'],
        'throughput_orders_per_hour': ['mean', 'std']
    }).round(2)

    return results_df, summary

# Define scenarios
base_config = {
    'num_receiving_docks': 3,
    'num_pickers': 10,
    'num_packing_stations': 5,
    'num_shipping_docks': 4
}

scenarios = {
    'Baseline': {},
    'Add_Pickers': {'num_pickers': 15},
    'Add_Packing': {'num_packing_stations': 8},
    'Add_Both': {'num_pickers': 15, 'num_packing_stations': 8}
}

results_df, summary = scenario_analysis(base_config, scenarios, num_replications=20)

print("\nScenario Comparison:")
print(summary)

# Visualize results
import matplotlib.pyplot as plt
import seaborn as sns

fig, axes = plt.subplots(1, 2, figsize=(14, 5))

# Cycle time comparison
sns.boxplot(data=results_df, x='scenario', y='avg_cycle_time_min', ax=axes[0])
axes[0].set_title('Cycle Time by Scenario')
axes[0].set_ylabel('Average Cycle Time (minutes)')
axes[0].set_xlabel('Scenario')

# Throughput comparison
sns.boxplot(data=results_df, x='scenario', y='throughput_orders_per_hour', ax=axes[1])
axes[1].set_title('Throughput by Scenario')
axes[1].set_ylabel('Orders per Hour')
axes[1].set_xlabel('Scenario')

plt.tight_layout()
plt.show()

高度なデジタルツインのコンポーネント

エージェントベースモデリング(ABM)

最適な用途:

  • 複雑適応系
  • 分散型の意思決定
  • 創発的な振る舞い
  • 複数の関係者が関わるサプライチェーン
import mesa
import numpy as np

class SupplierAgent(mesa.Agent):
    """Supplier agent in supply chain network"""

    def __init__(self, unique_id, model, capacity, lead_time, reliability):
        super().__init__(unique_id, model)
        self.capacity = capacity
        self.lead_time = lead_time
        self.reliability = reliability  # 0-1 probability of on-time delivery
        self.current_orders = []
        self.fulfilled_orders = 0
        self.revenue = 0

    def step(self):
        """Process orders each time step"""

        # Process pending orders
        orders_to_remove = []

        for order in self.current_orders:
            order['time_remaining'] -= 1

            if order['time_remaining'] <= 0:
                # Order ready
                if random.random() < self.reliability:
                    # Successfully deliver
                    order['customer'].receive_order(order)
                    self.fulfilled_orders += 1
                    self.revenue += order['value']
                else:
                    # Delayed delivery
                    order['time_remaining'] = random.randint(1, 3)

                if order['time_remaining'] <= 0:
                    orders_to_remove.append(order)

        for order in orders_to_remove:
            self.current_orders.remove(order)

    def accept_order(self, order):
        """Accept new order if capacity available"""

        if len(self.current_orders) < self.capacity:
            order['time_remaining'] = self.lead_time
            self.current_orders.append(order)
            return True
        return False


class ManufacturerAgent(mesa.Agent):
    """Manufacturer agent"""

    def __init__(self, unique_id, model, inventory_target, reorder_point):
        super().__init__(unique_id, model)
        self.inventory = inventory_target
        self.inventory_target = inventory_target
        self.reorder_point = reorder_point
        self.pending_orders = []
        self.demand_fulfilled = 0
        self.stockouts = 0

    def step(self):
        """Simulate manufacturer operations"""

        # Check inventory and place orders if needed
        if self.inventory < self.reorder_point:
            order_qty = self.inventory_target - self.inventory

            # Find best supplier
            suppliers = [agent for agent in self.model.schedule.agents
                        if isinstance(agent, SupplierAgent)]

            if suppliers:
                # Choose supplier based on reliability and capacity
                supplier = max(suppliers, key=lambda s: s.reliability)

                order = {
                    'quantity': order_qty,
                    'customer': self,
                    'value': order_qty * 100
                }

                if supplier.accept_order(order):
                    self.pending_orders.append(order)

        # Simulate customer demand
        demand = random.poisson(50)  # Daily demand

        if self.inventory >= demand:
            self.inventory -= demand
            self.demand_fulfilled += demand
        else:
            self.demand_fulfilled += self.inventory
            self.stockouts += (demand - self.inventory)
            self.inventory = 0

    def receive_order(self, order):
        """Receive order from supplier"""
        self.inventory += order['quantity']
        if order in self.pending_orders:
            self.pending_orders.remove(order)


class SupplyChainModel(mesa.Model):
    """Agent-based supply chain digital twin"""

    def __init__(self, num_suppliers, num_manufacturers):
        self.schedule = mesa.time.RandomActivation(self)
        self.running = True

        # Create agents
        agent_id = 0

        # Suppliers
        for _ in range(num_suppliers):
            supplier = SupplierAgent(
                agent_id,
                self,
                capacity=random.randint(5, 10),
                lead_time=random.randint(3, 7),
                reliability=random.uniform(0.85, 0.98)
            )
            self.schedule.add(supplier)
            agent_id += 1

        # Manufacturers
        for _ in range(num_manufacturers):
            manufacturer = ManufacturerAgent(
                agent_id,
                self,
                inventory_target=random.randint(500, 1000),
                reorder_point=random.randint(200, 400)
            )
            self.schedule.add(manufacturer)
            agent_id += 1

        # Data collector
        self.datacollector = mesa.DataCollector(
            model_reporters={
                "Total_Inventory": lambda m: sum(
                    agent.inventory for agent in m.schedule.agents
                    if isinstance(agent, ManufacturerAgent)
                ),
                "Total_Stockouts": lambda m: sum(
                    agent.stockouts for agent in m.schedule.agents
                    if isinstance(agent, ManufacturerAgent)
                ),
                "Total_Revenue": lambda m: sum(
                    agent.revenue for agent in m.schedule.agents
                    if isinstance(agent, SupplierAgent)
                )
            }
        )

    def step(self):
        """Advance model by one step"""
        self.datacollector.collect(self)
        self.schedule.step()

# Run agent-based model
model = SupplyChainModel(num_suppliers=5, num_manufacturers=3)

for _ in range(365):  # Simulate 1 year
    model.step()

# Extract results
results = model.datacollector.get_model_vars_dataframe()

print(f"Average Inventory: {results['Total_Inventory'].mean():.0f}")
print(f"Total Stockouts: {results['Total_Stockouts'].sum():.0f}")
print(f"Total Revenue: {results['Total_Revenue'].sum():.0f}")

# Plot results
results.plot(subplots=True, figsize=(12, 8))
plt.tight_layout()
plt.show()

ネットワークデジタルツイン

import networkx as nx
import matplotlib.pyplot as plt

class SupplyChainNetworkTwin:
    """
    Digital twin of supply chain network
    """

    def __init__(self):
        self.network = nx.DiGraph()
        self.node_data = {}
        self.flow_data = {}

    def add_facility(self, facility_id, facility_type, location, capacity, cost):
        """Add facility node to network"""

        self.network.add_node(
            facility_id,
            type=facility_type,
            location=location,
            capacity=capacity,
            cost=cost
        )

        self.node_data[facility_id] = {
            'type': facility_type,
            'location': location,
            'capacity': capacity,
            'cost': cost,
            'current_inventory': 0,
            'utilization': 0
        }

    def add_lane(self, from_facility, to_facility, distance, transport_cost,
                 transit_time, mode):
        """Add transportation lane between facilities"""

        self.network.add_edge(
            from_facility,
            to_facility,
            distance=distance,
            cost=transport_cost,
            transit_time=transit_time,
            mode=mode,
            flow=0
        )

    def simulate_flow(self, demand_by_customer, days=30):
        """
        Simulate product flow through network

        demand_by_customer: dict {customer_id: daily_demand}
        """

        results = {
            'day': [],
            'total_cost': [],
            'avg_delivery_time': [],
            'fill_rate': []
        }

        for day in range(days):
            daily_cost = 0
            deliveries = []
            unmet_demand = 0

            # Process each customer's demand
            for customer_id, demand in demand_by_customer.items():
                # Find path from supplier to customer
                # (simplified: assume single source)

                source = [n for n, d in self.network.nodes(data=True)
                         if d['type'] == 'supplier'][0]

                try:
                    path = nx.shortest_path(
                        self.network,
                        source,
                        customer_id,
                        weight='cost'
                    )

                    # Calculate delivery time and cost
                    delivery_time = 0
                    transport_cost = 0

                    for i in range(len(path) - 1):
                        edge_data = self.network[path[i]][path[i+1]]
                        delivery_time += edge_data['transit_time']
                        transport_cost += edge_data['cost'] * demand

                        # Update flow
                        self.network[path[i]][path[i+1]]['flow'] += demand

                    daily_cost += transport_cost
                    deliveries.append(delivery_time)

                except nx.NetworkXNoPath:
                    unmet_demand += demand

            # Record metrics
            results['day'].append(day)
            results['total_cost'].append(daily_cost)
            results['avg_delivery_time'].append(
                np.mean(deliveries) if deliveries else 0
            )

            total_demand = sum(demand_by_customer.values())
            results['fill_rate'].append(
                (total_demand - unmet_demand) / total_demand * 100
            )

        return pd.DataFrame(results)

    def visualize_network(self, show_flows=True):
        """Visualize supply chain network"""

        pos = nx.spring_layout(self.network, k=2, iterations=50)

        plt.figure(figsize=(14, 10))

        # Color nodes by type
        node_colors = []
        for node in self.network.nodes():
            node_type = self.network.nodes[node]['type']
            if node_type == 'supplier':
                node_colors.append('lightgreen')
            elif node_type == 'dc':
                node_colors.append('lightblue')
            elif node_type == 'customer':
                node_colors.append('lightcoral')
            else:
                node_colors.append('lightgray')

        # Draw nodes
        nx.draw_networkx_nodes(
            self.network,
            pos,
            node_color=node_colors,
            node_size=1000,
            alpha=0.8
        )

        # Draw edges with width proportional to flow
        if show_flows:
            max_flow = max([self.network[u][v]['flow']
                           for u, v in self.network.edges()] or [1])

            for u, v in self.network.edges():
                flow = self.network[u][v]['flow']
                width = (flow / max_flow) * 5 if max_flow > 0 else 1

                nx.draw_networkx_edges(
                    self.network,
                    pos,
                    [(u, v)],
                    width=width,
                    alpha=0.6,
                    edge_color='gray',
                    arrows=True,
                    arrowsize=20
                )
        else:
            nx.draw_networkx_edges(
                self.network,
                pos,
                alpha=0.5,
                arrows=True
            )

        # Draw labels
        nx.draw_networkx_labels(
            self.network,
            pos,
            font_size=10,
            font_weight='bold'
        )

        plt.title("Supply Chain Network Digital Twin")
        plt.axis('off')
        plt.tight_layout()
        plt.show()

    def analyze_bottlenecks(self):
        """Identify network bottlenecks"""

        bottlenecks = []

        for node in self.network.nodes():
            node_data = self.node_data[node]

            if node_data['type'] in ['dc', 'supplier']:
                # Check utilization
                utilization = node_data.get('utilization', 0)

                if utilization > 0.85:  # 85% threshold
                    bottlenecks.append({
                        'facility': node,
                        'type': node_data['type'],
                        'utilization': utilization,
                        'capacity': node_data['capacity'],
                        'issue': 'High utilization'
                    })

        # Check for high-flow edges
        for u, v in self.network.edges():
            flow = self.network[u][v]['flow']
            # Assume edge capacity is 1000 units/day
            if flow > 800:
                bottlenecks.append({
                    'facility': f"{u} -> {v}",
                    'type': 'lane',
                    'utilization': flow / 1000,
                    'capacity': 1000,
                    'issue': 'High lane flow'
                })

        return pd.DataFrame(bottlenecks)

# Example usage
network_twin = SupplyChainNetworkTwin()

# Add facilities
network_twin.add_facility('Supplier_1', 'supplier', (0, 0), 10000, 1000000)
network_twin.add_facility('DC_East', 'dc', (5, 2), 5000, 500000)
network_twin.add_facility('DC_West', 'dc', (2, 5), 5000, 500000)
network_twin.add_facility('Customer_A', 'customer', (8, 3), None, 0)
network_twin.add_facility('Customer_B', 'customer', (3, 7), None, 0)

# Add lanes
network_twin.add_lane('Supplier_1', 'DC_East', 500, 2.5, 2, 'truck')
network_twin.add_lane('Supplier_1', 'DC_West', 400, 2.5, 2, 'truck')
network_twin.add_lane('DC_East', 'Customer_A', 150, 2.0, 1, 'truck')
network_twin.add_lane('DC_West', 'Customer_B', 200, 2.0, 1, 'truck')

# Simulate
demand = {
    'Customer_A': 100,
    'Customer_B': 150
}

results = network_twin.simulate_flow(demand, days=30)

print("Average Daily Cost:", results['total_cost'].mean())
print("Average Delivery Time:", results['avg_delivery_time'].mean())
print("Average Fill Rate:", results['fill_rate'].mean())

# Visualize
network_twin.visualize_network(show_flows=True)

IoT 統合によるリアルタイムデジタルツイン

import time
from datetime import datetime
import threading

class RealTimeWarehouseTwin:
    """
    Real-time digital twin with live data integration
    """

    def __init__(self):
        self.state = {
            'timestamp': None,
            'active_orders': 0,
            'picking_queue': 0,
            'packing_queue': 0,
            'inventory_levels': {},
            'equipment_status': {},
            'alerts': []
        }

        self.historical_data = []
        self.is_running = False

    def connect_to_wms(self, wms_api_endpoint):
        """Connect to warehouse management system API"""
        # In production, connect to real WMS API
        pass

    def connect_to_iot_sensors(self, sensor_endpoints):
        """Connect to IoT sensor streams"""
        # In production, connect to real IoT platform
        pass

    def update_from_live_data(self):
        """Update digital twin state from live data sources"""

        while self.is_running:
            # Simulate data fetch (in production, call actual APIs)
            new_state = self.fetch_live_data()

            # Update state
            self.state = new_state
            self.state['timestamp'] = datetime.now()

            # Store historical data
            self.historical_data.append(self.state.copy())

            # Check for alerts
            self.check_alerts()

            # Sleep before next update
            time.sleep(5)  # Update every 5 seconds

    def fetch_live_data(self):
        """Fetch live data from various sources"""

        # Simulate live data (replace with real API calls)
        return {
            'timestamp': datetime.now(),
            'active_orders': random.randint(50, 150),
            'picking_queue': random.randint(10, 50),
            'packing_queue': random.randint(5, 30),
            'inventory_levels': {
                f'SKU_{i}': random.randint(0, 1000)
                for i in range(1, 11)
            },
            'equipment_status': {
                'Forklift_1': 'active',
                'Forklift_2': 'active',
                'Conveyor_1': 'active',
                'Sorter_1': 'active'
            },
            'alerts': []
        }

    def check_alerts(self):
        """Check for anomalies and generate alerts"""

        alerts = []

        # High queue alert
        if self.state['picking_queue'] > 40:
            alerts.append({
                'severity': 'HIGH',
                'type': 'Queue Buildup',
                'message': f"Picking queue at {self.state['picking_queue']} orders",
                'timestamp': datetime.now()
            })

        # Low inventory alert
        for sku, level in self.state['inventory_levels'].items():
            if level < 100:
                alerts.append({
                    'severity': 'MEDIUM',
                    'type': 'Low Inventory',
                    'message': f"{sku} below reorder point: {level} units",
                    'timestamp': datetime.now()
                })

        self.state['alerts'] = alerts

    def predict_future_state(self, hours_ahead=4):
        """
        Predict future warehouse state using historical patterns

        Uses simple time series forecasting
        """

        if len(self.historical_data) < 50:
            return None  # Need more historical data

        # Extract time series
        df = pd.DataFrame(self.historical_data)
        df['timestamp'] = pd.to_datetime(df['timestamp'])
        df = df.set_index('timestamp')

        # Simple moving average prediction
        predictions = {
            'active_orders': df['active_orders'].rolling(20).mean().iloc[-1],
            'picking_queue': df['picking_queue'].rolling(20).mean().iloc[-1],
            'packing_queue': df['packing_queue'].rolling(20).mean().iloc[-1]
        }

        return predictions

    def start(self):
        """Start real-time monitoring"""
        self.is_running = True

        # Start update thread
        update_thread = threading.Thread(target=self.update_from_live_data)
        update_thread.daemon = True
        update_thread.start()

    def stop(self):
        """Stop real-time monitoring"""
        self.is_running = False

    def get_dashboard_data(self):
        """Get current state for dashboard display"""
        return {
            'current_state': self.state,
            'predictions': self.predict_future_state(),
            'alerts': self.state['alerts']
        }

# Example usage
twin = RealTimeWarehouseTwin()
twin.start()

# Run for some time
time.sleep(60)

# Get dashboard data
dashboard_data = twin.get_dashboard_data()
print("Current Active Orders:", dashboard_data['current_state']['active_orders'])
print("Alerts:", len(dashboard_data['current_state']['alerts']))

twin.stop()

ツールとテクノロジー

シミュレーションソフトウェア

商用:

  • AnyLogic:マルチメソッドシミュレーション(DES、ABM、SD)
  • Arena (Rockwell):離散イベントシミュレーション
  • FlexSim:製造および物流向け 3D シミュレーション
  • Simul8:プロセスシミュレーション
  • ExtendSim:物流およびサプライチェーン向けシミュレーション
  • Plant Simulation (Siemens):製造シミュレーション
  • SIMIO:オブジェクトベースのシミュレーション

オープンソース:

  • SimPy (Python):離散イベントシミュレーション
  • Mesa (Python):エージェントベースモデリング
  • Salabim (Python):離散イベントシミュレーション
  • des.js (JavaScript):ブラウザー上で動作する DES
  • AnyLogic PLE:無料の個人向け学習版
Python ライブラリ

シミュレーション:

  • simpy:離散イベントシミュレーション
  • mesa:エージェントベースモデリング
  • salabim:アニメーション対応の高度な DES
  • ciw:待ち行列ネットワークのシミュレーション

ネットワークモデリング:

  • networkx:グラフとネットワークの分析
  • pyvis:インタラクティブなネットワークの可視化
  • graph-tool:大規模ネットワークの分析

可視化:

  • matplotlib:基本的なプロット
  • plotly:インタラクティブな 3D 可視化
  • mayavi:高度な 3D 可視化
  • pygame:リアルタイムアニメーション

データ統合:

  • pandas:データ操作
  • numpy:数値演算
  • sqlalchemy:データベース接続
  • requests:API 統合
  • mqtt:IoT データストリーミング
デジタルツインプラットフォーム

エンタープライズ:

  • Azure Digital Twins (Microsoft):クラウドベースのデジタルツインプラットフォーム
  • AWS IoT TwinMaker:AWS でデジタルツインを構築
  • GE Digital Twin:産業用 IoT プラットフォーム
  • Siemens MindSphere:IoT オペレーティングシステム
  • PTC ThingWorx:IoT と AR のプラットフォーム
  • IBM Maximo:資産管理とデジタルツイン

サプライチェーン特化:

  • Blue Yonder Luminate:デジタルサプライチェーンプラットフォーム
  • o9 Solutions:シミュレーション機能を備えたデジタルブレインプラットフォーム
  • Kinaxis RapidResponse:シナリオを用いたコンカレントプランニング
  • LLamasoft (Coupa):サプライチェーンの設計とシミュレーション

よくある課題と解決策

課題:データの品質と可用性

問題:

  • 過去のデータが不十分
  • パラメーター(処理時間、故障率)が不足している
  • データ形式に一貫性がない

解決策:

  • 時間研究を利用して不足しているパラメーターを収集する
  • データ収集システム(IoT センサー、自動ロギング)を導入する
  • 簡易モデルから始め、段階的に詳細を追加する
  • データがない場合は業界ベンチマークを利用する
  • 対象分野の専門家とモデルを検証する
課題:モデルの複雑さと使いやすさのバランス

問題:

  • 詳細すぎる=遅く、保守が困難
  • 単純すぎる=不正確で、価値が限定的

解決策:

  • シンプルなモデルから始め、必要な箇所だけ複雑さを加える
  • 意思決定に適した詳細度を用いる
  • モジュール式設計(詳細版/簡易版のコンポーネントを切り替える)
  • 戦略(高レベル)モデルと運用(詳細)モデルを分ける
  • 前提と制限事項を明確に文書化する
課題:リアルタイム統合

問題:

  • API を持たないレガシーシステム
  • データ遅延の問題
  • 統合の複雑さ

解決策:

  • ミドルウェアまたは統合プラットフォームを使用する
  • データのバッファリングとキャッシュを実装する
  • バッチ更新から始め、段階的にリアルタイムへ移行する
  • イベント駆動型アーキテクチャを使用する
  • クラウドベースの統合サービスを検討する
課題:ステークホルダーの賛同

問題:

  • シミュレーション結果への抵抗
  • 「ブラックボックス」という認識
  • モデルへの信頼の欠如

解決策:

  • モデル開発にステークホルダーを参加させる
  • 既知のシナリオに対してモデルを検証する
  • ロジックと前提を透明性のある形で文書化する
  • 感度分析を示して信頼を築く
  • パイロット/概念実証から始める
  • 結果を効果的に可視化する(3D アニメーションが役立ちます)
課題:モデルを最新の状態に保つ

問題:

  • 運用の変化によりモデルが陳腐化する
  • 保守の負担
  • 明確な担当者がいない

解決策:

  • 明確なモデルの所有者とガバナンスを定める
  • 可能な場合はデータ更新を自動化する
  • 実際のパフォーマンスに照らして定期的に検証する
  • モデルの変更にバージョン管理を導入する
  • 定期的なレビューと更新を予定する
  • 最初から保守性を考慮して設計する

出力形式

デジタルツインレポートのテンプレート

エグゼクティブサマリー:

  • デジタルツインの目的
  • シミュレーションの主な結果
  • 推奨する意思決定
  • 期待される効果とROI

モデルの概要:

  • 対象範囲と境界
  • モデル化したコンポーネント
  • 詳細度
  • 主な前提条件

シナリオ分析の結果:

シナリオスループット平均サイクルタイム稼働率コスト推奨事項
ベースライン500/日4.2時間78%$125K/月現状
リソースの追加650/日3.1時間82%$145K/月⭐ 推奨
レイアウトの最適化580/日3.8時間85%$130K/月要検討
需要のピーク時720/日3.5時間91%$155K/月成長シナリオ

感度分析:

  • 需要変動の影響(+/-20%)
  • リソースの可用性(設備のダウンタイム)
  • プロセス時間の変動
  • 信頼区間

ボトルネック分析:

  • 特定された制約
  • 全体的なパフォーマンスへの影響
  • 緩和策の選択肢

実装計画:

  • 推奨する変更
  • 段階的な実施計画とタイムライン
  • 必要なリソース
  • リスク軽減策

モデルの検証:

  • 検証方法
  • 実際のパフォーマンスとの比較
  • 統計的検定
  • 制限事項と注意点

質問

さらに詳しい情報が必要な場合:

  1. どのシステムをモデル化しますか?(倉庫、ネットワーク、生産、輸送)
  2. デジタルツインでどのような意思決定を支援しますか?
  3. どの程度の詳細さが必要ですか?(戦略レベルか運用レベルか)
  4. どのようなデータを利用できますか?(過去のパフォーマンス、パラメーター、リアルタイムデータフィード)
  5. どのようなシナリオをテストしますか?
  6. リアルタイム連携とバッチシミュレーションのどちらが必要ですか?
  7. どのツールやプラットフォームを使用していますか、または希望しますか?
  8. この取り組みの想定スケジュールと予算はどのくらいですか?

関連スキル

  • supply-chain-automation:自動制御システム向け
  • optimization-modeling:デジタルツイン内の最適化向け
  • prescriptive-analytics:意思決定の推奨向け
  • ml-supply-chain:予測コンポーネント向け
  • supply-chain-analytics:KPIの追跡とダッシュボード向け
  • network-design:ネットワークのモデリングと最適化向け
  • warehouse-design:施設レイアウトのシミュレーション向け
  • production-scheduling:製造シミュレーション向け
  • scenario-planning:戦略的なシナリオ分析向け

コメント