From 7c210b04796bf5e58a53841748df39e72caae30e Mon Sep 17 00:00:00 2001 From: SongQi Date: Mon, 24 Jul 2023 15:07:19 +0800 Subject: [PATCH] update --- .idea/.gitignore | 3 + .idea/.name | 1 + .idea/HTSim.iml | 8 + .../inspectionProfiles/profiles_settings.xml | 6 + .idea/misc.xml | 4 + .idea/modules.xml | 8 + .idea/vcs.xml | 6 + ABMfim流程00.txt | 42 ++++ Environment.py | 165 ++++++++++++++ Firm.py | 37 ++++ InventorySystem.py | 128 +++++++++++ Order.py | 36 +++ OrderSystem.py | 103 +++++++++ ProduceSystem.py | 60 +++++ bom23.csv | 208 ++++++++++++++++++ bom23.xlsx | Bin 0 -> 17116 bytes demand23.xlsx | Bin 0 -> 9870 bytes fake_api.py | 20 ++ plan.xlsx | Bin 0 -> 11596 bytes rawmaterial.xlsx | Bin 0 -> 10695 bytes 20 files changed, 835 insertions(+) create mode 100644 .idea/.gitignore create mode 100644 .idea/.name create mode 100644 .idea/HTSim.iml create mode 100644 .idea/inspectionProfiles/profiles_settings.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/vcs.xml create mode 100644 ABMfim流程00.txt create mode 100644 Environment.py create mode 100644 Firm.py create mode 100644 InventorySystem.py create mode 100644 Order.py create mode 100644 OrderSystem.py create mode 100644 ProduceSystem.py create mode 100644 bom23.csv create mode 100644 bom23.xlsx create mode 100644 demand23.xlsx create mode 100644 fake_api.py create mode 100644 plan.xlsx create mode 100644 rawmaterial.xlsx diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..26d3352 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/.idea/.name b/.idea/.name new file mode 100644 index 0000000..fed8c48 --- /dev/null +++ b/.idea/.name @@ -0,0 +1 @@ +OrderSystem.py \ No newline at end of file diff --git a/.idea/HTSim.iml b/.idea/HTSim.iml new file mode 100644 index 0000000..d870a4a --- /dev/null +++ b/.idea/HTSim.iml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 0000000..105ce2d --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..ab530bf --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..6b96659 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/ABMfim流程00.txt b/ABMfim流程00.txt new file mode 100644 index 0000000..4aa49c2 --- /dev/null +++ b/ABMfim流程00.txt @@ -0,0 +1,42 @@ +!按子系统功能! + +Oss: +接收订单。 +计算到当期为止,未交付订单的各类产品与其库存之差,得到gap最大的产品,并将能获得该商品最大生产率的生产状态通知Iss和Pss。 + +交付当期到期和已经出现延期的订单,先进先出。 + +计算(env)延期平均时间:延期时间 * 延期产品数/10000 + +Iss: +按照Oss指定的生产状态,准备原材料并交付Pss(如果原材料不充足,就将能交付的先交付) + +每期末统计原材料使用情况,按照遗传算法提供的Sspolicy,选择是否订货(初始原材料备货为s),如果订货要随机生成该原材料的交货期。 + +每期末要更新原材料剩余情况(上一次余值+当期到货量-当期使用量) + +更新产成品期末库存。 + + +Pss: +根据Oss的指示,开始生产,如果原材料不充足,就按照gap大小进行生产;如果没有gap的产品了,就按照库存水平由少到多进行生产。 +(此处如果出现原材料余料,回交Iss) +生成最终生产结果,产成品交付Iss。 + + +!按流程 ! + +Oss:接收订单。计算到当期为止,未交付订单的各类产品与其库存之差,得到gap最大的产品,并将能获得该商品最大生产率的生产状态通知Iss和Pss。 + +Iss:按照Oss指定的生产状态,准备原材料并交付Pss(如果原材料不充足,就将能交付的先交付)。 + +Pss:根据Oss的指示,开始生产,如果原材料不充足,就按照gap大小进行生产;如果没有gap的产品了,就按照库存水平由少到多进行生产。 +(此处如果出现原材料余料,回交Iss) +生成最终生产结果,产成品交付Iss。 + +Iss:统计原材料使用情况,按照遗传算法提供的Sspolicy,选择是否订货(初始原材料备货为s),如果订货要随机生成该原材料的交货期。 +每期末要更新原材料剩余情况(上一次余值+当期到货量-当期使用量)。 +更新产成品期末库存。 + +Oss:交付当期到期和已经出现延期的订单,先进先出。计算(env)延期平均时间:延期时间 * 延期产品数/10000 + diff --git a/Environment.py b/Environment.py new file mode 100644 index 0000000..0ca7f73 --- /dev/null +++ b/Environment.py @@ -0,0 +1,165 @@ +import random +import numpy as np +import pandas as pd +import agentpy as ap +from datetime import datetime +from numpy import random +import json + +from Firm import Firm +# import passive agents +from Order import Order + +from fake_api import get_plan_by_pp_id, get_bom_by_prd_id + + +class FMSEnv(ap.Model): + # put all parameters here, not in any other places + # xv_int_max_order: int + # ev_n_order_created: int + + the_firm: Firm + + # record data, define below + # op_os_n_total_order: int + # op_os_n_total_order_delayed: int + op_os_all_delay_time: list + # op_os_delay_ratio: float + # op_is_flt_material_room_left: float + # op_is_flt_product_room_left: float + + op_ps_str_status: str + op_os_to_dlv: np.ndarray + op_is_current_product: np.ndarray + op_is_current_material: np.ndarray + op_is_trans_material: np.ndarray + op_ps_back_trans_material: np.ndarray + op_ps_produced_num: np.ndarray + op_is_ip_mat_id: np.ndarray + op_ip_prd_s: np.ndarray + op_ip_prd_big_s: np.ndarray + op_ip_prd_est_pfm: int + + def __init__(self, dct_all_para, _run_id=None): + super().__init__() + + # create agents here + self.the_firm = Firm(env=self, dct_all_para=dct_all_para) + + # get the input parameter values from the dictionary + self.int_stop_time = int(dct_all_para['time']) + # self.xv_int_max_order = int(dct_all_para['xv_int_max_order']) + self.xv_dlv_product_para = np.asarray(dct_all_para['xv_dlv_product_para']) + # self.xv_int_dlv_period_lam = int(dct_all_para['xv_int_dlv_period_lam']) + # self.ev_n_order_created = 0 + + self.running = True + self.t = 0 + + # Creation of orders should be done in the environment + def create_order(self): + # Check if maximum number of orders has been reached + xv_int_create_order_num = 1 + # xv_int_create_order_num = random.poisson(lam=xv_int_create_order_lam, size=None) + # if self.ev_n_order_created < xv_int_max_order: + for i in range(xv_int_create_order_num): + new_order = Order(model=self, time_created=self.t, xv_dlv_product_para=self.xv_dlv_product_para, + leadtime=max(self.the_firm.xv_ary_lead_time)) + return new_order + return None + + # Execute the interactions of each time step in the simulation. + def step(self): + # organize the interactions of agents in each time step here + new_order = self.create_order() + self.the_firm.the_os.accept_order(new_order) + + self.the_firm.operating() + self.update() + + if self.t >= self.int_stop_time: + self.running = False + self.stop() + else: + print(f"running the {self.t} step") + + # Record data after each simulation + def update(self): + self.op_os_n_total_order = len(self.the_firm.the_os.a_lst_order) + # self.op_os_n_total_order_delayed = len([e for e in self.the_firm.the_os.a_lst_order if e.xv_dlv_t < self.t]) + self.op_os_to_dlv = self.the_firm.the_os.ev_ary_to_dlv + # self.op_os_all_delay_time = self.the_firm.the_os.ev_lst_all_delay_time + + self.op_ps_produced_num = self.the_firm.the_ps.ev_ary_produced_num + self.op_ps_str_status = self.the_firm.the_ps.ev_str_status + + # self.op_is_current_product = self.model.the_firm.the_is.ev_ary_current_product + # + # self.op_is_current_material = self.model.the_firm.the_is.ev_ary_current_material + # + # self.op_is_trans_material= self.model.the_firm.the_is.ev_lst_trans_material + + self.op_ps_back_trans_material = self.model.the_firm.the_ps.ev_lst_backtrans_material + + self.record([att for att in self.__dict__.keys() if att.startswith('op_')]) + pass + + +if __name__ == '__main__': + dct_para = { + 'time': 60, + # 'xv_int_max_order': random.randint(30, 50), + # 'xv_dlv_product_para': tuple([(30, 100), (30, 50)]), + # 'xv_dlv_product_para': tuple([30,40,30,20]), # 读取生产率 np.read. + # 'xv_int_dlv_period_lam': 8.5, + # 'xv_int_create_order_lam': 2, + # 'xv_ary_price_product': tuple([0.3,0.2,0.5,1]), + # 'xv_ary_cost_material_per': tuple([0.1,0.1,0.2,0.4]), + # 'xv_ary_volume_material': tuple([1.0, 1.5]), + # 'xv_ary_volume_product': tuple([3.0, 5.0]), + 'xv_array_lead_time': 2, # 读取原材料表格 np.read.,暂时不读 + # 'xv_int_lead_time_c': 3, + # 'xv_int_lead_time_d': 1, + 'xv_ary_initial_product_num': pd.read_excel("initial_product.xlsx").to_numpy(), + 'xv_ary_initial_material_num': pd.read_excel("initial_material.xlsx").to_numpy(), # 应读取遗传算法中随机生成的s,暂写为'1' 创建两个excel分别存储产品和原材料的库存 每个excel中存系统代码和库存 + # 'xv_flt_initial_cash': 50000.0, + # 'dct_status_info': json.dumps({ #需要引入生产状态表 + # "0": {"xv_flt_produce_rate": tuple([0.0, 0.0]), + # "xv_ary_mat_material": tuple([0.0, 0.0]), + # "xv_flt_broken_rate": 0, + # "xv_flt_run_cost": 0.0, + # "name": "wait" + # }, + # "1": {"xv_flt_produce_rate": tuple([90.0, 0.0]), + # "xv_ary_mat_material": tuple([4.0, 1.0]), + # "xv_flt_broken_rate": 0.03, + # "xv_flt_run_cost": 40.0, + # "name": "produceA" + # }, + # "2": {"xv_flt_produce_rate": tuple([0.0, 60.0]), + # "xv_ary_mat_material": tuple([1.5, 5.0]), + # "xv_flt_broken_rate": 0.05, + # "xv_flt_run_cost": 50.0, + # "name": "produceB" + # }, + # "3": {"xv_flt_produce_rate": tuple([55.0, 30.0]), + # "xv_ary_mat_material": tuple([2.0, 1.5]), + # "xv_flt_broken_rate": 0.07, + # "xv_flt_run_cost": 60.0, + # "name": "produceAB" + # }, + # "-1": {"xv_flt_produce_rate": 0.0, + # "xv_ary_mat_material": tuple([0.0, 0.0]), + # "xv_flt_broken_rate": 0.1, + # "xv_flt_run_cost": 100.0, + # "name": "failed" + # } + # }) + + } + sample = ap.Sample(dct_para) + + exp = ap.Experiment(FMSEnv, sample, iterations=1, record=True) + results = exp.run() + # results['variables']['FMSEnv'].to_excel(f"simulation-results-{datetime.today().strftime('%Y-%m-%d-%H-%M-%S')}.xlsx", + # engine='openpyxl') diff --git a/Firm.py b/Firm.py new file mode 100644 index 0000000..22607b7 --- /dev/null +++ b/Firm.py @@ -0,0 +1,37 @@ +import numpy as np +import json + +from InventorySystem import InventorySystem +from OrderSystem import OrderSystem +from ProduceSystem import ProduceSystem + +# Create Company Instance +class Firm: + the_os: OrderSystem + the_is: InventorySystem + the_ps: ProduceSystem + + def __init__(self, env, dct_all_para): + self.env = env + + # get the parameters here + self.xv_ary_initial_product_num = np.asarray(dct_all_para['xv_ary_initial_product_num']) + self.xv_ary_initial_material_num = np.asarray(dct_all_para['xv_ary_initial_material_num']) + self.dct_status_info = json.loads(dct_all_para['dct_status_info']) + self.xv_ary_lead_time = np.array([dct_all_para['xv_int_lead_time_a'], dct_all_para['xv_int_lead_time_b']]) + + # create agents here + self.the_os = OrderSystem(env) + self.the_is = InventorySystem(env, xv_ary_initial_material_num=self.xv_ary_initial_material_num, + xv_ary_initial_product_num=self.xv_ary_initial_product_num, + xv_ary_lead_time=self.xv_ary_lead_time) + self.the_ps = ProduceSystem(env, dct_status_info=self.dct_status_info) + # self.the_fs = FinancialSystem(env, xv_flt_initial_cash=self.xv_flt_initial_cash) + + def operating(self): + self.the_os.rank_order() + self.the_ps.change_status() + self.the_ps.run_produce() + self.the_os.do_shipment() + # self.the_is.inventory_cost() + # self.the_fs.financial_calculating() diff --git a/InventorySystem.py b/InventorySystem.py new file mode 100644 index 0000000..1e9780f --- /dev/null +++ b/InventorySystem.py @@ -0,0 +1,128 @@ +import agentpy as ap +import numpy as np +from collections import deque + + +class InventorySystem(ap.Agent): + + xv_ary_initial_material_num: np.ndarray + xv_ary_initial_product_num: np.ndarray + ev_ary_current_material: np.ndarray #期末库存 + ev_ary_current_product: np.ndarray #期末库存 + ev_lst_trans_quan_material: np.ndarray # Iss传递给Pss的原材料数量 + ev_lst_backtrans_quan_material: np.ndarray # Pss退回的原材料数量 + ev_ary_material_state_to_use: np.ndarray # 当期预期使用的原材料 + ev_ary_material_to_use: np.ndarray # 当期实际消耗的原材料 + xv_array_dlv_product: np.ndarray # 产品交付 + + ev_ary_is_order_material: np.ndarray # 是否订购产品 + ev_ary_num_material_to_order: np.ndarray # 订购数量 + ev_ary_num_material_order_done: np.ndarray # 当期送到的数量 + + # set the lead time of material replenishment + xv_ary_lead_time: np.ndarray + + # the list of materials in transit + ev_lst_trans_material: list # Iss交给Pss的原材料 + ev_list_back_trans_material: list # Pss返回的原材料 + + ev_lst_material_inventory: list + ev_lst_product_inventory: list + + # Material list + xv_lst_material: np.ndarray + xv_ary_bom: np.ndarray + + # 原材料订购的开始时间和到达时间 + ev_int_order_time: np.ndarray + ev_int_arrive_time: np.ndarray + ev_ary_num_material_order_done: np.ndarray # 当期原材料到货量 + + def setup(self, xv_ary_initial_material_num, + xv_ary_initial_product_num, xv_ary_lead_time): + + # Set up the initial inventory quantity + self.xv_ary_initial_material_num = xv_ary_initial_material_num + self.xv_ary_initial_product_num = xv_ary_initial_product_num + self.ev_ary_current_material = xv_ary_initial_material_num # 115*2 + self.ev_ary_current_product = xv_ary_initial_product_num #23*2 + self.ev_ary_num_material_order_done = np.zeros((115, )) + + # set the lead time of materials + # 读取各个原材料的历史平均交货周期 bom table + self.xv_ary_lead_time = pd.read_excel("bom23.xlsx").iloc[:, 4].to_numpy() + # 切换成原材料 + self.ev_ary_is_order_material = np.array([False for i in range(115)]) + self.ev_ary_num_material_to_order = np.zeros((115,)) # 初始化 + self.ev_ary_material_state_to_use = np.zeros((115,)) + + self.ev_lst_material_inventory = [] + self.ev_lst_product_inventory = [] + + self.xv_lst_material = pd.read_excel("rawmaterial.xlsx").to_numpy() + + self.xv_ary_bom = pd.read_excel("bom23.xlsx").to_numpy() + + self.ev_lst_trans_quan_material = [] # Iss传递给Pss的原材料数量 + self.ev_lst_backtrans_quan_material = [] # Pss退回的原材料数量 + + self.ev_int_order_time = np.zeros((115,)) + self.ev_int_arrive_time = np.zeros((115,)) + + def material_state_to_use(self, ev_int_produce_type, xv_plan_excel): + # 读取Oss决定的生产状态,根据生产状态所需的原材料进行汇总 + # self. ev_ary_material_state_to_use = np.multiply(self.xv_array_dlv_product, 原材料表 ) + produce_state = ev_int_produce_type # 获取生产状态 + produce_plan = xv_plan_excel[xv_plan_excel[:, 0] == produce_state] + for plan in produce_plan: + for bom in self.xv_ary_bom: + if plan[1] == bom[0]: + self.ev_ary_material_state_to_use[np.where(self.xv_lst_material == bom[1])] += plan[3] * bom[ + 2] # 某原材料的生产数量加等产品生产量乘单位原材料消耗量 + + def material_check(self, ev_ary_material_state_to_use): + # 根据Iss决策,核对库存是否充足,并将拥有的原材料交给Pss + # 可能需要一部生成原材料列表在调用 + # Check whether materials are enough and transfer material + index_mapping = {value: idx for idx, value in enumerate(self.ev_ary_current_material[:, 0])} + sorted_indices = [index_mapping[value] for value in self.xv_lst_material] + self.ev_ary_current_material = self.ev_ary_current_material[sorted_indices] # 按照原材料制定顺序进行重排 + for i in range(115): + if self.ev_ary_current_material[i, 1] >= ev_ary_material_state_to_use[i]: + self.ev_lst_trans_quan_material[i] = ev_ary_material_state_to_use[i] + # return (self.ev_ary_current_material >= ev_ary_material_to_use).all() + else: + self.ev_lst_trans_quan_material[i] = self.ev_ary_current_material[i] + + def consume_and_store(self, ev_ary_material_to_use, ev_ary_produced_num,ev_changed_product, + ev_lst_backtrans_material): + self.ev_lst_backtrans_quan_material = ev_lst_backtrans_material + # Update the inventory after production + for i in range(115): + self.ev_ary_material_to_use[i] = self.ev_lst_trans_quan_material[i] - \ + self.ev_lst_backtrans_quan_material[i] + + if self.ev_int_arrive_time == self.model.t: # 判断材料是否到达 + self.ev_ary_num_material_order_done[i] += self.ev_ary_num_material_to_order[i] + self.ev_ary_num_material_to_order[i] = 0 + self.ev_ary_is_order_material[i] = False + + self.ev_ary_current_material = self.ev_ary_current_material - ev_ary_material_to_use \ + + self.ev_ary_num_material_order_done + self.ev_ary_current_product = ev_changed_product + ev_ary_produced_num + return self.ev_ary_current_material, self.ev_ary_current_product + + def material_replenishment(self): + # Check the inventory of material to decide whether to replenish + # 核对原材料数量是否低于s + # 带时间戳 + for i in range(115): + if self.ev_ary_current_material[i] <= s: + self.ev_ary_is_order_material[i] = True + self.ev_ary_num_material_to_order[i] = S - self.ev_ary_current_material[i] + self.ev_int_order_time[i] = self.model.t + self.ev_int_arrive_time[i] = self.ev_int_order_time[i] + \ + self.xv_ary_bom[self.xv_ary_bom[:, 1] == self.xv_lst_material[i]][0, 4] + else: + self.ev_ary_num_material_to_order[i] = 0 + diff --git a/Order.py b/Order.py new file mode 100644 index 0000000..c6a8e89 --- /dev/null +++ b/Order.py @@ -0,0 +1,36 @@ +import agentpy as ap +import random +import numpy as np +from numpy import random +import pandas as pd + + +class Order(ap.Agent): + + xv_time_created: int # 订单创建时间 + xv_time_circle: int # 随机生成的交货周期 + xv_dlv_t: int # 客户希望的交货时间 + ev_actual_dlv_t: int # 实际交付时间 + + ev_is_delivered: bool # 订单是否已交付 + ev_is_accepted: bool # 订单是否被接受 + ev_int_delay_time: int # total delay time + + xv_ary_dlv_product: np.ndarray + ev_ary_dlv_product: np.ndarray + + def setup(self, time_created): + self.xv_time_created = time_created + # read the demand of 23 productions + df = pd.read_excel("demand23.xlsx") + self.xv_ary_dlv_product = df.to_numpy() + df = df.iloc[:, 1] + self.ev_ary_dlv_product = df.to_numpy() + self.xv_time_circle = np.random.randint(7, 11, 1) + self.xv_dlv_t = self.xv_time_created + self.xv_time_circle + self.ev_actual_dlv_t = self.xv_dlv_t + self.ev_int_delay_time = self.ev_actual_dlv_t - self.xv_dlv_t + + # Set the initial status of order to be undelivered, accepted + self.ev_is_delivered = False + self.ev_is_accepted = False diff --git a/OrderSystem.py b/OrderSystem.py new file mode 100644 index 0000000..004d0b6 --- /dev/null +++ b/OrderSystem.py @@ -0,0 +1,103 @@ +import agentpy as ap +import numpy as np +from Order import Order +import pandas as pd + +# 输出一个生产状态指令,一个交付情况,一个延期时间计算 +class OrderSystem(ap.Agent): + a_lst_order: ap.AgentList[Order] + ev_int_produce_type: int + ev_ary_to_dlv: np.ndarray # 当前order list内所有产品的综合 + ev_ary_product_to_produce: np.ndarray # 用于计算 production gap + ev_lst_all_delay_time: list + ev_ave_delay_time: float + xv_plan_excel: np.ndarray + ev_lst_flag: list + ev_changed_product: np.ndarray + # ev_ary_dlv_product: np.ndarray?????? + + def setup(self): + # Create a list of order + self.a_lst_order = ap.AgentList(self, []) + self.ev_ary_to_dlv = np.zeros((23, 1)) + self.ev_int_produce_type = 0 + self.ev_ary_product_to_produce = np.zeros((23,)) + self.ev_lst_all_delay_time = [] + self.ev_ave_delay_time = 0 + # self.ev_ary_dlv_product = np.zeros((23,)) ???? + self.ev_changed_product = np.zeros((23, )) + self.xv_plan_excel = pd.read_excel("plan.xlsx").to_numpy() + pass + + def accept_order(self, new_order): + # Determine whether the order is received and all are currently received + if new_order is not None: + new_order.ev_is_accepted = True + self.a_lst_order.append(new_order) + + def rank_order(self): + # Sort order + self.a_lst_order = self.a_lst_order.sort('xv_dlv_t', reverse=False) + + def produce_status(self): + # 计算a_lst_order内所有订单包含的产品总量与当期库存的gap,排序最大缺口量,选定生产状态 + self.ev_ary_to_dlv = np.zeros((23,)) + for order in self.model.the_firm.the_os.a_lst_order: + self.ev_ary_to_dlv += order.ev_ary_dlv_product # 当前天之前所有的还未满足的需求求和 + self.ev_ary_product_to_produce[:, 1] = self.ev_ary_to_dlv - self.model.the_firm.the_is.ev_ary_current_product + # self.ev_ary_product_to_produce = self.model.the_firm.the_is.ev_ary_current_product - self.ev_ary_to_dlv + # self.ev_ary_product_to_produce > 0: + # 选出这些产品,按照数值大小进行分类,数值最大的产品对应的能带来最大生产率的状态,则选定为ev_int_produce_type + # 如果均<=0, 按照产品库存ev_ary_current_material进行排序,选择能给库存最低的产品带来最高生产率的状态 + sorted_indices = np.argsort(self.ev_ary_product_to_produce[:, 1])[::-1] + sorted_data = self.ev_ary_product_to_produce[sorted_indices] + if sorted_data[0, 1] > 0: # 判断是否存在库存不足 + pass + else: + sorted_indices = np.argsort(self.model.the_firm.the_is.ev_ary_current_product[:, 1]) # 对库存进行从小到大排序 + sorted_data = self.model.the_firm.the_is.ev_ary_current_product[sorted_indices] + + a = self.xv_plan_excel[self.xv_plan_excel[:, 1] == sorted_data[0, 0]] # 检索最大值的产品的四种方案或者最小库存产品的四种方案 + sorted_indices = np.argsort(a[:, 3])[::-1] + sorted_data = a[sorted_indices] + # return sorted_data[0, 0] + self.ev_int_produce_type = sorted_data[0, 0] + return self.ev_int_produce_type + + def do_shipment(self,ev_ary_current_product): + # Make shipments based on ranked order list + # 交付两次,第一次未交付完成的订单要标记,交货的时候先便利delay的订单,如果能够满足全部剩余量,就交货,如果不能 + # 就继续在列表中存在 + # 只有完全交付的订单,才计算delay time = order.ev_int_delay_time * 第二次交付的订单内的各类产品之和 + # 需要更新 order.ev_ary_dlv_product + # Make shipments based on ranked order list + self.ev_lst_all_delay_time = [] + self.ev_ave_delay_time = 0 + self.ev_changed_product = ev_ary_current_product # 23x1 ndarray 存储本次库存的改变 + for order in self.a_lst_order: + if order.xv_dlv_t == self.model.t: # 第一次交付 + # Check and make shipment + order.ev_is_delivered = True + for i in range(len(order.ev_ary_dlv_product)): + if order.ev_ary_dlv_product[i] <= self.ev_changed_product[i]: + order.ev_ary_dlv_product[i] = 0 + self.ev_changed_product[i] -= order.ev_ary_dlv_product[i] + else: + order.ev_is_delivered = False + elif order.xv_dlv_t < self.model.t and order.ev_is_delivered == False: # 第二次交付 + order.ev_is_delivered = True + for i in range(len(order.ev_ary_dlv_product)): + if order.ev_ary_dlv_product[i] > self.ev_changed_product[i]: # 先判断能不能一次性交付 + order.ev_is_delivered = False + + if order.ev_is_delivered: # 如果一次性交付 + delay_num = np.sum(order.ev_ary_dlv_product) + order.ev_ary_dlv_product = np.zeros((23,)) + self.ev_changed_product = self.ev_changed_product - order.ev_ary_dlv_product + + order.ev_actual_dlv_t = self.model.t + order.ev_int_delay_time = order.ev_actual_dlv_t - order.xv_dlv_t + + self.ev_ave_delay_time += order.ev_int_delay_time * delay_num / 10000 + + return self.ev_changed_product, self.ev_ave_delay_time diff --git a/ProduceSystem.py b/ProduceSystem.py new file mode 100644 index 0000000..63d25b7 --- /dev/null +++ b/ProduceSystem.py @@ -0,0 +1,60 @@ +import agentpy as ap +import numpy as np +import random + + +class ProduceSystem(ap.Agent): + ev_str_status: str # 0,6,8,10 pp_id + ev_lst_backtrans_material: list # 退回Iss的原材料 + ev_ary_produce_number: np.ndarray + + def setup(self, dct_status_info): + self.ev_str_status = 0 + self.ev_lst_backtrans_material = [] + + def change_status(self): + self.ev_str_status = self.model.the_firm.the_os.produce_status() + + + def run_produce(self, ev_lst_material, ev_lst_trans_quan_material, ev_ary_product_to_produce, xv_plan_excel, + xv_ary_bom): # ev_ary_product_to_produce 是 需求和库存的gap 23x2 + # 生产状态由Iss确定,这个函数需要计算生产数量和原材料消耗量。 + # 如果原材料不足,就按照production gap(ev_ary_product_to_produce) 大小进行生产;如果没有gap的产品了,就按照库存水平由少到多进行生产。 + # 输出生产结果和原材料消耗结果 + produce_plan = xv_plan_excel[xv_plan_excel[:, 0] == self.ev_str_status] + sorted_indices = np.argsort(ev_ary_product_to_produce[:, 1])[::-1] + sorted_data = ev_ary_product_to_produce[sorted_indices] # 对gap进行从大到小排序 + sorted_indices_product = np.argsort(self.model.the_firm.the_is.ev_ary_current_product[:, 1]) # 对库存进行从小到大排序 + sorted_data_product = self.model.the_firm.the_is.ev_ary_current_product[sorted_indices_product] + self.ev_lst_backtrans_material = ev_lst_trans_quan_material + self.ev_ary_produce_number = [] + for product in sorted_data: + if product[1] > 0: # gap存在 + product_material = xv_ary_bom[xv_ary_bom[:, 0] == product[0]] + produce_number = produce_plan[produce_plan[:, 1] == product[0]][3] + for material in product_material: + produce_number = min(produce_number, int( + self.ev_lst_backtrans_material[np.where(ev_lst_material == material[1])] / material[ + 2])) # 取能生产的最小个数 + for material in product_material: + self.ev_lst_backtrans_material[np.where(ev_lst_material == material[1])] -= produce_number * material[ + 2] # 更新原材料消耗情况 + self.ev_ary_produce_number.append([product[0], produce_number]) + sorted_data_product = sorted_data_product[sorted_data_product[:, 0] != product[0]] + else: + for current_product in sorted_data_product: + product_material = xv_ary_bom[xv_ary_bom[:, 0] == current_product[0]] + produce_number = produce_plan[produce_plan[:, 1] == current_product[0]][3] + for material in product_material: + produce_number = min(produce_number, int( + self.ev_lst_backtrans_material[np.where(ev_lst_material == material[1])] / material[ + 2])) # 取能生产的最小个数 + for material in product_material: + self.ev_lst_backtrans_material[np.where(ev_lst_material == material[1])] -= produce_number * \ + material[2] # 更新原材料消耗情况 + self.ev_ary_produce_number.append([current_product[0], produce_number]) + break + + self.ev_ary_produce_number = np.array(self.ev_ary_produce_number) + + return self.ev_ary_produce_number, self.ev_lst_backtrans_material \ No newline at end of file diff --git a/bom23.csv b/bom23.csv new file mode 100644 index 0000000..bd0886b --- /dev/null +++ b/bom23.csv @@ -0,0 +1,208 @@ +prd_id,mtr_id,qty,unit +ADTB331M0611,Z013V09501055,0.000347,ƽ +ADTB331M0611,F00VC50A055,0.000418,ƽ +ADTB331M0611,SM26040B070,0.000033,ǧ +ADTB331M0611,D12073G2126b,0.0001, +ADTB331M0611,YG00002,9.33E-06, +ADTB331M0611,YHCP50A,0.00017,ǧ +ADTB331M0611,L063-109Y,0.0001,֧ +ADTB331M0611,P06-30I,0.0001,ֻ +ADTB331M0611,GT063T,0.000039,ǧ +AGTR4R7V0812,Z510V12001070,0.000623,ƽ +AGTR4R7V0812,F30VC20A070,0.000784,ƽ +AGTR4R7V0812,EHD450Z085,0.000094,ǧ +AGTR4R7V0812,D12087F2126,0.0001, +AGTR4R7V0812,YG00001,2.07E-05, +AGTR4R7V0812,YHBH421H,0.00019,ǧ +AGTR4R7V0812,N080-130Y,0.0001,֧ +AGTR4R7V0812,P08-30I,0.0001,ֻ +AGTR4R7V0812,GT080C,0.000071,ǧ +AMSR2R2M0609,Z530V12001050,0.000295,ƽ +AMSR2R2M0609,F30VC30A050,0.000345,ƽ +AMSR2R2M0609,W19040X065,0.000045,ǧ +AMSR2R2M0609,D12073T2126b,0.0001, +AMSR2R2M0609,YG00001,9.33E-06, +AMSR2R2M0609,YHBH421H,0.00011,ǧ +AMSR2R2M0609,T063-100Y,0.0001,֧ +AMSR2R2M0609,P06-26T,0.0001,ֻ +AMSR2R2M0609,MS063T,0.000039,ǧ +RD1H108M12025,Z067V10201160,0.00576,ƽ +RD1H108M12025,F00VC40A160,0.006224,ƽ +RD1H108M12025,SM26040B190,0.000519,ǧ +RD1H108M12025,D15147G2328y,0.0001, +RD1H108M12025,YG00006,0.000031, +RD1H108M12025,YHCP50A,0.00121,ǧ +RD1H108M12025,L125-257+,0.0001,֧ +RD1H108M12025,GNH-12.5M,0.0001,ֻ +RD1H108M12025,AW125T,0.000181,ǧ +RD1E108M10020,Z036V09001140,0.002408,ƽ +RD1E108M10020,F00VC40A140,0.002982,ƽ +RD1E108M10020,SM26040B160,0.000209,ǧ +RD1E108M10020,D15147G2328y,0.0001, +RD1E108M10020,YG00005,0.000031, +RD1E108M10020,YHCP50A,0.00057,ǧ +RD1E108M10020,L100-215+,0.0001,֧ +RD1E108M10020,GNH-10F,0.0001,ֻ +RD1E108M10020,AW100T,0.000121,ǧ +AMSR100V1013I,Z530V12001070,0.00126,ƽ +AMSR100V1013I,F30VC20A070,0.00147,ƽ +AMSR100V1013I,W19040X085,0.00018,ǧ +AMSR100V1013I,D15091TL2328,0.0001, +AMSR100V1013I,YG00007,,ǧ +AMSR100V1013I,YHBH421H,0.00026,ǧ +AMSR100V1013I,T100-135Y,0.0001,֧ +AMSR100V1013I,P10-35T,0.0001,ֻ +AMSR100V1013I,MS100T,0.000086,ǧ +AGEN181V1625,Z280V12002170,0.009673,ƽ +AGEN181V1625,F30VC20A170,0.010948,ƽ +AGEN181V1625,EHD440Z200,0.001346,ǧ +AGEN181V1625,D20180G2530,0.0001, +AGEN181V1625,YG00006,0.000065, +AGEN181V1625,YHBH250H,0.0028,ǧ +AGEN181V1625,T160-270Y,0.0001,֧ +AGEN181V1625,P16-50I,0.0001,ֻ +AGEN181V1625,EF160T,0.000261,ǧ +CEGM680M1020,Z210V11501140,0.002884,ƽ +CEGM680M1020,F30VC20A140,0.003164,ƽ +CEGM680M1020,W28540X1160,0.000334,ǧ +CEGM680M1020,D15131G2328,0.0001, +CEGM680M1020,YG00005,0.000031, +CEGM680M1020,YHBH250H,0.00057,ǧ +CEGM680M1020,N100-210Y,0.0001,֧ +CEGM680M1020,P10-35I,0.0001,ֻ +CEGM680M1020,EG100T,0.000117,ǧ +RD2G475M1012M,Z660V12002070,0.000819,ƽ +RD2G475M1012M,F30VC20A070,0.001057,ƽ +RD2G475M1012M,WS28050K085,0.000127,ǧ +RD2G475M1012M,TX15084*G2328,0.0001, +RD2G475M1012M,YG00003,0.0000285, +RD2G475M1012M,YHBH450H,0.00025,ǧ +RD2G475M1012M,L100-130+,0.0001,֧ +RD2G475M1012M,GNH-10L,0.0001,ֻ +RD2G475M1012M,AW100T,0.000082,ǧ +AMSR4R7M0812,Z530V12001070,0.00063,ƽ +AMSR4R7M0812,F30VC20A070,0.000735,ƽ +AMSR4R7M0812,EHD450Z085,0.000095,ǧ +AMSR4R7M0812,D12087T2126d,0.0001, +AMSR4R7M0812,YG00001,2.07E-05, +AMSR4R7M0812,YHBH421H,0.00019,ǧ +AMSR4R7M0812,T080-130Y,0.0001,֧ +AMSR4R7M0812,P08-30T,0.0001,ֻ +AMSR4R7M0812,MS080T,0.000072,ǧ +AVSR2R2V0609,Z530V12001050,0.000305,ƽ +AVSR2R2V0609,F30VC30A050,0.00037,ƽ +AVSR2R2V0609,W19040X065,0.000046,ǧ +AVSR2R2V0609,D12073F2126b,0.0001, +AVSR2R2V0609,YG00001,9.33E-06, +AVSR2R2V0609,YHBH421H,0.00011,ǧ +AVSR2R2V0609,L063-100Y,0.0001,֧ +AVSR2R2V0609,P06-26I,0.0001,ֻ +AVSR2R2V0609,EF063C,0.000034,ǧ +AMSR6R8M1013,Z530V12001070,0.000903,ƽ +AMSR6R8M1013,F30VC20A070,0.001043,ƽ +AMSR6R8M1013,EHD450Z085,0.000136,ǧ +AMSR6R8M1013,D15091T2328,0.0001, +AMSR6R8M1013,YG00002,1.93E-05, +AMSR6R8M1013,YHBH421H,0.00026,ǧ +AMSR6R8M1013,T100-135Y,0.0001,֧ +AMSR6R8M1013,P10-35T,0.0001,ֻ +AMSR6R8M1013,MS100T,0.000086,ǧ +AMSR3R3M0810,Z530V12001050,0.000445,ƽ +AMSR3R3M0810,F30VC30A050,0.00052,ƽ +AMSR3R3M0810,EHD450Z065,0.000072,ǧ +AMSR3R3M0810,D12073T2126b,0.0001, +AMSR3R3M0810,YG00002,0.000019, +AMSR3R3M0810,YHBH421H,0.00017,ǧ +AMSR3R3M0810,T080-103Y,0.0001,֧ +AMSR3R3M0810,P08-26T,0.0001,ֻ +AMSR3R3M0810,MS080T,0.000059,ǧ +AMSN120M0812I,Z280V12001070,0.000651,ƽ +AMSN120M0812I,F30VC20A070,0.000826,ƽ +AMSN120M0812I,EHD450Z085,0.000098,ǧ +AMSN120M0812I,D12087TL2126d,0.0001, +AMSN120M0812I,YG00003,0.000031, +AMSN120M0812I,YHBH250H,0.00019,ǧ +AMSN120M0812I,T080-130Y,0.0001,֧ +AMSN120M0812I,P08-30T,0.0001,ֻ +AMSN120M0812I,MS080T,0.000072,ǧ +WL1H476M6L011,Z082V10001065,0.000371,ƽ +WL1H476M6L011,F20VC50A065,0.000436,ƽ +WL1H476M6L011,MJ24540K080,0.001368,ǧ +WL1H476M6L011,D12087G2126,0.0001, +WL1H476M6L011,YG00024,0.00001, +WL1H476M6L011,YHCP50A,0.00012,ǧ +WL1H476M6L011,L063-117+,0.0001,֧ +WL1H476M6L011,GAH-6.3L,0.0001,ֻ +WL1H476M6L011,AW063T,0.000043,ǧ +AMSR8R2M1013,Z530V12001070,0.001085,ƽ +AMSR8R2M1013,F30VC20A070,0.001225,ƽ +AMSR8R2M1013,EHD450Z085,0.000164,ǧ +AMSR8R2M1013,D15091T2328,0.0001, +AMSR8R2M1013,YG00001,1.93E-05, +AMSR8R2M1013,YHBH421H,0.00026,ǧ +AMSR8R2M1013,T100-135Y,0.0001,֧ +AMSR8R2M1013,P10-35T,0.0001,ֻ +AMSR8R2M1013,MS100T,0.000086,ǧ +AGFM121M1320,Z210V11501140,0.004998,ƽ +AGFM121M1320,F30VC20A140,0.005348,ƽ +AGFM121M1320,EHD440Z160,0.000579,ǧ +AGFM121M1320,D15147G2328bd,0.0001, +AGFM121M1320,YG00004,0.0000375, +AGFM121M1320,YHBH250H,0.00099,ǧ +AGFM121M1320,L130-215Y,0.0001,֧ +AGFM121M1320,P13-35I,0.0001,ֻ +AGFM121M1320,EF130T,0.000155,ǧ +AGSS100V1013,Z590V10503075,0.001493,ƽ +AGSS100V1013,F30VC20A075,0.00162,ƽ +AGSS100V1013,W19040X090,0.00021,ǧ +AGSS100V1013,D15091F2328,0.0001, +AGSS100V1013,YG00003,0.000029, +AGSS100V1013,YHBH450H,0.00049,ǧ +AGSS100V1013,L100-140Y,0.0001,֧ +AGSS100V1013,P10-35I,0.0001,ֻ +AGSS100V1013,GS100C,0.000099,ǧ +AVSR3R3V0609,Z490V12501055,0.000358,ƽ +AVSR3R3V0609,F30VC20A055,0.000413,ƽ +AVSR3R3V0609,W19040X070,0.000053,ǧ +AVSR3R3V0609,D12073F2126b,0.0001, +AVSR3R3V0609,YG00001,9.33E-06, +AVSR3R3V0609,YHBH421H,0.00011,ǧ +AVSR3R3V0609,L063-100Y,0.0001,֧ +AVSR3R3V0609,P06-20I,0.0001,ֻ +AVSR3R3V0609,EF063C,0.000034,ǧ +ACSR4R7V0812,Z530V12001070,0.000623,ƽ +ACSR4R7V0812,F30VC20A070,0.000728,ƽ +ACSR4R7V0812,EHD450Z085,0.000094,ǧ +ACSR4R7V0812,D12087F2126,0.0001, +ACSR4R7V0812,YG00001,2.07E-05, +ACSR4R7V0812,YHBH421H,0.00019,ǧ +ACSR4R7V0812,N080-130Y,0.0001,֧ +ACSR4R7V0812,P08-30I,0.0001,ֻ +ACSR4R7V0812,GS080T,0.000072,ǧ +AMSR1R0M0609VI,Z480V12002050,0.000115,ƽ +AMSR1R0M0609VI,F30VC30A050,0.000165,ƽ +AMSR1R0M0609VI,EHD450Z065,0.000018,ǧ +AMSR1R0M0609VI,D12073TL2126b,0.0001, +AMSR1R0M0609VI,YG00002,9.33E-06, +AMSR1R0M0609VI,YHBH421H,0.00011,ǧ +AMSR1R0M0609VI,T063-100Y,0.0001,֧ +AMSR1R0M0609VI,P06-26T,0.0001,ֻ +AMSR1R0M0609VI,MS063T,0.000039,ǧ +AGTR4R7V0812V,Z510V12001070,0.000623,ƽ +AGTR4R7V0812V,F30VC20A070,0.000784,ƽ +AGTR4R7V0812V,W19020K085,0.000094,ǧ +AGTR4R7V0812V,D12087F2126,0.0001, +AGTR4R7V0812V,YG00001,2.07E-05, +AGTR4R7V0812V,YHBH421H,0.00019,ǧ +AGTR4R7V0812V,N080-130Y,0.0001,֧ +AGTR4R7V0812V,P08-30I,0.0001,ֻ +AGTR4R7V0812V,GT080C,0.000071,ǧ +WL1V477M10016,Z047V10401100,0.00153,ƽ +WL1V477M10016,F00VC40A100,0.00202,ƽ +WL1V477M10016,SM26050B120,0.00017,ǧ +WL1V477M10016,D15131G2328,0.0001, +WL1V477M10016,YG00004,0.00003, +WL1V477M10016,YHCP50A,0.00049,ǧ +WL1V477M10016,L100-175+,0.0001,֧ +WL1V477M10016,GNH-10F,0.0001,ֻ +WL1V477M10016,AW100T,0.000101,ǧ diff --git a/bom23.xlsx b/bom23.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..43a982b8d61cdec67d832604dc66a8ba25e9b240 GIT binary patch literal 17116 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 ZI7T@sh>zX{0AN2pJ|E6gRN#+a{~s%51_b~B literal 0 HcmV?d00001 diff --git a/demand23.xlsx b/demand23.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..b661fffd58e34620cc5de805011c0efa60e6b671 GIT binary patch literal 9870 zcmeG?1y@|j(u2DU?(PyCg1bv_cNp9uxO;$v1PKrvf(;TNSa1vO4ugk;phJQN{U&+) z&hF;z_Y2;;bI!eWrn{=Ux~#jZHB}K12>{3dQ~&@#3$Q-Ubufnm0A3;j0Qdk@coSK7 zHy@CjkA-f42guv(k-w`8O%WnIO922L*8hLcfAI=brHyLzapTHgD&5NNaH_1ee$S z7vlOjc*xjAOq$|lYy>SJAeZ7HwXk?@mkr4DZ0^(Mm)nw7sUmYAB$5dKl)Eyh58N2| zfC)A(7aoH2Y-;GQ10Ro6%;J8U#-DR?WDPPu{luw^m`E42O5|%^qDgD0!)mSl!RtCh zwbPrWg>T-{27aA8_GJh5+QbHOTc;3}IzL%`MZy`_c;KZodsnb&a9|h9i^Ma{>sCQR z2MbPMUqz1ZfjO2i`&rEl`9j?K9Ce2T)xy?vudj!=sSNtf03oA62Riyk<3=v zQeLP!yg{!p`%SJ6ck+<|85({GMJ$ncR)q0@y3xAzRSn#{jLuPYY;Chp!G7vwcG%+U z(mgE6cwx1}nJmH~Z)^MIn&2T9+VdY|dO8lmj7Pc2k=20 zwe{|xqOwYFzLYq};Za{?O>V4(Q0x*z=$i8~bxqTNboL}hRcdl0qoC-}4UI#p`m|Dz zinmrhsyJWFBjk;)T=!_x@fbrRfF>9fWNc9Qshq(ENzcB>n$e#~hDf(h_YtL@vVu-J zPif++Cl7W`JH>EbOT|`RI;O-h+ma0u3U_2zl7f_RqrQyNI`j0cxDYYc1hJUC-*X>M zW4$g1E4Flc_A-@Zof;#u!bfT!V=0Mf*gn+6I_E_#Vg)xR=k%CQ@S)+C-+^kfI4bPu zwVYnAytp<1>DeUn!qGL=EawYkV6IcX#EnLvSCd{!Bj0j>d0kbHEVg zE9)JbQ~LIi^^Kv-sVx(y&HnQU2s7pTDA#kG5@(Dz+fvE?SM`ZEdCvzaH!cE07Uu>V z70nM{+#Nrr!U@r#o?YyFm|k;oj5|WQ?n5Zq$}pyPwSmjc+^2>9nc?Bxk~ zh=KSnP*EiJ@zZexRO_Q^sz^m7e7wW`gUTZe>^I){A0$$SIPwS{f4O2|B#kRBrIS1R zFcZt~G{OCp*U5W|as=ivM;ms+ucLJit0Nnln`z##+I@cbAPc!b&W{YBo<1c-Qx?Mt z08D?Kqh-J9k-tFw-hOMR9x&nDQ+$p2nW9pa3M?QV9P2|9H>5}%#+u^&xhShm54z3<4L-L#ITaFnT!| zFS&aeR6RtLld-_xFyy7ujS5kD@AMUFkzCBmDB&^d1;XM#1LUo9Nrb(DR+-#T(cm(W zY)nZ+7ut;_Qt@AAQFd=C+2?+NlUz&q(F5l`OIK}}wM#f_N*2g4cT5y}B*MDclkglF z{{=2e0?OFNrPm4b8=~e-e!qipZajUQctaJ&K3Zwk>7r00(t|5)tF;^JUZiEwcGchk z=B&A}lD8!u3y?>)N_K-e??&_MNIz|>wbnc^sqt80H}Se{BI$*4lx!c5v2oRYL$R7C zB?`-^6smXq>U{q|w$&C-+Z8>#L~b@_@=TPXk`B8yHk5V$-FqHTVgTdY+2Av-rF8n4GBn_@{7aK7WEFukN8`f52d4BT1vK9U{|$*fai%9> zes5!5xa(e7`AyOogh*|n>62A!H^-rzz>6o$7>$@1#vmrijruQ4SqF!W=`|I)f90

