在模拟时更改模拟过程中的资源数量

发布于 2025-02-03 00:07:18 字数 262 浏览 3 评论 0原文

我目前正在制作一个模型,该模型在整个工作日中模拟了一个simpy的交付过程。交货是由一次交付一个包裹的车手进行的。包装需要在特定时间窗口中交付。此外,该系统全天都涉及波动的需求。因此,我想调整系统可用的员工数量,以每小时匹配波动的需求。 我将骑手建模为具有一定能力的资源。是否有可能在模拟运行期间调整资源的能力,或者还有其他方法可以用我的系统对骑手进行建模?

我已经在小型文档,示例或其他帖子中寻找了可能的解决方案。但是,我还没有成功。因此,对于任何建议或可能的解决方案,我将非常感谢!先感谢您。

I am currently working on a model which simulates a delivery process throughout an operating day in simpy. The deliveries are conducted by riders which are delivering one package at a time. The packages need to be delivered within a certain time window. In addition, the system deals with fluctuating demand throughout the day. Therefore, I would like to adapt the number of employees available to the system matching the fluctuating demand on an hourly basis.
I modelled the riders as a resource with a certain capacity. Is there any possibility to adjust the capacity of the resource during a simulation run or are there other ways to model the riders with my system?

I already looked for possible solution within the simpy documentation, examples or other posts. However, I was not successful yet. Thus, for any advice or possible solutions I would be very grateful! Thank you in advance.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

北陌 2025-02-10 00:07:18

使用商店代替资源。资源具有修复数量的资源。商店的工作更像是带有最大容量的列表的队列后备。要减少商店中的数字,只需停止将对象放回商店。

因此,我将一家商店包裹了一个班级来管理骑手人数。

"""
    Simple demo of a pool of riders delivering packages where the 
    number of riders can change over time

    Idel riders are keep in a store that is wrapped in a class
    to manage the number of riders

    Programmer Michael R. Gibbs
"""

import simpy
import random

class Rider():
    """
        quick class to track the riders that deliver packages
    """

    # tracks the next id to be assigned to a rider
    next_id = 1

    def __init__(self):

        self.id = Rider.next_id
        Rider.next_id += 1

class Pack():
    """
        quick class to track the packages
    """

    # tracks the next id to be assigned to a pack
    next_id = 1

    def __init__(self):

        self.id = Pack.next_id
        Pack.next_id += 1

class RiderPool():
    """
        Pool of riders where the number of riders can be changed
    """

    def __init__(self, env, start_riders=10):

        self.env = env

        # tracks the number of riders we need
        self.target_cnt = start_riders

        # tracks the number of riders we have
        self.curr_cnt = start_riders

        # the store idle riders
        self.riders = simpy.Store(env)

        # stores do not start with objects like resource pools do.
        # need to add riders yourself as part of set up
        self.riders.items = [Rider() for _ in range(start_riders)]
 

    def add_rider(self):
        """
            Add a rider to the pool
        """

        self.target_cnt += 1


        if self.curr_cnt < self.target_cnt:
            # need to add a rider to the pool to get to the target
            rider = Rider()
            self.riders.put(rider)
            self.curr_cnt += 1
            print(f'{env.now:0.2f} rider {rider.id} added')

        else:
            # already have enough riders,
            # must have tried to reduce the rider pool while all riders were busy
            # In effect we are cancelling a previous remove rider call
            print(f'{env.now:0.2f} keeping rider scheduled to be removed instead of adding')

    def remove_rider(self):
        """
            Remove a rider from the pool

            If all the riders are busy, the actual removal of a rider
            will happen when a that rider finishes it current task and is
            tried to be put/returned back into the pool
        """

        self.target_cnt -= 1

        if self.curr_cnt > self.target_cnt:
            if len(self.riders.items) > 0:
                # we have a idle rider that we can remove now

                rider = yield self.riders.get()
                self.curr_cnt -= 1
                print(f'{env.now:0.2f} rider {rider.id} removed from store')

            else:
                # wait for a rider to be put back to the pool
                pass
        

    def get(self):
        """
            Get a rider from the pool

            returns a get request that can be yield to, not a rider
        """

        rider_req = self.riders.get()

        return rider_req

    def put(self, rider):
        """
            put a rider pack into the pool
        """

        if self.curr_cnt <= self.target_cnt:
            # still need the rider
            self.riders.put(rider)
        else:
            # have tool many riders, do not add back to pool
            self.curr_cnt -= 1
            print(f'{env.now:0.2f} rider {rider.id} removed on return to pool')

