APS: frepple 开源解读

https://github.com/frePPLe/frepple/tree/master

class OperatorDelete:
    def __init__(self):
        # Initialize the class

    @classmethod
    def initialize(cls):
        # Initialize the metadata and Python class

    @classmethod
    def create(cls, args, kwds):
        try:
            # Create the solver
            s = OperatorDelete()

            # Iterate over extra keywords and set attributes

            return s
        except Exception as e:
            # Handle exceptions
            pass

    @classmethod
    def solve(cls, v):
        # Implement the solve method
        try:
            # Free Python interpreter for other threads
            Py_BEGIN_ALLOW_THREADS

            # Perform the solving logic
            if v is None:
                # Delete all excess
                self.solve()
            elif isinstance(v, Demand):
                # Delete upstream of a single demand
                self.solve_demand(v)
            elif isinstance(v, Buffer):
                # Delete upstream of a single buffer
                self.solve_buffer(v)
            elif isinstance(v, Resource):
                # Delete upstream of a single resource
                self.solve_resource(v)
            elif isinstance(v, OperationPlan):
                # Delete an operation plan
                self.solve_operation_plan(v)

        except Exception as e:
            # Handle exceptions
            pass
        finally:
            # Reclaim Python interpreter
            Py_END_ALLOW_THREADS

    @classmethod
    def solve_operation_plan(cls, o, v):
        # Implement solve for operation plan
        if not o:
            return  # Null argument passed

        # Mark all buffers.
        # The batching solver doesn't like that we push both consumers and producers,
        # but ideally we would pass true for both arguments.
        self.push_buffers(o, True, False)

        # Delete the operation plan
        if o.get_proposed():
            if self.cmds:
                self.cmds.add(CommandDeleteOperationPlan(o))
            else:
                del o

        # Propagate to all upstream buffers
        while buffersToScan:
            curbuf = buffersToScan.pop()
            self.solve_buffer(curbuf)

    @classmethod
    def solve_resource(cls, r, v):
        # Implement solve for a resource
        if self.get_log_level() > 0:
            print(f"Scanning {r} for excess")

        # Loop over all operation plans on the resource
        for op_plan in r.get_load_plans():
            if op_plan.get_event_type() == 1:
                # Add all buffers into which material is produced to the stack
                self.push_buffers(op_plan, False, True)

        # Process all buffers found, and their upstream colleagues
        while buffersToScan:
            cur_buf = buffersToScan.pop()
            self.solve_buffer(cur_buf)

    @classmethod
    def solve_demand(cls, d, v):
        # Implement solve for a demand
        if self.get_log_level() > 1:
            print(f"Scanning {d} for excess")

        # Delete all delivery operation plans.
        while True:
            # Find a candidate operation plan to delete
            candidate = None
            delivery_plans = d.get_delivery()

            for op_plan in delivery_plans:
                if op_plan.get_proposed():
                    candidate = op_plan
                    break

            if not candidate:
                break

            # Push the buffer on the stack in which the deletion creates excess inventory
            self.push_buffers(candidate, True, False)

            # Delete only the delivery, immediately or through a delete command
            if self.cmds:
                self.cmds.add(CommandDeleteOperationPlan(candidate))
            else:
                del candidate

    @classmethod
    def push_buffers(cls, o, consuming, producing):
        # Implement pushing buffers
        # Loop over all flow plans
        for flow_plan in o.get_flow_plans():
            # Skip flow plans we're not interested in
            if not (
                (consuming and flow_plan.get_quantity() < 0)
                or (producing and flow_plan.get_quantity() > 0)
            ):
                continue

            # Check if the buffer is already found on the stack
            found = False
            for buf in buffersToScan:
                if buf == flow_plan.get_buffer():
                    found = True
                    break

            # Add the buffer to the stack if not found
            if not found:
                buffersToScan.append(flow_plan.get_buffer())

        # Recursive call for all sub-operation plans
        for sub_op_plan in o:
            self.push_buffers(sub_op_plan, consuming, producing)

    @classmethod
    def solve_buffer(cls, b, v):
        # Implement solve for a buffer
        if self.get_log_level() > 1:
            print(f"Scanning buffer {b}")

        # Get the list of flow plans for the buffer
        flow_plans = b.get_flow_plans()
        
        fiter = iter(flow_plans)
        fend = None  # Replace with the actual end condition for the flow plans

        if fiter is fend:
            return  # No flow plans in the buffer

        excess = fiter.get_onhand() - fiter.get_min()

        if excess > ROUNDING_ERROR:
            fiter = iter(flow_plans)
            while excess > ROUNDING_ERROR and fiter is not fend:
                if fiter.get_quantity() <= 0:
                    # Not a producer
                    fiter = iter(fiter)
                    continue

                fp = None
                if fiter.get_event_type() == 1:
                    fp = fiter

                if not fp or not fp.get_operation_plan().get_proposed() or \
                        fp.get_operation_plan().get_demand() or \
                        (fp.get_operation_plan().get_owner() and \
                        fp.get_operation_plan().get_owner().get_demand()) or \
                        fp.get_flow().has_type(FlowTransferBatch):
                    # It's locked or a delivery operation plan
                    fiter = iter(fiter)
                    continue

                cur_excess = b.get_excess(iter(fiter))
                if fp:
                    for flow_plan in fp.get_operation_plan().get_flow_plans():
                        if flow_plan.get_quantity() < ROUNDING_ERROR or \
                                flow_plan.get_buffer() == b or \
                                not flow_plan.get_flow().get_quantity():
                            continue
                        my_excess = (b.get_excess(flow_plan) - \
                                flow_plan.get_flow().get_quantity_fixed()) * \
                                fp.get_flow().get_quantity() / \
                                flow_plan.get_flow().get_quantity()
                        if my_excess >= 0.0 and my_excess < cur_excess:
                            cur_excess = my_excess

                if cur_excess < ROUNDING_ERROR:
                    fiter = iter(fiter)
                    continue

                while fiter is not fend and fiter.get_event_type() == 1 and \
                        fiter.get_operation_plan().get_top_owner() == \
                        fp.get_operation_plan().get_top_owner():
                    fiter = iter(fiter)

                new_size_opplan = None  # Calculate the new size as needed
                new_size_flowplan = None  # Calculate the new size as needed

                if cur_excess < fp.get_flow().get_quantity_fixed() + \
                        fp.get_operation().get_size_multiple() * \
                        fp.get_flow().get_quantity():
                    # This excess is unavoidable
                    fiter = iter(fiter)
                    continue
                elif cur_excess >= fiter.get_quantity() - ROUNDING_ERROR:
                    # Completely delete the producer
                    new_size_opplan = 0.0
                    new_size_flowplan = 0.0
                else:
                    # Resize the producer
                    # We need to keep the operation plan start date constant
                    # during the resize
                    # Calculate new sizes and apply them

                if new_size_flowplan < ROUNDING_ERROR:
                    # The complete operation plan is excess
                    excess -= fiter.get_quantity()
                    self.push_buffers(fp.get_operation_plan(), True, False)

                    if self.cmds:
                        self.cmds.add(CommandDeleteOperationPlan(fp.get_operation_plan()))
                    else:
                        del fp.get_operation_plan()
                else:
                    # Reduce the operation plan
                    # Add upstream buffers to the stack
                    self.push_buffers(fp.get_operation_plan(), True, False)
                    excess -= fiter.get_quantity() - new_size_flowplan

                    # Resize operation plan if needed
                    if self.cmds:
                        # TODO: Adjust the command or operation plan resizing logic
                        self.cmds.add(CommandMoveOperationPlan(
                            fp.get_operation_plan(), Date.infinite_past,
                            fp.get_operation_plan().get_end(), new_size_opplan))
                    else:
                        # Set the new size for the operation plan
                        fp.get_operation_plan().set_quantity(new_size_opplan)

                fiter = iter(fiter)

    @staticmethod
    def solve_python(self, args):
        # Implement the solve method with Python-specific code
  • 关键在于理解buffer的含义以及操作。
  • 有consumer和producing, 数量有正负