szJ^}Am1EWAC4C!5e2UeB!4!ebSKXq>Ckqw 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 z z*`+S*6-WxK~BKx7$cB#xO=~8H_;hnN2?xcE{M}4 zKqFyBrjo>6uNaDzM37}uU7^{0mYd*&C^h$?=hT@#>`>q!mtSsGwDC)3Kt6Me44E`5 zV9RMT(7RCUF%wAB2ja_YAw&bFkcnL!T`7wJ6fzy-~ihSyP7rFFkz48ymhI6=d? zF-qu4>`96aGRGtp)=e=IHpQaYW7YWKU#h2GS80`L&s%^3)5H}>+YSCRG!^9-S^U(2 z+Lu?+TFtZmw6QE*=SuxM-g;20^R_DQN5{F6lY)(vxsC2KQ-+c&rFsd`JqHs#JxYx0 znT&7{&O#y64`BX^2_O9q2C=pQ!vSf@=^6oHT5#65+y{D!R>6pPvjay$-|(7l;>0FQ z6V0YjA>#%BCc)Ak%ipXjQnY!tzgO+j(H7J1rC~wgEXvZvS5crC3@MsjT zfySP(VU5WZ$hsL`3j%Lc zYP$IJPL}(kSAESlnsF!ncJ54uGVB8-@+Rkw?@Pu7|4n&mh_HB*wR`(fv=)ZDa0$$h zqMhrb37^vQwluMNP1MIu{43c30?qPo1jCg&%g_7j4UcQl#><>lWdb0Wq#deUF#cx+ zRQGM*R_6D3$8)${jNr3)si0n0*P_0Mnr5rwBEsS-WW@|fvYsliTmV83kMQB~WA};^ zB>$*P9`xm|&SOn#J_p^)vPJe9!AgK_6os%c_N~xZMx?)*sI(sJI^*hbkMviPz!K*r zzBy;<+hDt&Y3dacje#8F7cnK5jg=i-_5mCv>-;(!+KqwX*bzB*G#6gNIWOL4oL<>| zc|JM$1wIJ+=t@Ev>LhW^#3S?KdQ3}|dLtZ4X**d;n4XN=nfGipEEhNUroB|ni`w9> zBK~EAD5jTYp2NP{P1f8XW;minJKPnN5FaH&-}$h-3sKWB@^~yw9wXXNf-cwrPY@PT z6+U75lp81Px#h8&kNwSnd=D=QH}>a|t2Rc@r&A&iZ+kFG3HoUF1c@FhG9{sWOzkEC zgEs(xks7=;9Tn3+^LRd%g>JIThd1_S<2jgdA4pc$*-Mmo6JAh4_t(|z4wH}YP) zaP!gemmgIzoTJx9u7AuSfDPl}oQj5K+OFUT&zeIld#e2Gty=P*e8_YcSF@9&7F%nH zUKZCnbhQhKEZNUMid&5GsS@`-822aiAaLy#ky_9BX8LX4$;n%^1|6KQG`3z@IBRBb zfBGhJbj8vHN35`#Thr6pd!94WlDq#3Z9Ov-6$~%RH$N)4;z0K63V_{NHc_3i1M1wl zzIYZxL0F4jW$L>0ZM$MAn^jGP`FTy+M~Ua~?`5h#@XVy1wYp`7^rchm3qRb|e{J7p zQa+zW`=fhF!(lW6H>y9i=MMquJ1bb0Ob9I>>tM;N`t83o)u$oHXYv2_-FJj(Q;-6| zs-qMJYax~#TPgUYp%LE-rBh(C_6B+UrIR4nFdq-1%C4?$rg6mE)r9+$svrH# zR|K|Hrz(4<1OmG1ErvXVc3BudFI{gX-{3A_ z*l}hj?HQ(lYjunAfR2-X4;G6tl!EC1*nW;~HXH2jN54h1*J{|++sX=08-pvbR%>w6E|${p*;B@#lJO`F z{4xhlj1!u#=yEfsVu{jUXjC<0Cm+Az_P_AN8_u31wC;pXV;IjQMoePY!z4=^mk2_* z#zUdDuNBbr49GY(1Om7J)RxT*$?}Ft=W!MYZ)}ofj8k|vED_k4kQ-$dt8|vS>%@oH zM2eHtYqR?kdqXsFg)S5{dN7B{=7ysaN zA^4NcjjlLO#WG+Zjz;n)k{exN99$y3!gdpcmJkhfVN%r$siea@mLzIb$ijLPZWzy^`10XA@_^xU3PRlE{1ILR2I({pYM9wt@UI^^#|J@ng)O@`}Iw^g=4)(sDJtmqW z!`niS6F=QGim(c!l0w-r8)0M3`#XPn3Eb@dI$LIwrT7K^nX6?1W`+>QX5YPk8WdH~ zIH~PD!AdP$hAvq&*lorFao2H-L=IVY?Na%-%lX=P$`d93mN$!jx}{%M4do@n*Sgw( zVN=HU>|*N17cbr~f~w|J#jaJ5G|ut-gmUTwe-tc=Rv5mQimBdcDStZ%acu{skU~e$ z_?jcu1*@`DBasw;bpDrih%dd`cRSR=S5&wO^RS33SeZF@V^32djAk7^_``zkN#lW( zBRfk>PnYrWn~ZWq1RWkP%kl0W381j=SK7E49j_}pG6~zT;6;W)Q960iDD^1iU*0{9 zc-F2Gjqu%S%Y;mVi((N%WllL=uTSiW`-W1V;$w(*JHHIY#|VC%U#2W$t%DH^E44pE z4S$nut^s)}f<*oauRauJ938b9JNBz#L}<3TNN>0Bc0WCde-0{Y1hRR!dxh_rod>^Q z$oFn=2N|fLi4S4un5aw$8?pE=cQa?Xfq{Jxk&>KbwJ|57&zElZYpzodH)cdWnd3E0 z3Qc}+#929_)bVgN-Q`oYzJw#k&m5Q`;?88f>s(Xro$^Hd^51I9gS&%zFQ+#1cq5>K zB7&z{_&x6qErhy_1lNrW@9#cPzBEB&XYqME-A_PMc02}lVn!M)b@m03c*_>SeFbQg z3CM@N5w&KX86>vogqxoFY?+ z!Vd7vb6Bi-{d6iqtoO5rIVv%49h8F!q3QPfC7fZ8k=joq_9@yx#0Z_lh^1FKItMle z2QCdsEw#ixx}`NI%ran~nIRE=h-`s63?Am!s*Cs@iYZ!HJUeg#%cwo4Ph_94Ug69(773mJ-{vq~olD;k#$dJE3UWzA8M`a5hV{y^l#&DEISmHRvW*^4e$mtQ>NtvWlDXhO+7N<6Oz7or6qB ztkIH27mr;5VuX#H$=Rbtn~$3HG+SJB#R`r@x%z@VE!D-v<F5X|VM6rXE>l#WL5@E*w4OBElJ}jZu$8*431NjXM1>4$=0i8^KoiV`2Z zoU;ta{B%4sy2Jjwv>(hI3+*$G;@%9)8=<5LGR=b#c4^##aMA8b$ijYJQoLcYqMYnE z^gRvf&uq0;Ts|a7@)>kw_w9Q!Yi2i1{c9F@u?khdJwFgY&3ZLKgFnN+84DIO zsazg+y+O!dNLUytDbT7f(X%=cN}IGeImT_ylgMxdN#8BG9TC>#Srt8rW7uC$JgYLN zvr20RpY8Hg?7?Hp<``^I3%YkeT!!}#pzvQXPy__-QmW6}z7YL$=OH*xhn)>9ZUZ1d zKwgO)z#}S0b3+GH6J;j{3!4vrY(Sh7bZygKtJ{=E{BIdzF})6Zo8El+7MGw#)Akmp zrA)yNFV6tmrNFwwXTWKSB2D+4&z-+FOv1LqHF+%!-+8gr83rB=N-5*MK(+GV^y2I| z00!LJa%MVNZ^-#pc>k2$^Mzq%0b*CxgCH{shp)eyFT+&*K$Kr1Xe*znWnpde7Wcp~ z!8vzKDfJjlS;%c^rW`In*;f_H6Y__S>cbUHqb=MU!=!z7X<2rJG3)q#3Tb27N6Y~; zv6&asxy7tQWQg8EwpgrxrI5Vl&A?d*_G7pPL?r8dO zK3X`ibnpt*%b;DNt})cn^4hnI!85>cQ+;N>HToLx9)-uV-|y7NKVdu(OB}0oFqz{{ z7Ze@Nzz3&l5R2pP85TmN^NDHA5wT^HvOoJRekVa67Tvk3SIo_DAl75-tsS?sBvbT~ z%1XKQTGXNuJ^G0SJz*(@+EYw*+z88e5j7Ohm7gM8R!!@$RHK*zZ8PSlFOzrGI!ThI zh%4rF;_N~00G?|1RsPR2`o@v^5rszhh

7o5e(w4Dp7diBc^&l~*G{#~ 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 BwG{vW literal 0 HcmV?d00001