def gen_packs(env, riders):
    """
        generates the arrival of packages to be delivered by riders
    """

    while True:

        yield env.timeout(random.randint(1,4))
        pack = Pack()

        env.process(ship_pack(env, pack, riders))

def ship_pack(env, pack, riders):
    """
        The process of a rider delivering a packages
    """

    print(f'{env.now:0.2f} pack {pack.id} getting rider')

    rider = yield riders.get()

    print(f'{env.now:0.2f} pack {pack.id} has rider {rider.id}')

    # trip time
    yield env.timeout(random.randint(5,22))

    riders.put(rider)

    print(f'{env.now:0.2f} pack {pack.id} delivered')

def rider_sched(env, riders):
    """
        Changes the number of riders in rider pool over time
    """


    yield env.timeout(30)
    # time to remove a few riders

    print(f'{env.now:0.2f} -- reducing riders')
    print(f'{env.now:0.2f} -- request queue len {len(riders.riders.get_queue)}')
    print(f'{env.now:0.2f} -- rider store len {len(riders.riders.items)}')

    for _ in range(5):
        env.process(riders.remove_rider())

    yield env.timeout(60)
    # time to add back some riders

    print(f'{env.now:0.2f} -- adding riders ')
    for _ in range(2):
        riders.add_rider()


# run the model
env = simpy.Environment()
riders = RiderPool(env, 10)

env.process(gen_packs(env, riders))
env.process(rider_sched(env, riders))

env.run(100)

print(f'{env.now:0.2f} -- end rider count {riders.target_cnt}')

Use a store instead of a resource. Resources has a fix number of resources. Stores works a bit like a bit more like a queue backed with a list with a optional max capacity. To reduce then number in the store, just stop putting the object back into the store.

So is a example I wrapped a store with a class to manage the number of riders.

"""
    Simple demo of a pool of riders delivering packages where the 
    number of riders can change over time

    Idel riders are keep in a store that is wrapped in a class
    to manage the number of riders

    Programmer Michael R. Gibbs
"""

import simpy
import random

class Rider():
    """
        quick class to track the riders that deliver packages
    """

    # tracks the next id to be assigned to a rider
    next_id = 1

    def __init__(self):

        self.id = Rider.next_id
        Rider.next_id += 1

class Pack():
    """
        quick class to track the packages
    """

    # tracks the next id to be assigned to a pack
    next_id = 1

    def __init__(self):

        self.id = Pack.next_id
        Pack.next_id += 1

class RiderPool():
    """
        Pool of riders where the number of riders can be changed
    """

    def __init__(self, env, start_riders=10):

        self.env = env

        # tracks the number of riders we need
        self.target_cnt = start_riders

        # tracks the number of riders we have
        self.curr_cnt = start_riders

        # the store idle riders
        self.riders = simpy.Store(env)

        # stores do not start with objects like resource pools do.
        # need to add riders yourself as part of set up
        self.riders.items = [Rider() for _ in range(start_riders)]
 

    def add_rider(self):
        """
            Add a rider to the pool
        """

        self.target_cnt += 1


        if self.curr_cnt < self.target_cnt:
            # need to add a rider to the pool to get to the target
            rider = Rider()
            self.riders.put(rider)
            self.curr_cnt += 1
            print(f'{env.now:0.2f} rider {rider.id} added')

        else:
            # already have enough riders,
            # must have tried to reduce the rider pool while all riders were busy
            # In effect we are cancelling a previous remove rider call
            print(f'{env.now:0.2f} keeping rider scheduled to be removed instead of adding')

    def remove_rider(self):
        """
            Remove a rider from the pool

            If all the riders are busy, the actual removal of a rider
            will happen when a that rider finishes it current task and is
            tried to be put/returned back into the pool
        """

        self.target_cnt -= 1

        if self.curr_cnt > self.target_cnt:
            if len(self.riders.items) > 0:
                # we have a idle rider that we can remove now

                rider = yield self.riders.get()
                self.curr_cnt -= 1
                print(f'{env.now:0.2f} rider {rider.id} removed from store')

            else:
                # wait for a rider to be put back to the pool
                pass
        

    def get(self):
        """
            Get a rider from the pool

            returns a get request that can be yield to, not a rider
        """

        rider_req = self.riders.get()

        return rider_req

    def put(self, rider):
        """
            put a rider pack into the pool
        """

        if self.curr_cnt <= self.target_cnt:
            # still need the rider
            self.riders.put(rider)
        else:
            # have tool many riders, do not add back to pool
            self.curr_cnt -= 1
            print(f'{env.now:0.2f} rider {rider.id} removed on return to pool')