Identify Critical Paths: Start by identifying the critical paths in your scheduling process. Critical paths are sequences of tasks that have the least flexibility in terms of start times. These paths are essential to meeting project deadlines or other constraints.

Determine Buffer Locations: Based on your critical paths, identify locations in your schedule where buffers can be introduced. These locations are typically at the end of a critical path or just before a critical constraint.

Size the Buffer: The buffer's size depends on factors such as variability in task durations, uncertainty in resource availability, and the desired level of risk mitigation. It's often calculated based on statistical analysis, like the Critical Chain Project Management (CCPM) method or Monte Carlo simulations.

Set Buffer Policies: Define policies that determine when and how the buffer is consumed. Common buffer policies include:

Start Buffer: This is added at the start of the project. It ensures that the project starts on time.

Resource Buffer: This buffer is used to manage resource constraints. It ensures that critical resources are available when needed.

Feeding Buffer: Placed before critical constraints or dependent tasks, this buffer ensures that inputs are available as needed.

Monitoring and Control: Continuously monitor the progress of your project. If tasks start to encroach on the buffer, take action to bring the project back on track. This might involve reallocating resources, addressing bottlenecks, or resequencing tasks.

Buffer Management: The focus should be on protecting the project buffer. If it starts to be consumed, assess why this is happening and take corrective action.

Project Review: After the project is completed, conduct a review to understand why the buffer was consumed. This can help in making improvements for future projects.

Flexibility: Keep in mind that the buffer provides flexibility, but it's important to strike a balance. Having too much buffer may result in inefficient resource allocation, while too little buffer might not provide enough protection.

In the context of backward scheduling, the buffer helps to ensure that scheduled tasks can start as soon as possible while accounting for constraints and uncertainties. It's essentially a cushion that provides protection against delays without compromising the overall project timeline. The goal is to optimize the use of resources and time, minimize disruptions, and ensure that the project is completed on time.

相关推荐
湫ccc8 分钟前
《Python基础》之pip换国内镜像源
开发语言·python·pip
fhvyxyci9 分钟前
【C++之STL】摸清 string 的模拟实现(下)
开发语言·c++·string
hakesashou9 分钟前
Python中常用的函数介绍
java·网络·python
qq_4597300311 分钟前
C 语言面向对象
c语言·开发语言
菜鸟的人工智能之路19 分钟前
极坐标气泡图:医学数据分析的可视化新视角
python·数据分析·健康医疗
菜鸟学Python20 分钟前
Python 数据分析核心库大全!
开发语言·python·数据挖掘·数据分析
小白不太白95022 分钟前
设计模式之 责任链模式
python·设计模式·责任链模式
喜欢猪猪27 分钟前
Django:从入门到精通
后端·python·django
一个小坑货27 分钟前
Cargo Rust 的包管理器
开发语言·后端·rust
bluebonnet2732 分钟前
【Rust练习】22.HashMap
开发语言·后端·rust