### Example: Simple Supply Chain Source: https://github.com/supplychainsimulation/supplynetpy/blob/main/REVIEW.md Canonical example demonstrating a basic supply chain setup. The profit, total cost, and transportation cost remain unchanged after the Link registration fix. ```python from supplychainpy import demand, environment, inventory, manufacturer, link env = environment.Environment() # Nodes demand_node = demand.Demand(env=env, name='Demand') inventory_node = inventory.Inventory(env=env, name='Inventory', unit_cost=1.0, unit_capacity=1000) # Links link_demand_inventory = link.Link(env=env, source=demand_node, sink=inventory_node, cost=1.0, lead_time=lambda: 1) # Run simulation results = env.run(until=5) # Print results print(f"profit: {results.profit:.1f}") print(f"total_cost: {results.total_cost:.1f}") print(f"transportation_cost: {results.transportation_cost:.1f}") ``` -------------------------------- ### Run Quick-Start Example Source: https://github.com/supplychainsimulation/supplynetpy/blob/main/CLAUDE.md Execute the canonical quick-start example to perform a sanity check on core semantics. The expected output is profit = -4435.0. ```bash python examples/py/intro_simple.py ``` -------------------------------- ### Serve and Build Documentation Locally Source: https://github.com/supplychainsimulation/supplynetpy/blob/main/CLAUDE.md Install documentation dependencies and then serve or build the documentation locally using mkdocs. ```bash pip install -r docs/requirements.txt ``` ```bash mkdocs serve ``` ```bash mkdocs build ``` -------------------------------- ### Newsvendor Simulation Setup Source: https://github.com/supplychainsimulation/supplynetpy/blob/main/docs/example-newvendor.md This Python script sets up and runs a simulation for the newsvendor problem. It configures an infinite supplier, a newsvendor with perishable inventory and periodic replenishment, and normally distributed demand. Ensure the script is placed in the correct directory relative to the 'examples' folder. ```python from supplychainpy.demand_models.demand_models import normal_quantity from supplychainpy.inventory_models.inventory_models import Inventory from supplychainpy.order_policies.order_policies import OrderPolicy from supplychainpy.simulations.simulations import Simulation from supplychainpy.supplier_models.supplier_models import InfiniteSupplier from supplychainpy.node import Node # Simulation parameters order_quantity = 110 num_days = 365 # Create nodes supplier = InfiniteSupplier() newsvendor = Node( inventory_model=Inventory(level=order_quantity, perishable_shelf_life=1), order_policy=OrderPolicy(policy_type='periodic', reorder_frequency=1), demand_model=normal_quantity(mean=100, stdev=20), consume_available=True ) # Create simulation sim = Simulation(num_days=num_days) sim.add_supplier(supplier=supplier, node=newsvendor) sim.run_simulation() # Analyze results sim.plot_profit_curve(order_quantity=order_quantity) ``` -------------------------------- ### Install SupplyNetPy Source: https://github.com/supplychainsimulation/supplynetpy/blob/main/README.md Install the library using pip. Ensure you have Python and pip installed. ```sh pip install supplynetpy ``` -------------------------------- ### Install SupplyNetPy and Dependencies Source: https://github.com/supplychainsimulation/supplynetpy/blob/main/validation/README.md Install the necessary libraries for SupplyNetPy, including simpy, numpy, and matplotlib. ```bash pip install supplynetpy simpy numpy matplotlib ``` -------------------------------- ### Setup Simulation Environment Source: https://github.com/supplychainsimulation/supplynetpy/blob/main/examples/notebooks/case_study_pharma.ipynb Initializes the simulation environment, including supplier, distributor, link, and demand processes. Configures node disruption and recovery times using geometric distributions. ```python def setup_simulation(env, s, S, ini_level, st, disrupt_time, recovery_time): """Setup environment, supplier, distributor, link, and demand process.""" supplier = scm.Supplier(env, "S1", "Supplier 1", "infinite_supplier", node_disrupt_time=disrupt_time.geometric, node_recovery_time=recovery_time.geometric) distributor = scm.InventoryNode(env, "D1", "Distributor 1", "distributor", capacity=float('inf'), initial_level=ini_level, inventory_holding_cost=h, inventory_type="perishable", manufacture_date=manufacturer_date_cal, shelf_life=e, replenishment_policy=scm.SSReplenishment, policy_param={'s': s, 'S': S}, product_buy_price=0, product_sell_price=price) link = scm.Link(env, "l1", supplier, distributor, cost=o, lead_time=lambda: l) demand = scm.Demand(env, "d1", "demand 1", order_arrival_model=lambda: 1, order_quantity_model=st.poisson_demand, demand_node=distributor) return supplier, distributor, demand, link ``` -------------------------------- ### Setup Simulation Environment Source: https://github.com/supplychainsimulation/supplynetpy/blob/main/docs/case_study_pharma.md Defines the simulation environment, including suppliers, distributors, links, and demand processes. Used for initializing a new simulation run. ```python def setup_simulation(env, s, S, ini_level, st, disrupt_time, recovery_time): """Setup environment, supplier, distributor, link, and demand process.""" supplier = scm.Supplier(env=env, ID="S1", name="Supplier 1", node_type="infinite_supplier", node_disrupt_time=disrupt_time.geometric, node_recovery_time=recovery_time.geometric) distributor = scm.InventoryNode(env=env, ID="D1", name="Distributor 1", node_type="distributor", capacity=float('inf'), initial_level=ini_level, inventory_holding_cost=h, inventory_type="perishable", manufacture_date=manufacturer_date_cal, shelf_life=e, replenishment_policy=scm.SSReplenishment, policy_param={'s': s, 'S': S}, product_buy_price=0, product_sell_price=price) link = scm.Link(env=env, ID="l1", source=supplier, sink=distributor, cost=o, lead_time=lambda: l) demand = scm.Demand(env=env, ID="d1", name="demand 1", order_arrival_model=lambda: 1, order_quantity_model=st.poisson_demand, demand_node=distributor) return supplier, distributor, demand, link ``` -------------------------------- ### EOQ Simulation Setup and Execution Source: https://github.com/supplychainsimulation/supplynetpy/blob/main/examples/notebooks/EOQ_est.ipynb Sets up the simulation environment, defines supply chain components (supplier, inventory node, link, demand), and runs the simulation for a specified length. It iterates through different lot sizes to collect cost and unmet demand data. ```python import simpy import numpy as np import matplotlib.pyplot as plt import SupplyNetPy.Components as scm """ Demand for the Deskpro computer at Best Buy is 1,000 units per month. Best Buy incurs a fixed order placement, transportation, and receiving cost of $4,000 each time an order is placed. Each computer costs Best Buy $500 and the retailer has a holding cost of 20 percent. Evaluate the number of computers that the store manager should order in each replenishment lot. Analysis: In this case, the store manager has the following inputs: - Annual demand, D = 1,000 * 12 = 12,000 units (approx 34 units per day) - Order cost per lot, S = 4,000 - Unit cost per computer, C = 500 - Holding cost per year as a fraction of unit cost, h = 0.2 (500*0.2 = 100 per year => 100/365 = 0.273 per day) Assumptions: - Demand is constant and deterministic - Lead time is zero Optimum Economic Order Quantity (EOQ) is determined to minimize the total cost. Total cost = Annual material cost + Annual ordering cost + Annual holding cost This is same as -> Total cost = total transportation cost + inventory cost (we'll ignore material cost since it is constant) """ D = 12000 # annual demand d = 34 # daily demand order_cost = 4000 # order cost unit_cost = 500 # unit cost holding_cost = 0.273 # holding cost per day lead_time = 0 # lead time simlen = 3650 # simulation length in days total_cost_arr = [] unsat_arr = [] print(f"lot size Inv holding cost Order cost Average cost(per day) Unmet demand") for lot_size in range(800,1600,10): order_interval = 365*lot_size/D env = simpy.Environment() hp_supplier = scm.Supplier(env=env, ID="S1", name="HPComputers", node_type="infinite_supplier") bestbuy = scm.InventoryNode(env=env, ID="D1", name="Best Buy", node_type="distributor", capacity=lot_size, initial_level=lot_size, inventory_holding_cost=holding_cost, replenishment_policy=scm.PeriodicReplenishment, product_buy_price=450, policy_param={'T':order_interval,'Q':lot_size}, product_sell_price=unit_cost) link1 = scm.Link(env=env,ID="l1", source=hp_supplier, sink=bestbuy, cost=order_cost, lead_time=lambda: lead_time) demand1 = scm.Demand(env=env,ID="d1", name="demand_d1", order_arrival_model=lambda: 1, order_quantity_model=lambda: d, demand_node=bestbuy, consume_available=True) scm.global_logger.disable_logging() # disable logging for all components env.run(until=simlen) bb_invlevels = np.array(bestbuy.inventory.instantaneous_levels) hp_sup_transportation_cost = bestbuy.stats.transportation_cost total_cost = bestbuy.stats.inventory_carry_cost + bestbuy.stats.transportation_cost total_cost_arr.append([lot_size, total_cost/simlen]) unsat_demand = demand1.stats.demand_placed[1]-demand1.stats.fulfillment_received[1] unsat_arr.append([lot_size,unsat_demand]) print(f"{lot_size} {bestbuy.stats.inventory_carry_cost:.2f} {bestbuy.stats.transportation_cost} {total_cost/simlen:.2f} {unsat_demand:.2f}") total_cost_arr = np.array(total_cost_arr) unsat_arr = np.array(unsat_arr) EOQ = np.argmin(total_cost_arr[:,1]) plt.figure() plt.plot(total_cost_arr[:,0], total_cost_arr[:,1],marker='.',linestyle='-') plt.plot(total_cost_arr[EOQ,0], total_cost_arr[EOQ,1],marker='o',color='red',label=f'EOQ = {total_cost_arr[EOQ,0]:.2f} with cost = {total_cost_arr[EOQ,1]:.2f}') plt.xlabel("lot size") plt.ylabel("Average cost per day") plt.title("Average cost vs lot size") plt.legend() plt.grid() plt.show() ``` -------------------------------- ### Example: Supply Chain with Factory Source: https://github.com/supplychainsimulation/supplynetpy/blob/main/REVIEW.md Example featuring a manufacturer node. The core metrics (profit, total cost, transportation cost) are verified to be consistent after the Link registration refactoring. ```python from supplychainpy import demand, environment, inventory, manufacturer, link env = environment.Environment() # Nodes demand_node = demand.Demand(env=env, name='Demand') inventory_node = inventory.Inventory(env=env, name='Inventory', unit_cost=1.0, unit_capacity=1000) manufacturer_node = manufacturer.Manufacturer(env=env, name='Manufacturer', unit_cost=2.0, unit_capacity=500) # Links link_demand_inventory = link.Link(env=env, source=demand_node, sink=inventory_node, cost=1.0, lead_time=lambda: 1) link_inventory_manufacturer = link.Link(env=env, source=inventory_node, sink=manufacturer_node, cost=1.0, lead_time=lambda: 1) # Run simulation results = env.run(until=5) # Print results print(f"profit: {results.profit:.1f}") print(f"total_cost: {results.total_cost:.1f}") print(f"transportation_cost: {results.transportation_cost:.1f}") ``` -------------------------------- ### Example: Link Construction with Plain Node Source: https://github.com/supplychainsimulation/supplynetpy/blob/main/REVIEW.md Demonstrates successful Link construction when the sink is a plain Node instance. Previously, this would raise an AttributeError because plain Nodes did not have a suppliers attribute. ```python from supplychainpy import environment, node, link env = environment.Environment() # Nodes source_node = node.Node(env=env, name='Source') plain_node = node.Node(env=env, name='PlainNode') # Link construction that now succeeds link_obj = link.Link(env=env, source=source_node, sink=plain_node, cost=1, lead_time=lambda: 1) # Verification: plain_node.suppliers should now be a list containing link_obj print(f"plain_node.suppliers == [link_obj]: {plain_node.suppliers == [link_obj]}") ``` -------------------------------- ### Install SupplyNetPy in Editable Mode Source: https://github.com/supplychainsimulation/supplynetpy/blob/main/CLAUDE.md Use this command to install the library in editable mode for development purposes. ```bash pip install -e . ``` -------------------------------- ### Create and Simulate Supply Chain Network Source: https://github.com/supplychainsimulation/supplynetpy/blob/main/README.md Example demonstrating how to create a supply chain network with suppliers, distributors, and demand nodes, and then simulate its behavior over a specified time period. Requires importing the SupplyNetPy Components module. ```python import SupplyNetPy.Components as scm supplier1 = {'ID': 'S1', 'name': 'Supplier1', 'node_type': 'infinite_supplier'} distributor1 = {'ID': 'D1', 'name': 'Distributor1', 'node_type': 'distributor', 'capacity': 150, 'initial_level': 50, 'inventory_holding_cost': 0.2, 'replenishment_policy': scm.SSReplenishment, 'policy_param': {'s':100,'S':150}, 'product_buy_price': 100,'product_sell_price': 105} link1 = {'ID': 'L1', 'source': 'S1', 'sink': 'D1', 'cost': 5, 'lead_time': lambda: 2} demand1 = {'ID': 'd1', 'name': 'Demand1', 'order_arrival_model': lambda: 1, 'order_quantity_model': lambda: 10, 'demand_node': 'D1'} # create a supply chain network supplychainnet = scm.create_sc_net(nodes=[supplier1, distributor1], links=[link1], demands=[demand1]) # simulate for 20 days supplychainnet = scm.simulate_sc_net(supplychainnet, sim_time=20, logging=True) ``` -------------------------------- ### Define Supply Chain Network Components and Links Source: https://github.com/supplychainsimulation/supplynetpy/blob/main/examples/notebooks/simple_supplier_n_factory.ipynb Initializes simulation environment, defines raw materials, supplier, product, manufacturer, distributor, and links between them. This setup includes defining capacities, costs, and replenishment policies. ```python simtime = 365 env = simpy.Environment() raw_mat = scm.RawMaterial(ID="RM1", name="raw materials mouse", extraction_quantity=650, extraction_time=3, mining_cost=0.01, cost=0.1) #supplier = scm.Supplier(env=env, ID='S1', name='Supplier', node_type="infinite_supplier", raw_material=raw_mat) supplier = scm.Supplier(env=env, ID='S1', name='Supplier', node_type="supplier", raw_material=raw_mat, capacity=5000, initial_level=5000, inventory_holding_cost=0.1) mouse = scm.Product(ID="MW11", name="Toad M11", manufacturing_cost=10, manufacturing_time=1, sell_price=300, buy_price=150, raw_materials=[(raw_mat,1)], batch_size=2500) factory = scm.Manufacturer(env=env, ID='F1', name='Factory Cansaulim', capacity=2500, initial_level=800, shelf_life=10, inventory_holding_cost=0.1, replenishment_policy=scm.SSReplenishment, policy_param={'s':1500, 'S':2500}, product=mouse, product_sell_price=300, inventory_type="perishable") distributor = scm.InventoryNode(env=env, ID='D1', name='Mumbai Warehouse', node_type="warehouse", shelf_life=10, capacity=1000, initial_level=1000, inventory_holding_cost=0.22, replenishment_policy = scm.RQReplenishment, policy_param={'R':500, 'Q':500}, product_buy_price=300, product_sell_price=320, inventory_type="perishable") link1 = scm.Link(env=env, ID='L1', source=supplier, sink=factory, cost=10, lead_time=lambda: 1) link2 = scm.Link(env=env, ID='L2', source=factory, sink=distributor, cost=10, lead_time=lambda: 1) demand1 = scm.Demand(env=env, ID='demand_D1', name='Demand Pune', order_arrival_model=lambda: 2, order_quantity_model=lambda:50, delivery_cost=lambda:10, lead_time=lambda:0.9, demand_node=distributor) demand2 = scm.Demand(env=env, ID='demand_D2', name='Demand Kalyan', order_arrival_model=lambda: 1, order_quantity_model=lambda:35, delivery_cost=lambda:10, lead_time=lambda:0.9, demand_node=distributor) ``` -------------------------------- ### Configure Inventory Node for Power-Outage Disruption Source: https://github.com/supplychainsimulation/supplynetpy/blob/main/docs/api-reference/api-intro.md This example shows how to configure an InventoryNode to simulate a power-outage style disruption, where a random fraction of the current shelf stock is destroyed. The `disruption_impact` is set to 'destroy_fraction', and `disruption_loss_fraction` is a lambda function generating a random uniform value between 0.1 and 0.4. ```python warehouse = scm.InventoryNode( ..., disruption_impact="destroy_fraction", disruption_loss_fraction=lambda: random.uniform(0.1, 0.4), ) ``` -------------------------------- ### Initialize Simulation Environment Source: https://github.com/supplychainsimulation/supplynetpy/blob/main/docs/safety_inv_est.md Sets up the simulation environment using simpy and numpy for demand generation. It defines daily demand parameters and the total simulation length. ```python import simpy import numpy as np import matplotlib.pyplot as plt import SupplyNetPy.Components as scm daily_demand_mean = 2500/7 daily_demand_std = 500/(7**0.5) simlen = 3650 # simulation length in days def normal_quantity(): global daily_demand_mean, daily_demand_std sample = -1 while sample < 0: sample = np.random.normal(daily_demand_mean, daily_demand_std, 1)[0] return sample env = simpy.Environment() ``` -------------------------------- ### Example: Invalid Link Registration Sink Mismatch Source: https://github.com/supplychainsimulation/supplynetpy/blob/main/REVIEW.md Demonstrates the ValueError raised when a Link is registered with a sink node that is not its designated sink. ```python from supplychainpy import environment, node, link env = environment.Environment() source_node1 = node.Node(env=env, name='Source1') source_node2 = node.Node(env=env, name='Source2') sink_node = node.Node(env=env, name='Sink') link_obj = link.Link(env=env, source=source_node1, sink=sink_node, cost=1, lead_time=lambda: 1) # Attempt to add link_obj to a different node's suppliers, expecting ValueError try: source_node2.add_supplier_link(link_obj) except ValueError as e: print(f"Caught expected error: {e}") ``` -------------------------------- ### Create Network and Run Simulation Source: https://github.com/supplychainsimulation/supplynetpy/blob/main/docs/example.md Initializes the supply chain network with defined nodes and links, then executes the simulation for a specified duration. ```python network = SupplyChainNetwork() network.add_node(supplier_node) network.add_node(factory_node) network.add_node(distributor_node) network.add_links(links) network.simulate(time=5) ``` -------------------------------- ### Example: Invalid Link Registration Type Source: https://github.com/supplychainsimulation/supplynetpy/blob/main/REVIEW.md Illustrates the TypeError raised when attempting to register an object that is not a Link instance with the add_supplier_link method. ```python from supplychainpy import environment, node, link env = environment.Environment() source_node = node.Node(env=env, name='Source') sink_node = node.Node(env=env, name='Sink') # Attempt to add a non-Link object, expecting TypeError try: sink_node.add_supplier_link('not a link') except TypeError as e: print(f"Caught expected error: {e}") ``` -------------------------------- ### Import Libraries for Supply Chain Simulation Source: https://github.com/supplychainsimulation/supplynetpy/blob/main/examples/notebooks/simple_supplier_n_factory.ipynb Imports necessary libraries for simulation, data handling, and plotting. Ensure these are installed before running the simulation. ```python import numpy as np import random import simpy import matplotlib.pyplot as plt import SupplyNetPy.Components as scm ``` -------------------------------- ### Node.place_order() Method Source: https://github.com/supplychainsimulation/supplynetpy/blob/main/REVIEW.md Wraps the process of selecting a supplier and initiating an order. Policies now use this method instead of directly interacting with the selection policy and order processing logic. ```python def place_order(self, quantity) -> None: supplier = self.selection_policy.select(quantity) self.env.process(self.process_order(supplier, quantity)) ``` -------------------------------- ### Create Factory Node and Product Source: https://github.com/supplychainsimulation/supplynetpy/blob/main/docs/example.md Configures a factory node that produces products. It includes settings for product creation and factory inventory management with replenishment thresholds. ```python product_node = { "id": "product", "type": "product", "inventory": { "product": { "capacity": 1000, "min_quantity": 200, "max_quantity": 1000 } } } factory_node = { "id": "factory", "type": "factory", "product": "product", "production": { "product": { "quantity": 100, "time": 1, "cost": 1 } }, "replenishment": { "product": { "s": 300, "S": 1000 } } } ``` -------------------------------- ### Generate Supply Chain Network and Measure Time Source: https://github.com/supplychainsimulation/supplynetpy/blob/main/examples/notebooks/performance_ana.ipynb Generates a supply chain network with a specified number of nodes and measures the time taken for its creation. This function helps in understanding the overhead of network setup. ```python replenishment_policies = [scm.SSReplenishment, scm.RQReplenishment, scm.PeriodicReplenishment] selection_policies = [scm.SelectAvailable, scm.SelectCheapest, scm.SelectFirst, scm.SelectFastest] selection_mode = ["fixed","dynamic"] def generate_supply_chain(n: int, simtime: int, logging: bool = False) -> float: """ Generate a supply chain network with n nodes and measure execution time. Node Ratios (Generalized for Balanced Networks): - Suppliers to Manufacturers: 3–10 suppliers per manufacturer - Manufacturers to Warehouses/Distributors: 1–3 per manufacturer - Warehouses/Distributors to Retailers: 1–10 per warehouse/distributor """ env = simpy.Environment() supplynet = {"env": env, "nodes": {}, "links": {}, "demands": {}} if n < 4: print("Cannot create a network with fewer than 4 nodes!") return # Determine node counts num_suppliers = max(1, n // 5) num_manufacturers = max(1, n // 10) num_distributors = max(1, n // 7) num_retailers = n - num_suppliers - num_manufacturers - num_distributors #print(f"num_suppliers={num_suppliers}, num_manufacturers={num_manufacturers}, num_distributors={num_distributors}, num_retailers={num_retailers}") # Create product and raw material rawmat = scm.RawMaterial(ID="rm1", name="raw_material", extraction_time=1, extraction_quantity=100, mining_cost=0.1, cost=1) product = scm.Product(ID="p1", name="product", manufacturing_cost=1, manufacturing_time=1, raw_materials=[(rawmat, 1)], sell_price=10, batch_size=100) # Helper function for generating random attributes def rand_attrs(init_range, hold_range, s_range, buy_range=None, sell_range=None): attrs = { "initial_level": random.randint(*init_range), "inventory_holding_cost": random.uniform(*hold_range), "s": random.randint(*s_range) } if buy_range: attrs["buy"] = random.randint(*buy_range) if sell_range: attrs["sell"] = random.randint(*sell_range) return attrs # Create suppliers for i in range(1, num_suppliers + 1): ID = f"S{i}" supplynet["nodes"][ID] = scm.Supplier(env, ID=ID, name=f"Supplier {i}", node_type="infinite_supplier", raw_material=rawmat) max_dem = int(DemandDist().mean*(DemandDist().mean+DemandDist().var)) min_dem = int(DemandDist().mean*(DemandDist().mean-DemandDist().var)) dmax_dem = (num_retailers//num_distributors)*max_dem dmin_dem = (num_retailers//num_distributors)*min_dem mmax_dem = (num_distributors//num_manufacturers)*dmax_dem mmin_dem = (num_distributors//num_manufacturers)*dmin_dem #print(f"max_dem={max_dem}, min_dem={min_dem}, dmax_dem={dmax_dem}, dmin_dem={dmin_dem}, mmax_dem={mmax_dem}, mmin_dem={mmin_dem}") # Create manufacturers for i in range(1, num_manufacturers + 1): ID = f"M{i}" attrs = rand_attrs((mmin_dem, mmax_dem), (0.2, 0.5), (mmin_dem, mmax_dem), sell_range=(product.sell_price, product.sell_price+1)) supplynet["nodes"][ID] = scm.Manufacturer(env, ID=ID, name=f"Manufacturer {i}", capacity=mmax_dem, initial_level=attrs["initial_level"], ``` -------------------------------- ### Build Network with Classmethod Constructor Source: https://context7.com/supplychainsimulation/supplynetpy/llms.txt Constructs a supply network using the Network.build classmethod, defining nodes, links, and demands. The network is then simulated. ```python import SupplyNetPy.Components as scm net = scm.Network.build( nodes=[ {'ID': 'S1', 'name': 'Supplier', 'node_type': 'infinite_supplier'}, {'ID': 'D1', 'name': 'Distributor', 'node_type': 'distributor', 'capacity': 200, 'initial_level': 80, 'inventory_holding_cost': 0.1, 'replenishment_policy': scm.RQReplenishment, 'policy_param': {'R': 50, 'Q': 100}, 'product_buy_price': 50, 'product_sell_price': 60}, ], links=[{'ID': 'L1', 'source': 'S1', 'sink': 'D1', 'cost': 3, 'lead_time': lambda: 1}], demands=[{'ID': 'd1', 'name': 'Demand', 'order_arrival_model': lambda: 2, 'order_quantity_model': lambda: 15, 'demand_node': 'D1'}] ) net.simulate(sim_time=100, logging=False) # Typed accessors print(net.node_count) # 2 print(net.link_count) # 1 print(net.node('D1').stats.inventory_level) # Post-run KPIs via .results r = net.results print(r['profit'], r['shortage'], r['inventory_carry_cost']) ``` -------------------------------- ### Run Simulation with Replenishment and Demand Source: https://github.com/supplychainsimulation/supplynetpy/blob/main/examples/notebooks/different_replenish_policies_anylogistix.ipynb This snippet sets up and runs a discrete-event simulation. It defines replenishment and demand processes and executes the simulation until a specified time. Ensure 'env', 'inventory', 'replenish', and 'demand' are properly initialized before running. ```python print(f"{env.now}:A: Shipment received. Inventory replenished to {inventory.level}") else: yield inv_drop replenish_event = env.process(replenish(env, inventory)) demand_event = env.process(demand(env, inventory)) env.run(until=10) print(f"Inventory lvl: {inventory.level}") ``` -------------------------------- ### Fix Inventory Expiry Event Handling in SupplyNetPy Source: https://github.com/supplychainsimulation/supplynetpy/blob/main/REVIEW.md This fix ensures that the SimPy 'get' event is yielded when removing expired items, maintaining consistency between container levels and on-hand counts. It resolves issues with inaccurate holding cost calculations. ```python if qty > 0: get_event, _ = self.get(qty) yield get_event ``` -------------------------------- ### Initialize Supplier Node with Direct API Source: https://context7.com/supplychainsimulation/supplynetpy/llms.txt Initializes an infinite supplier node using the direct API with a simpy environment. Supports disruption modeling parameters like failure probability and recovery time. ```python import simpy import SupplyNetPy.Components as scm env = simpy.Environment() scm.set_seed(42) # reproducible disruptions # Infinite supplier (unlimited stock) sup = scm.Supplier(env=env, ID='S1', name='Supplier', node_type='infinite_supplier') ``` -------------------------------- ### Import SupplyNetPy and SimPy Source: https://github.com/supplychainsimulation/supplynetpy/blob/main/validation/README.md Import the SupplyNetPy library as 'scm' and the simpy library for simulation. ```python import SupplyNetPy as scm import simpy ``` -------------------------------- ### Updating pyproject.toml Dependencies and Python Version Source: https://github.com/supplychainsimulation/supplynetpy/blob/main/REVIEW.md Updates pyproject.toml to require Python >=3.9, drops support for Python 3.8, adds support for Python 3.12, and explicitly lists networkx and matplotlib as dependencies. This ensures the library installs correctly and avoids reliance on incidentally present packages. ```toml [project] requires-python = ">=3.9" classifiers = [ "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", ] dependencies = [ "networkx", "matplotlib", ] ``` -------------------------------- ### Initialize and Run Simulation for Expected Costs Source: https://github.com/supplychainsimulation/supplynetpy/blob/main/examples/notebooks/case_study_pharma.ipynb Initializes and runs a simulation to calculate expected costs per day for a given range of reorder points. The results are stored in a NumPy array. ```python exp_cost_per_day = np.array([[1300, 7.077822120396403, 15.375530317761504, 0.48621696037098827], [1400, 8.072049149524776, 8.944250976240237, 0.2828420504910372], [1500, 8.177269862608554, 8.759889885683231, 0.27701204091031023],[1600, 7.855598168510199, 8.654408519286562, 0.273676427225108], [1700, 7.877377475166987, 8.572385582297002, 0.27108263421247314],[1800, 8.310096514871837, 8.127115493237646, 0.2570019576587373], [1900, 8.391168946106205, 7.832646240299199, 0.24769002225700004]]) ``` ```python exp_cost_per_day5 = np.array([[100, 25.30862172992034, 15.375530317761504, 0.48621696037098827], [200, 18.16593400246117, 16.986714360773185, 0.5371670734273444], [300, 17.650956809395268, 16.973452926426557, 0.5367477100515831], [400, 15.589053570872546, 16.870657435971335, 0.533497031221257], [500, 15.498871274512553, 15.421650748015102, 0.48767541643367135], [600, 13.898840987447143, 15.368269057506792, 0.48598733916010683], [700, 13.78142778173988, 15.215801669376146, 0.4811658970062092], [800, 13.20616981550624, 15.448069084740641, 0.48851083759413116], [900, 11.804738360182338, 13.481785962414833, 0.4263315056811608]]) ``` -------------------------------- ### Print and Get Node-wise Performance Metrics in SupplyNetPy Source: https://context7.com/supplychainsimulation/supplynetpy/llms.txt Use `print_node_wise_performance` to display KPIs as a text table or `get_node_wise_performance` to retrieve them as structured data for analysis. The code requires importing SupplyNetPy components and simulating a network first. Individual node statistics can also be accessed directly from the node object. ```python import SupplyNetPy.Components as scm # Build and simulate net = scm.create_sc_net( nodes=[ {'ID': 'S1', 'name': 'Supplier', 'node_type': 'infinite_supplier'}, {'ID': 'D1', 'name': 'Distributor', 'node_type': 'distributor', 'capacity': 200, 'initial_level': 100, 'inventory_holding_cost': 0.2, 'replenishment_policy': scm.SSReplenishment, 'policy_param': {'s': 60, 'S': 200}, 'product_buy_price': 10, 'product_sell_price': 14}, ], links=[{'ID': 'L1', 'source': 'S1', 'sink': 'D1', 'cost': 2, 'lead_time': lambda: 1}], demands=[{'ID': 'd1', 'name': 'Demand', 'order_arrival_model': lambda: 1, 'order_quantity_model': lambda: 8, 'demand_node': 'D1'}], ) scm.simulate_sc_net(net, sim_time=50, logging=False) node_list = list(net['nodes'].values()) # Print table to stdout scm.print_node_wise_performance(node_list) # Get structured data for further analysis rows = scm.get_node_wise_performance(node_list) # Each row: {'Performance Metric': 'profit', 'Distributor': 1234.5, ...} import pandas as pd df = pd.DataFrame.from_records(rows) print(df.to_string(index=False)) # Or access a single node's stats directly d1 = net['nodes']['D1'] stats = d1.stats.get_statistics() # stats keys: name, demand_placed, fulfillment_received, demand_received, # demand_fulfilled, shortage, backorder, inventory_level, # inventory_waste, inventory_carry_cost, inventory_spend_cost, # transportation_cost, destroyed_qty, destroyed_value, # node_cost, revenue, profit print(f"D1 profit: {stats['profit']}, shortage: {stats['shortage']}") ``` -------------------------------- ### Build Network with Dictionary Wrapper Source: https://context7.com/supplychainsimulation/supplynetpy/llms.txt Creates a supply network by wrapping an existing dictionary structure using scm.create_sc_net and then initializing a Network object. The legacy dictionary remains in sync. ```python import SupplyNetPy.Components as scm # Style B: wrap existing dict sc = scm.create_sc_net(nodes=[...], links=[...], demands=[...]) net2 = scm.Network(sc) net2.simulate(200) # Legacy dict still in sync: print(net2.as_dict()['profit']) ``` -------------------------------- ### Simulate and Calculate EOQ with SupplyNetPy Source: https://github.com/supplychainsimulation/supplynetpy/blob/main/docs/EOQ_est.md This script simulates inventory management to find the Economic Order Quantity (EOQ). It iterates through different lot sizes, calculates total daily costs (ordering + holding), and identifies the lot size that minimizes these costs. The simulation uses `simpy` for discrete-event simulation and `SupplyNetPy` components for inventory and supplier nodes. It also visualizes the cost per lot size and highlights the calculated EOQ. ```python import simpy import numpy as np import matplotlib.pyplot as plt import SupplyNetPy.Components as scm """ Demand for the Deskpro computer at Best Buy is 1,000 units per month. Best Buy incurs a fixed order placement, transportation, and receiving cost of $4,000 each time an order is placed. Each computer costs Best Buy $500 and the retailer has a holding cost of 20 percent. Evaluate the number of computers that the store manager should order in each replenishment lot. Analysis: In this case, the store manager has the following inputs: - Annual demand, D = 1,000 * 12 = 12,000 units (approx 34 units per day) - Order cost per lot, S = 4,000 - Unit cost per computer, C = 500 - Holding cost per year as a fraction of unit cost, h = 0.2 (500*0.2 = 100 per year => 100/365 = 0.273 per day) Assumptions: - Demand is constant and deterministic - Lead time is zero Optimum Economic Order Quantity (EOQ) is determined to minimize the total cost. Total cost = Annual material cost + Annual ordering cost + Annual holding cost This is same as -> Total cost = total transportation cost + inventory cost (we'll ignore material cost since it is constant) """ D = 12000 # annual demand d = 34 # daily demand order_cost = 4000 # order cost unit_cost = 500 # unit cost holding_cost = 0.273 # holding cost per day lead_time = 0 # lead time simlen = 3650 # simulation length in days total_cost_arr = [] unsat_arr = [] print(f"lot size \t Inv holding cost \t Order cost \t Average cost(per day) \t Unmet demand") for lot_size in range(800,1600,10): order_interval = 365*lot_size/D env = simpy.Environment() hp_supplier = scm.Supplier(env=env, ID="S1", name="HPComputers", node_type="infinite_supplier") bestbuy = scm.InventoryNode(env=env, ID="D1", name="Best Buy", node_type="distributor", capacity=lot_size, initial_level=lot_size, inventory_holding_cost=holding_cost, replenishment_policy=scm.PeriodicReplenishment, product_buy_price=450, policy_param={'T':order_interval,'Q':lot_size}, product_sell_price=unit_cost) link1 = scm.Link(env=env,ID="l1", source=hp_supplier, sink=bestbuy, cost=order_cost, lead_time=lambda: lead_time) demand1 = scm.Demand(env=env,ID="d1", name="demand_d1", order_arrival_model=lambda: 1, order_quantity_model=lambda: d, demand_node=bestbuy, consume_available=True) scm.global_logger.disable_logging() # disable logging for all components env.run(until=simlen) bb_invlevels = np.array(bestbuy.inventory.instantaneous_levels) hp_sup_transportation_cost = bestbuy.stats.transportation_cost total_cost = bestbuy.stats.inventory_carry_cost + bestbuy.stats.transportation_cost total_cost_arr.append([lot_size, total_cost/simlen]) unsat_demand = demand1.stats.demand_placed[1]-demand1.stats.fulfillment_received[1] unsat_arr.append([lot_size,unsat_demand]) print(f"{lot_size} \t\t {bestbuy.stats.inventory_carry_cost:.2f}\t\t{bestbuy.stats.transportation_cost}\t\t{total_cost/simlen:.2f}\t\t{unsat_demand:.2f}") total_cost_arr = np.array(total_cost_arr) unsat_arr = np.array(unsat_arr) EOQ = np.argmin(total_cost_arr[:,1]) plt.figure() plt.plot(total_cost_arr[:,0], total_cost_arr[:,1],marker='.',linestyle='-') plt.plot(total_cost_arr[EOQ,0], total_cost_arr[EOQ,1],marker='o',color='red',label=f'EOQ = {total_cost_arr[EOQ,0]:.2f} with cost = {total_cost_arr[EOQ,1]:.2f}') plt.xlabel("lot size") plt.ylabel("Average cost per day") plt.title("Average cost vs lot size") plt.legend() plt.grid() plt.show() ``` -------------------------------- ### Switching to nx.multipartite_layout for Visualization Source: https://github.com/supplychainsimulation/supplynetpy/blob/main/REVIEW.md Implements nx.multipartite_layout keyed by a new _TIER_INDEX for improved visualization. This arranges nodes left-to-right (suppliers to demand) and ensures arrows render correctly with nx.DiGraph. ```python nx.multipartite_layout ``` -------------------------------- ### Create and Simulate Supply Chain Network Source: https://github.com/supplychainsimulation/supplynetpy/blob/main/docs/ten_min.md Use `create_sc_net` to instantiate and assemble supply chain nodes, adding metadata. Then, use `simulate_sc_net` to run the simulation over a specified period and log performance metrics. ```python from supplychainpy import create_sc_net, simulate_sc_net # Create and simulate the supply chain network supplychainnet = create_sc_net(name='my_supply_chain', json_file='scipy_example.json') log = simulate_sc_net(supplychainnet, 100) ``` -------------------------------- ### Configure Retailer Nodes and Demand in SupplyNet Source: https://github.com/supplychainsimulation/supplynetpy/blob/main/examples/notebooks/performance_ana.ipynb Sets up retailer nodes with capacities and various replenishment policies. Demand is modeled using exponential arrival and uniform quantity distributions. Links are created from distributors to retailers. ```python # Create retailers and demand for i in range(1, num_retailers + 1): ID = f"R{i}" attrs = rand_attrs((min_dem, max_dem), (1, 1.5), (min_dem, max_dem), buy_range=(product.sell_price, product.sell_price+2), sell_range=(product.sell_price+2, product.sell_price+3)) supplynet["nodes"][ID] = scm.InventoryNode(env, ID=ID, name=f"Retailer {i}", node_type="retailer", capacity=max_dem, initial_level=attrs["initial_level"], inventory_holding_cost=attrs["inventory_holding_cost"], replenishment_policy=random.choice(replenishment_policies), policy_param={'s': attrs["s"], 'S': max_dem, 'R':attrs["s"], 'Q': min_dem, 'T': 1}, product_buy_price=attrs["buy"], product_sell_price=attrs["sell"], supplier_selection_policy=scm.SelectAvailable, supplier_selection_mode="dynamic") # Demand demand_id = f"demand_{ID}" supplynet["demands"][demand_id] = scm.Demand(env, ID=demand_id, name=f"Demand {i}", order_arrival_model=DemandDist().exponential, # around 10 orders per day order_quantity_model=DemandDist().uniform, # around 10 units per order demand_node=supplynet["nodes"], ID]) # Links from distributors to retailer for j in range(1, num_distributors + 1): link_id = f"Ld{j}r{i}" supplynet["links"][link_id] = scm.Link(env, ID=link_id, source=supplynet["nodes"]["D{j}"], sink=supplynet["nodes"], ID], cost=random.randint(1, 3), ``` -------------------------------- ### Build Distribution Package Source: https://github.com/supplychainsimulation/supplynetpy/blob/main/CLAUDE.md Use hatchling to build the distribution package for the project. ```bash python -m build ``` -------------------------------- ### Replenishment Before Demand Simulation Source: https://github.com/supplychainsimulation/supplynetpy/blob/main/examples/notebooks/different_replenish_policies_anylogistix.ipynb This simulation demonstrates a scenario where replenishment orders are placed before demand arrives. Observe how inventory is replenished before demand is processed, ensuring demand fulfillment. ```text Process A: Replenishment is created. Process B: Demand is created. 0:B: Demand for 1 0:B: Fulfilled, Inventory: 0.0 0:A: Ordering replenishment for 1 units 2.0:B: Demand for 1 2.0:B: not fulfilled at, Inventory: 0.0 2.0:A: Shipment received. Inventory replenished to 1.0 4.0:B: Demand for 1 4.0:B: Fulfilled, Inventory: 0.0 4.0:A: Ordering replenishment for 1 units 6.0:B: Demand for 1 6.0:B: not fulfilled at, Inventory: 0.0 6.0:A: Shipment received. Inventory replenished to 1.0 8.0:B: Demand for 1 8.0:B: Fulfilled, Inventory: 0.0 8.0:A: Ordering replenishment for 1 units Inventory lvl: 0.0 ```