def gen_packs(env, riders):
    """
        generates the arrival of packages to be delivered by riders
    """

    while True:

        yield env.timeout(random.randint(1,4))
        pack = Pack()

        env.process(ship_pack(env, pack, riders))

def ship_pack(env, pack, riders):
    """
        The process of a rider delivering a packages
    """

    print(f'{env.now:0.2f} pack {pack.id} getting rider')

    rider = yield riders.get()

    print(f'{env.now:0.2f} pack {pack.id} has rider {rider.id}')

    # trip time
    yield env.timeout(random.randint(5,22))

    riders.put(rider)

    print(f'{env.now:0.2f} pack {pack.id} delivered')

def rider_sched(env, riders):
    """
        Changes the number of riders in rider pool over time
    """


    yield env.timeout(30)
    # time to remove a few riders

    print(f'{env.now:0.2f} -- reducing riders')
    print(f'{env.now:0.2f} -- request queue len {len(riders.riders.get_queue)}')
    print(f'{env.now:0.2f} -- rider store len {len(riders.riders.items)}')

    for _ in range(5):
        env.process(riders.remove_rider())

    yield env.timeout(60)
    # time to add back some riders

    print(f'{env.now:0.2f} -- adding riders ')
    for _ in range(2):
        riders.add_rider()


# run the model
env = simpy.Environment()
riders = RiderPool(env, 10)

env.process(gen_packs(env, riders))
env.process(rider_sched(env, riders))

env.run(100)

print(f'{env.now:0.2f} -- end rider count {riders.target_cnt}')
花开雨落又逢春i 2025-02-10 00:07:18

我喜欢提供的答案,但是有一些(例如我自己)喜欢或多或少地使用这些资源。结果,我写了一个simpy.resource的子类,该类只是在持续时间内消耗资源。我认为这更适合建模一个生病的人或类似的东西……好像不再存在该人,而是仅仅是“更高的优先”任务。这种方法还可以使监视易于插入(在方法中)开始计算病假时间,转移的次数等。取决于模型的上下文。将原因参数放入redaim_capacity方法的目的是为了监视不同类别的不可用类别,甚至可能很有用。

对于PriorityResourceprementiveresource是编写的或必须更改为使用Store

from simpy import Resource

class CustomResource(Resource):
    def reduce_capacity(duration: float | int, qty: int = 1):
        """
        Temporarily reduce the capacity of `qty` resources for `duration`
        """
        def reduce(d):
            with self.request() as req:
                yield req
                yield self._env.timeout(d)

        for _ in range(qty):
            self._env.process(reduce_capacity(duration))

    

I like the answer provided, but there are some - such as myself - that like to use the resources more or less as they are written. As a result, I wrote a subclass of simpy.Resource which simply consumes the resource for a duration. I think that this is more appropriate to model a person who is out sick or something along those lines... it isn't as if the person no longer exists, but is simply on a "higher priority" task. This approach also makes monitoring easier to insert as you can - within the method - start counting sick time, number of times diverted, etc. depending on the context of the model. It may even be useful to put a reason parameter into the reduce_capacity method for the purposes of monitoring different categories of unavailability.

One would have to change the method somewhat for PriorityResource and PreemptiveResource, but the basic methodology works well without changing the basic functionality of the Resource as it is written or having to change to using the Store.

from simpy import Resource

class CustomResource(Resource):
    def reduce_capacity(duration: float | int, qty: int = 1):
        """
        Temporarily reduce the capacity of `qty` resources for `duration`
        """
        def reduce(d):
            with self.request() as req:
                yield req
                yield self._env.timeout(d)

        for _ in range(qty):
            self._env.process(reduce_capacity(duration))

    
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文