From 8e3f62e06d824ef9f63c1db8c5e8f2499b67d62c Mon Sep 17 00:00:00 2001 From: DingZQ Date: Fri, 7 Feb 2025 23:23:20 +0800 Subject: [PATCH] Add run_simulation.py --- run_simulation.py | 856 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 856 insertions(+) create mode 100644 run_simulation.py diff --git a/run_simulation.py b/run_simulation.py new file mode 100644 index 0000000..ced988f --- /dev/null +++ b/run_simulation.py @@ -0,0 +1,856 @@ +import numpy as np +from tjnetwork import * +from api.s36_wda_cal import * +from get_real_status import * +from datetime import datetime,timedelta +from math import modf +import json +import pytz +import requests +import time + + +url_path = 'http://10.101.15.16:9000/loong' # 内网 +# url_path = 'http://183.64.62.100:9057/loong' # 外网 +url_real = url_path + '/api/mpoints/realValue' +url_hist = url_path + '/api/curves/data' + +PATTERN_TIME_STEP=15.0 +DN_900_ID='2498' +DN_500_ID='3854' +DN_1000_ID='3853' +H_RESSURE='2510' +L_PRESURE='2514' +H_TANK='4780' +L_TANK='4854' + +H_REGION_1='SA_ZBBDJSCP000002' +H_REGION_2='' #to do +L_REGION_1='SA_ZBBDTJSC000001' +L_REGION_2='SA_R00003' + +# reservoir basic height +RESERVOIR_BASIC_HEIGHT = float(250.35) + +# regions +regions = ['hp', 'lp'] +regions_demand_patterns = {'hp': ['DN900', 'DN500'], 'lp': ['DN1000']} # 出厂水量近似表示用水量 +regions_patterns = {'hp': ['ChuanYiJiXiao', 'BeiQuanHuaYuan', 'ZhuangYuanFuDi', 'JingNingJiaYuan', + '308', 'JiaYinYuan', 'XinChengGuoJi', 'YiJingBeiChen', 'ZhongYangXinDu', + 'XinHaiJiaYuan', 'DongFengJie', 'DingYaXinYu', 'ZiYunTai', 'XieMaGuangChang', + 'YongJinFu', 'BianDianZhan', 'BeiNanDaDao', 'TianShengLiJie', 'XueYuanXiaoQu', + 'YunHuaLu', 'GaoJiaQiao', 'LuZuoFuLuXiaDuan', 'TianRunCheng', 'CaoJiaBa', + 'PuLingChang', 'QiLongXiaoQu', 'TuanXiao', + 'TuanShanBaoZhongShiHua', 'XieMa', 'BeiWenQuanJiuHaoErQi', 'LaiYinHuSiQi', + 'DN500', 'DN900'], + 'lp': ['PanXiMingDu', 'WanKeJinYuHuaFuGaoCeng', 'KeJiXiao', + 'LuGouQiao', 'LongJiangHuaYuan', 'LaoQiZhongDui', 'ShiYanCun', 'TianQiDaSha', + 'TianShengPaiChuSuo', 'TianShengShangPin', 'JiaoTang', 'RenMinHuaYuan', + 'TaiJiBinJiangYiQi', 'TianQiHuaYuan', 'TaiJiBinJiangErQi', '122Zhong', + 'WanKeJinYuHuaFuYangFang', 'ChengBeiCaiShiKou', 'WenXingShe', 'YueLiangTianBBGJCZ', + 'YueLiangTian', 'YueLiangTian200', 'ChengTaoChang', 'HuoCheZhan', 'LiangKu', 'QunXingLu', + 'JiuYuanErTongYiYuan', 'TangDouHua', 'TaiJiBinJiangErQi(SanJi)', + 'ZhangDouHua', 'JinYunXiaoQuDN400', + 'DN1000']} + +# nodes +monitor_single_patterns = ['ChuanYiJiXiao', 'BeiQuanHuaYuan', 'ZhuangYuanFuDi', 'JingNingJiaYuan', + '308', 'JiaYinYuan', 'XinChengGuoJi', 'YiJingBeiChen', 'ZhongYangXinDu', + 'XinHaiJiaYuan', 'DongFengJie', 'DingYaXinYu', 'ZiYunTai', 'XieMaGuangChang', + 'YongJinFu', 'PanXiMingDu', 'WanKeJinYuHuaFuGaoCeng', 'KeJiXiao', + 'LuGouQiao', 'LongJiangHuaYuan', 'LaoQiZhongDui', 'ShiYanCun', 'TianQiDaSha', + 'TianShengPaiChuSuo', 'TianShengShangPin', 'JiaoTang', 'RenMinHuaYuan', + 'TaiJiBinJiangYiQi', 'TianQiHuaYuan', 'TaiJiBinJiangErQi', '122Zhong', + 'WanKeJinYuHuaFuYangFang'] +monitor_single_patterns_id = {'ChuanYiJiXiao': '7338', 'BeiQuanHuaYuan': '7315', 'ZhuangYuanFuDi': '7316', + 'JingNingJiaYuan': '7528', '308': '8272', 'JiaYinYuan': '7304', + 'XinChengGuoJi': '7325', 'YiJingBeiChen': '7328', 'ZhongYangXinDu': '7329', + 'XinHaiJiaYuan': '9138', 'DongFengJie': '7302', 'DingYaXinYu': '7331', + 'ZiYunTai': '7420,9059', 'XieMaGuangChang': '7326', 'YongJinFu': '9059', + 'PanXiMingDu': '7320', 'WanKeJinYuHuaFuGaoCeng': '7419', + 'KeJiXiao': '7305', 'LuGouQiao': '7306', 'LongJiangHuaYuan': '7318', + 'LaoQiZhongDui': '9075', 'ShiYanCun': '7309', 'TianQiDaSha': '7323', + 'TianShengPaiChuSuo': '7335', 'TianShengShangPin': '7324', 'JiaoTang': '7332', + 'RenMinHuaYuan': '7322', 'TaiJiBinJiangYiQi': '7333', 'TianQiHuaYuan': '8235', + 'TaiJiBinJiangErQi': '7334', '122Zhong': '7314', 'WanKeJinYuHuaFuYangFang': '7418'} + +monitor_unity_patterns = ['BianDianZhan', 'BeiNanDaDao', 'TianShengLiJie', 'XueYuanXiaoQu', + 'YunHuaLu', 'GaoJiaQiao', 'LuZuoFuLuXiaDuan', 'TianRunCheng', + 'CaoJiaBa', 'PuLingChang', 'QiLongXiaoQu', 'TuanXiao', + 'ChengBeiCaiShiKou', 'WenXingShe', 'YueLiangTianBBGJCZ', + 'YueLiangTian', 'YueLiangTian200', + 'ChengTaoChang', 'HuoCheZhan', 'LiangKu', 'QunXingLu', + 'TuanShanBaoZhongShiHua', 'XieMa', 'BeiWenQuanJiuHaoErQi', 'LaiYinHuSiQi', + 'JiuYuanErTongYiYuan', 'TangDouHua', 'TaiJiBinJiangErQi(SanJi)', + 'ZhangDouHua', 'JinYunXiaoQuDN400', + 'DN500', 'DN900', 'DN1000'] +monitor_unity_patterns_id = {'BianDianZhan': '7339', 'BeiNanDaDao': '7319', 'TianShengLiJie': '8242', + 'XueYuanXiaoQu': '7327', 'YunHuaLu': '7312', 'GaoJiaQiao': '7340', + 'LuZuoFuLuXiaDuan': '7343', 'TianRunCheng': '7310', 'CaoJiaBa': '7300', + 'PuLingChang': '7307', 'QiLongXiaoQu': '7321', 'TuanXiao': '8963', + 'ChengBeiCaiShiKou': '7330', 'WenXingShe': '7311', + 'YueLiangTianBBGJCZ': '7313', 'YueLiangTian': '7313', 'YueLiangTian200': '7313', + 'ChengTaoChang': '7301', 'HuoCheZhan': '7303', + 'LiangKu': '7296', 'QunXingLu': '7308', + 'DN500': '3854', 'DN900': '2498', 'DN1000': '3853'} +monitor_patterns = monitor_single_patterns + monitor_unity_patterns +monitor_patterns_id = {**monitor_single_patterns_id, **monitor_unity_patterns_id} +# pumps +pumps_name = ['1#', '2#', '3#', '4#', '5#', '6#', '7#'] +pumps = ['PU00000', 'PU00001', 'PU00002', 'PU00003', 'PU00004', 'PU00005', 'PU00006'] +variable_frequency_pumps = ['PU00004', 'PU00005', 'PU00006'] +pumps_id = {'PU00000': '2747', 'PU00001': '2776', 'PU00002': '2730', 'PU00003': '2787', + 'PU00004': '2500', 'PU00005': '2502', 'PU00006': '2504'} +# reservoirs +reservoirs = ['ZBBDJSCP000002', 'R00003'] +reservoirs_id = {'ZBBDJSCP000002': '2497', 'R00003': '2571'} +# tanks +tanks = ['ZBBDTJSC000002', 'ZBBDTJSC000001'] +tanks_id = {'ZBBDTJSC000002': '4780', 'ZBBDTJSC000001': '9774'} + + +class DataLoader: + """数据加载器""" + def __init__(self, project_name, start_time: datetime, end_time: datetime, + pumps_control: dict = None, tank_initial_level_control: dict = None, + region_demand_control: dict = None, downloading_prohibition: bool = False): + self.project_name = project_name # 数据库名 + self.current_time = self.round_time(datetime.now(pytz.timezone('Asia/Shanghai')), 1) # 圆整至整分钟 + self.current_round_time = self.round_time(self.current_time, int(PATTERN_TIME_STEP)) + self.updating_data_flag = True \ + if self.current_round_time == self.round_time(start_time, int(PATTERN_TIME_STEP)) \ + else False # 判断是否从当前时刻开始模拟(是否更新最新监测数据) + self.downloading_prohibition = downloading_prohibition # 是否禁止下载数据(默认False: 允许下载) + self.updating_data_flag = False if self.downloading_prohibition else self.updating_data_flag + self.pattern_start_index = get_pattern_index( + self.round_time(start_time, int(PATTERN_TIME_STEP)).strftime("%Y-%m-%d %H:%M:%S")) # pattern起始索引 + self.pattern_end_index = get_pattern_index( + self.round_time(end_time, int(PATTERN_TIME_STEP)).strftime("%Y-%m-%d %H:%M:%S")) # pattern结束索引 + self.pattern_index_list = list(range(self.pattern_start_index, self.pattern_end_index + 1)) # pattern索引列表 + self.download_id = self.get_download_id() # 数据下载接口id '7338,7315,7316,...' + self.current_time_download_data = dict( + zip(self.download_id.split(','), + [np.nan]*len(list(self.download_id.split(',')))) + ) # {id(str): value(float)} + self.current_time_download_data_flag = dict( + zip(self.download_id.split(','), + [False]*len(list(self.download_id.split(',')))) + ) # 下载数据是否具备实时性, {id(str): flag(bool)} + self.old_flow_data = self.init_dict_of_list(dict( + zip(monitor_patterns, + [[np.nan]] * (len(monitor_patterns))) + )) # {pattern_name(str): flow(float)} + self.old_pattern_factor = self.init_dict_of_list(dict( + zip(monitor_patterns, + [[np.nan]] * (len(monitor_patterns))) + )) # {pattern_name(str): [pattern_factor(float)]} + self.new_flow_data = self.init_dict_of_list(dict( + zip(monitor_patterns, + [[np.nan]] * (len(monitor_patterns))) + )) # {pattern_name(str): flow(float)} + self.new_pattern_factor = self.init_dict_of_list(dict( + zip(monitor_patterns, + [[np.nan]] * (len(monitor_patterns))) + )) # {pattern_name(str): [pattern_factor(float)]} + self.reservoir_data = dict(zip(reservoirs, [np.nan]*len(reservoirs))) # {reservoir_name(str): level(float)} + self.tank_data = dict(zip(tanks, [np.nan] * len(tanks))) # {tank_name(str): level(float)} + self.pump_data = self.init_dict_of_list( + dict(zip(pumps, [[np.nan]]*len(pumps)))) # {pump_name(str): [frequency(float)]} + self.pump_control = pumps_control # {pump_name(str): [frequency(float)]} + self.tank_initial_level_control = tank_initial_level_control # {tank_name(str): level(float)} + self.region_demand_current = dict(zip(regions, [0]*len(regions))) # {region_name(str): total_demand(float)} + self.region_demand_control = region_demand_control # {region_name(str): total_demand(float)} + self.region_demand_control_factor = dict( + zip(regions, [1]*len(regions))) # 区域流量控制系数(用于调整用水量), {region_name(str): factor(float)} + + def load_data(self): + """生成数据集""" + self.download_data() # 下载实时数据 + self.get_old_pattern_and_flow() # 读取历史记录pattern信息 + self.cal_demand_convert_factor() # 计算用水量转换系数(设定用水量时) + self.set_new_flow() # 设置'更新'流量 + self.set_new_pattern_factor() # 设置'更新'pattern factors + self.set_reservoirs() # 设置清水池 + self.set_tanks() # 设置调节池 + self.set_pumps() # 设置水泵 + return self.pattern_start_index + + def download_data(self): + """下载数据""" + if self.updating_data_flag is True: + print('{} -- Start downloading data.'.format( + datetime.now().strftime('%Y-%m-%d %H:%M:%S'))) + data_wait_flag = True + while data_wait_flag: + try: + newest_data_time = self.download_real_data(self.download_id) # 获取实时数据 + except Exception as e: + print('{}\nWaiting for real data.'.format(e)) + time.sleep(1) + else: + print('{} -- Downloading data ok. Newest timestamp: {}.'.format( + datetime.now().strftime('%Y-%m-%d %H:%M:%S'), + newest_data_time.strftime('%Y-%m-%d %H:%M:%S'))) + data_wait_flag = False + + def cal_current_region_demand(self): + """计算区域当前用水量""" + if self.updating_data_flag is True: + for region in self.region_demand_current.keys(): + total_demand = 0 + for pipe in regions_demand_patterns[region]: + total_demand += self.current_time_download_data[monitor_patterns_id[pipe]] # 出厂流量 + self.region_demand_current[region] = total_demand + + def cal_history_region_demand(self, pattern_index_list): + """计算区域历史用水量(对应记录的pattern)""" + old_demand = {} + for region in regions: + total_demand = 0 + for pipe_pattern_name in regions_demand_patterns[region]: + old_flows, old_patterns = self.get_history_pattern_info(self.project_name, pipe_pattern_name) + for idx in pattern_index_list: + total_demand += old_flows[idx] / 4 # 15分钟水量 + old_demand[region] = total_demand + return old_demand + + def cal_demand_convert_factor(self): + """计算用水量转换系数(设定用水量时)""" + self.cal_current_region_demand() # 计算区域当前时刻用水量 + old_demand_moment = self.cal_history_region_demand([self.pattern_start_index]) # 计算区域目标时刻总用水量 + old_demand_period = self.cal_history_region_demand(self.pattern_index_list) # 计算区域目标时段总用水量 + for region in regions: + self.region_demand_control_factor[region] \ + = (self.region_demand_current[region] / 4) / old_demand_moment[region] \ + if self.updating_data_flag is True else 1 + self.region_demand_control_factor[region] = self.region_demand_control[region] / old_demand_period[region] \ + if (self.region_demand_control is not None) and (region in self.region_demand_control.keys()) \ + else self.region_demand_control_factor[region] + + def get_old_pattern_and_flow(self): + """获取所有pattern的选定时段的历史记录的pattern和flow""" + for idx in monitor_patterns: # 遍历patterns + old_flows, old_patterns = self.get_history_pattern_info(self.project_name, idx) + for pattern_idx in self.pattern_index_list: + old_flow_data = old_flows[pattern_idx] + old_pattern_factor = old_patterns[pattern_idx] + if pattern_idx == self.pattern_start_index: # 起始时刻 + self.old_flow_data[idx][0] = old_flow_data + self.old_pattern_factor[idx][0] = old_pattern_factor + else: + self.old_flow_data[idx].append(old_flow_data) + self.old_pattern_factor[idx].append(old_pattern_factor) + + def set_new_flow(self): + """计算模拟时段新流量(相较于历史记录)""" + for idx in self.new_flow_data.keys(): # 遍历patterns + region_name = None + for region in regions_patterns.keys(): + if idx in regions_patterns[region]: + region_name = region # pattern所属分区 + break + # 实时流量 + if self.updating_data_flag is True: + if idx in monitor_unity_patterns[-3:]: # 出水管流量 + self.new_flow_data[idx][0] = self.current_time_download_data[monitor_patterns_id[idx]] + else: # 其余流量 + self.new_flow_data[idx][0] \ + = self.region_demand_control_factor[region_name] * self.old_flow_data[idx][0] + # if idx == 'ZiYunTai': + # idx_a, idx_b = monitor_patterns_id[idx].split(',') + # self.new_flow_data[idx][0] \ + # = self.current_time_download_data[idx_a] - self.current_time_download_data[idx_b] + # else: + # self.new_flow_data[idx][0] = self.current_time_download_data[monitor_patterns_id[idx]] + # for data_id in monitor_patterns_id[idx].split(','): + # if (self.current_time_download_data_flag[data_id] is False) \ + # and (idx not in [pipe for pipe_list in regions_demand_patterns.values() + # for pipe in pipe_list]): # 无法获取实时数据 + # self.new_flow_data[idx][0] \ + # = self.region_demand_control_factor[region_name] * self.old_flow_data[idx][0] + # break + # 根据设定用水量修改新流量 + if (self.region_demand_control is not None) \ + and (region_name in self.region_demand_control.keys()): + for pattern_idx in self.pattern_index_list: + if pattern_idx == self.pattern_start_index: # 起始时刻 + self.new_flow_data[idx][0] \ + = self.region_demand_control_factor[region_name] * self.old_flow_data[idx][0] + else: + self.new_flow_data[idx].append( + self.region_demand_control_factor[region_name] + * self.old_flow_data[idx][self.pattern_index_list.index(pattern_idx)] + ) + + def set_new_pattern_factor(self): + """更新计算选定时段(设定用水量)/时刻的pattern factor""" + pattern_index_list = self.pattern_index_list \ + if self.region_demand_control is not None \ + else [self.pattern_start_index] + for idx in monitor_patterns: # 遍历patterns + for pattern_idx in pattern_index_list: # 遍历需要修改的pattern(index) + pattern_idx_cls = pattern_index_list.index(pattern_idx) # 转换index(类表存储结构) + old_flow_data = self.old_flow_data[idx][pattern_idx_cls] + old_pattern_factor = self.old_pattern_factor[idx][pattern_idx_cls] + if pattern_idx_cls == 0: # 起始时刻 + if idx in monitor_single_patterns: + if not np.isnan(self.new_flow_data[idx][0]): + self.new_pattern_factor[idx][0] = (self.new_flow_data[idx][0] * 1000 / 3600) # m3/h to L/s + if idx in monitor_unity_patterns: + if not np.isnan(self.new_flow_data[idx][0]): + self.new_pattern_factor[idx][0] \ + = old_pattern_factor * self.new_flow_data[idx][0] / old_flow_data + else: + if idx in monitor_single_patterns: + if len(self.new_flow_data[idx]) > pattern_idx_cls: + self.new_pattern_factor[idx].append( + (self.new_flow_data[idx][pattern_idx_cls] * 1000 / 3600)) # m3/h to L/s + if idx in monitor_unity_patterns: + if len(self.new_flow_data[idx]) > pattern_idx_cls: + self.new_pattern_factor[idx].append( + old_pattern_factor + * self.new_flow_data[idx][pattern_idx_cls] + / old_flow_data) + + def set_reservoirs(self): + """设置清水池""" + if self.updating_data_flag is True: + for idx in self.reservoir_data.keys(): + if self.current_time_download_data_flag[reservoirs_id[idx]] is False: # 无法获取实时数据 + print('There is no current data of reservoir: {}.'.format(idx)) + else: + self.reservoir_data[idx] \ + = self.current_time_download_data[reservoirs_id[idx]] + RESERVOIR_BASIC_HEIGHT + + def set_tanks(self): + """设置调节池""" + for idx in self.tank_data.keys(): + if self.updating_data_flag is True: + if self.current_time_download_data_flag[tanks_id[idx]] is False: # 无法获取实时数据 + print('There is no current data of tank: {}.'.format(idx)) + else: + self.tank_data[idx] = self.current_time_download_data[tanks_id[idx]] + self.tank_data[idx] = self.tank_initial_level_control[idx] \ + if (self.tank_initial_level_control is not None) and (idx in self.tank_initial_level_control) \ + else self.tank_data[idx] + + def set_pumps(self): + """设置水泵""" + for idx in self.pump_data.keys(): + if self.updating_data_flag is True: + if self.current_time_download_data_flag[pumps_id[idx]] is False: # 无法获取实时数据 + print('There is no current data of pump: {}.'.format(idx)) + if (self.pump_control is not None) and (idx in self.pump_control.keys()): + self.pump_data[idx] = self.pump_control[idx] + else: + self.pump_data[idx] = [self.current_time_download_data[pumps_id[idx]]] + if (self.pump_control is not None) and (idx in self.pump_control.keys()): + self.pump_data[idx] = self.pump_data[idx] + self.pump_control[idx] \ + if len(self.pump_control[idx]) < len(self.pattern_index_list) \ + else self.pump_control[idx] # 水泵设定 + else: + if (self.pump_control is not None) and (idx in self.pump_control.keys()): + self.pump_data[idx] = self.pump_control[idx] + self.pump_data[idx] \ + = list(np.array(self.pump_data[idx]) / 50) \ + if idx in variable_frequency_pumps else self.pump_data[idx] + + def set_valves(self): + """设置阀门""" + pass + + def download_real_data(self, ids: str): + """加载实时数据""" + # 数据接口的地址 + global url_real + # 设置GET请求的参数 + params = {'ids': ids} + # 发送GET请求获取数据 + response = requests.get(url_real, params=params) + # 检查响应状态码,200表示请求成功 + if response.status_code == 200: + newest_data_time = None # 下载记录数据的最新时间 + # 解析响应的JSON数据 + data = response.json() + for realValue in data: # 取出逐个id的数据 + data_time = convert_utc_to_bj(realValue['datadt']) # datetime + self.current_time_download_data[str(realValue['id'])] \ + = float(realValue['realValue']) # {id(str): value(float)} + if data_time > self.current_round_time.replace(tzinfo=None) - timedelta(minutes=5): # 下载数据为实时数据 + self.current_time_download_data_flag[str(realValue['id'])] = True + if newest_data_time is None: + newest_data_time = data_time + else: + newest_data_time = data_time if data_time > newest_data_time else newest_data_time # 更新最新时间 + if newest_data_time <= self.current_round_time.replace(tzinfo=None) - timedelta(minutes=5): # 最新记录时间早于当前时间 + warning_text = 'There is no current data with newest timestamp: {}.'.format( + newest_data_time.strftime('%Y-%m-%d %H:%M:%S')) + delta_time = self.current_round_time.replace(tzinfo=None) - newest_data_time + if delta_time < timedelta(minutes=PATTERN_TIME_STEP): # 时间接近(可等待再次下载) + raise Exception(warning_text) + else: + print(warning_text) + self.updating_data_flag = False + else: + for idx in monitor_unity_patterns[-3:]: # 出水管流量 + if self.current_time_download_data_flag[monitor_patterns_id[idx]] is False: # 无法获取出水管流量的实时数据 + print('There is no current data of outflow: {}.'.format(idx)) + self.updating_data_flag = False + if self.updating_data_flag is False: + print('Abandon updating data with downloaded data.') + return newest_data_time + else: + # 如果请求不成功,打印错误信息 + print("请求失败,状态码:", response.status_code) + raise ConnectionError('Cannot download data.') + + @ staticmethod + def init_dict_of_list(dict_of_list): + """初始化值为列表的字典(重新生成列表地址, 防止指向同一列表)""" + for idx in dict_of_list.keys(): + dict_of_list[idx] = dict_of_list[idx].copy() + return dict_of_list + + @ staticmethod + def get_download_id(): + """生成下载数据项的id""" + # id_list = (list(monitor_single_patterns_id.values()) + # + list(monitor_unity_patterns_id.values()) + # + list(tanks_id.values()) + # + list(reservoirs_id.values()) + # + list(pumps_id.values())) + id_list = (list(monitor_unity_patterns_id.values())[-3:] + + list(tanks_id.values()) + + list(reservoirs_id.values()) + + list(pumps_id.values())) + id_list = sorted(set(id_list), key=id_list.index) + if None in id_list: + id_list.remove(None) + return ','.join(id_list) + + @ staticmethod + def get_history_pattern_info(project_name, pattern_name): + """读取选定pattern的保存的历史pattern信息(flow, factor)""" + factors_list = [] + flow_list = [] + patterns_info = read_all(project_name, + f"select * from history_patterns_flows where id = '{pattern_name}' order by _order") + for item in patterns_info: + flow_list.append(float(item['flow'])) + factors_list.append(float(item['factor'])) + return flow_list, factors_list + + @ staticmethod + def judge_time(current_time, time_index_list): + """时间判断""" + current_index \ + = time_index_list.index(current_time) if (current_time in time_index_list) else None + return current_index + + @staticmethod + def get_time_index_list(start_time: datetime, end_time: datetime, step: int): + """生成时间索引""" + time_index_list = [] # 时间索引[str] + time_index = start_time + while time_index <= end_time: + time_index_list.append(time_index) + time_index += timedelta(minutes=step) + return time_index_list + + @ staticmethod + def round_time(time_: datetime, interval=5): + """时间向下取整到整n分钟(北京时间): 四舍六入五留双/向下取整""" + # return datetime.fromtimestamp(round(time_.timestamp() / (60 * interval)) * (60 * interval)) + return datetime.fromtimestamp(int((time_.timestamp()) // (60 * interval)) * (60 * interval)) + + +def convert_utc_to_bj(utc_time_str): + """将utc时间(str)转换成北京时间(datetime)""" + # 解析UTC时间字符串为datetime对象 + utc_time = datetime.strptime(utc_time_str, '%Y-%m-%dT%H:%M:%SZ') + # 设定UTC时区 + utc_timezone = pytz.timezone('UTC') + # 转换为北京时间 + beijing_timezone = pytz.timezone('Asia/Shanghai') + beijing_time = utc_time.replace(tzinfo=utc_timezone).astimezone(beijing_timezone).replace(tzinfo=None) + return beijing_time + + +def get_datetime(cur_datetime:str): + str_format = "%Y-%m-%d %H:%M:%S" + return datetime.strptime(cur_datetime, str_format) + + +def get_strftime(cur_datetime: datetime): + str_format = "%Y-%m-%d %H:%M:%S" + return cur_datetime.strftime(str_format) + + +def step_time(cur_datetime:str, step=5): + str_format="%Y-%m-%d %H:%M:%S" + dt=datetime.strptime(cur_datetime,str_format) + dt=dt+timedelta(minutes=step) + return datetime.strftime(dt,str_format) + + +def get_pattern_index(cur_datetime:str)->int: + str_format="%Y-%m-%d %H:%M:%S" + dt=datetime.strptime(cur_datetime,str_format) + hr=dt.hour + mnt=dt.minute + i=int((hr*60+mnt)/PATTERN_TIME_STEP) + return i + +def get_pattern_index_str(cur_datetime:str)->str: + i=get_pattern_index(cur_datetime) + [minN,hrN]=modf(i*PATTERN_TIME_STEP/60) + minN_str=str(int(minN*60)) + minN_str=minN_str.zfill(2) + hrN_str=str(int(hrN)) + hrN_str=hrN_str.zfill(2) + str_i='{}:{}:00'.format(hrN_str,minN_str) + return str_i + + +def from_seconds_to_clock (secs: int)->str: + hrs=int(secs/3600) + minutes=int((secs-hrs*3600)/60) + seconds=(secs-hrs*3600-minutes*60) + hrs_str=str(hrs).zfill(2) + minutes_str=str(minutes).zfill(2) + seconds_str=str(seconds).zfill(2) + str_clock='{}:{}:{}'.format(hrs_str,minutes_str,seconds_str) + return str_clock + +def from_clock_to_seconds (clock: str)->int: + str_format="%Y-%m-%d %H:%M:%S" + dt=datetime.strptime(clock,str_format) + hr=dt.hour + mnt=dt.minute + seconds=dt.second + return hr*3600+mnt*60+seconds + +def from_clock_to_seconds_2 (clock: str)->int: + str_format="%H:%M:%S" + dt=datetime.strptime(clock,str_format) + hr=dt.hour + mnt=dt.minute + seconds=dt.second + return hr*3600+mnt*60+seconds + + +def from_clock_to_seconds_3 (clock: str)->int: + str_format = "%H:%M" # 更新时间格式以适应 "小时:分钟" 格式 + dt = datetime.strptime(clock,str_format) + hr = dt.hour + mnt = dt.minute + seconds = dt.second + return hr * 3600 + mnt * 60 + + +###convert datetimestring +##"XXXX-XX-XXT00:00:00Z" ->"XXXX-XX-XX 00:00:00" +def trim_time_flag(url_date_time:str)->str: + str_datetime=str.replace(url_date_time,'T',' ') + str_datetime=str.replace(str_datetime,'Z','') + return str_datetime + +# 单时间步长模拟 +def run_simulation(name:str,start_datetime:str,end_datetime:str=None, duration:int=900)->str: + if(is_project_open(name)): + close_project(name) + open_project(name) + #get_current_data(cur_datetime) + #extract the patternindex from datetime + #e.g. 0: the first time step for 00:00-00:14; 1: the second step for 00:15-00:30 + start_datetime=trim_time_flag(start_datetime) + if(end_datetime!=None): + end_datetime=trim_time_flag(end_datetime) + + + + ## redistribute the basedemand according to the currentTotalQ and the base_totalQ + # step 1. get_real _data + if end_datetime==None or start_datetime==end_datetime: + end_datetime=step_time(start_datetime) + + # # ids=['2498','3854','3853','2510','2514','4780','4854'] + # # real_data=get_real_data(ids,start_datetime,end_datetime) + # print(datetime.now(pytz.timezone('Asia/Shanghai')).strftime("%Y-%m-%d %H:%M:%S")+"--获取实时数据完毕\n") + # #step 2. re-distribute the real q to base demand of the node region_sa by region_sa + # regions=get_all_service_area_ids(name) + # total_demands={} + # for region in regions: + # total_demands[region]=get_total_base_demand(name,region) + # region_demand_factor={} + # #Region_ID:SA_ZBBDJSCP000002 高区;SA_R00003+SA_ZBBDTJSC000001 低区 + # H_region_real_demands=real_data[DN_900_ID][start_datetime]+real_data[DN_500_ID][start_datetime] + # L_region_real_demands=real_data[DN_1000_ID][start_datetime] + # factor_H_zone=H_region_real_demands/total_demands[H_REGION_1]/3.6 #3.6: m3/h->L/s + # factor_L_zone=L_region_real_demands/(total_demands[L_REGION_1]+total_demands[L_REGION_2])/3.6 + # print(datetime.now(pytz.timezone('Asia/Shanghai')).strftime("%Y-%m-%d %H:%M:%S")+"--流量因子计算完毕完毕\n") + # for region in regions: + # region_nodes=get_nodes_in_region(name,region) + # factor=1 + # if region==H_REGION_1 or H_REGION_2: + # factor=factor_H_zone + # else: + # factor=factor_L_zone + # + # for node in region_nodes: + # d=get_demand(name,node) + # for r in d['demands']: + # r['demand']=factor*r['demand'] + # cs=ChangeSet() + # cs.append(d) + # set_demand(name,cs) + # + # # + # # + # print(datetime.now(pytz.timezone('Asia/Shanghai')).strftime("%Y-%m-%d %H:%M:%S")+"--节点流量重分配完毕\n") + + #step 3. set pattern index to the current time,and set duration to 300 secs + # + str_pattern_start=get_pattern_index_str(start_datetime) + dic_time=get_time(name) + dic_time['PATTERN START']=str_pattern_start + + if duration !=None: + dic_time['DURATION']=from_seconds_to_clock(duration) + else: + dic_time['DURATION']=dic_time['HYDRAULIC TIMESTEP'] + cs=ChangeSet() + cs.operations.append(dic_time) + set_time(name,cs) + + # step4. run simulation and save the result to name-time.out for download + #inp_file = 'inp\\'+name+'.inp' + #db_name=name + #dump_inp(db_name,inp_file,'2') + # result=run_inp(db_name) + result=run_project(name) + #json string format + # simulation_result, output, report + result_data=json.loads(result) + #print(result_data['simulation_result']) + print(datetime.now(pytz.timezone('Asia/Shanghai')).strftime("%Y-%m-%d %H:%M:%S")+'run finished successfully\n') + #print(result_data['report']) + return result + + +# 在线模拟 +def run_simulation_ex(name: str, simulation_type: str, start_datetime: str, + end_datetime: str = None, duration: int = 0, + pump_control: dict[str, list] = None, tank_initial_level_control: dict[str, float] = None, + region_demand_control: dict[str, float] = None, valve_control: dict[str, dict] = None, + downloading_prohibition: bool = False) -> str: + time_cost_start = time.perf_counter() + print('{} -- Hydraulic simulation started.'.format( + datetime.now(pytz.timezone('Asia/Shanghai')).strftime('%Y-%m-%d %H:%M:%S'))) + + if is_project_open(name): + close_project(name) + + if simulation_type.upper() == 'REALTIME': # 实时模拟(修改原数据库) + name_c = name + elif simulation_type.upper() == 'EXTENDED': # 扩展模拟(复制数据库) + name_c = '_'.join([name, 'c']) + if have_project(name_c): + if is_project_open(name_c): + close_project(name_c) + delete_project(name_c) + copy_project(name, name_c) # 备份项目 + else: + raise Exception('Incorrect simulation type, choose in (realtime, extended)') + + open_project(name_c) + + # 时间处理 + # extract the pattern index from datetime + # e.g. 0: the first time step for 00:00-00:14; 1: the second step for 00:15-00:30 + # start_datetime = get_strftime(convert_utc_to_bj(start_datetime)) + start_datetime = trim_time_flag(start_datetime) + if end_datetime is not None: + # end_datetime = get_strftime(convert_utc_to_bj(end_datetime)) + end_datetime = trim_time_flag(end_datetime) + + # pump name转化/输入值规范化 + if pump_control is not None: + for key in list(pump_control.keys()): + pump_control[key] = [pump_control[key]] if type(pump_control[key]) is not list else pump_control[key] + pump_control[pumps[pumps_name.index(key)]] = pump_control.pop(key) + + # 重新分配节点(nodes)水量 + # 1) (single)base_demand_new=1, pattern_new=real_data + # 2) (unity)base_demand_new=base_demand_old, pattern_new=factor*pattern_old(factor=flow_new/flow_old) + # 获取需水量数据 + # a) 历史pattern对应水量(读取保存数据库) + # b) 实时水量(数据接口下载) + + # 修改node demand = 1, pattern factor *= demand(monitor single patterns对应node) + # nodes = get_nodes(name_c) # nodes + # for node_name in nodes: # 遍历nodes + # demands_dict = get_demand(name_c, node_name) # {'demands':[{'demand':, 'pattern':}]} + # for demands in demands_dict['demands']: + # if (demands['pattern'] in monitor_single_patterns) and (demands['demand'] != 1): # 1) + # pattern = get_pattern(name_c, demands['pattern']) + # pattern['factors'] = list(demands['demand'] * np.array(pattern['factors'])) # 修改pattern + # cs = ChangeSet() + # cs.append(pattern) + # set_pattern(name_c, cs) + # demands_dict['demands'][ + # demands_dict['demands'].index(demands) + # ]['demand'] = 1 # 修改demand + # cs = ChangeSet() + # cs.append(demands_dict) + # set_demand(name_c, cs) + + start_time = get_datetime(start_datetime) # datetime + end_time = get_datetime(end_datetime) \ + if end_datetime is not None \ + else get_datetime(start_datetime) + timedelta(seconds=duration) # datetime + # modify_pattern_start_index = get_pattern_index(start_datetime) # 待修改pattern的起始索引(int) + + dataset_loader = DataLoader(project_name=name_c, + start_time=start_time, end_time=end_time, + pumps_control=pump_control, tank_initial_level_control=tank_initial_level_control, + region_demand_control=region_demand_control, + downloading_prohibition=downloading_prohibition) # 实例化数据加载器 + modify_index \ + = dataset_loader.load_data() # 加载数据(index: 需要修改pattern的factor index, None: 无需修改除水泵和调节池外pattern) + new_patterns \ + = dataset_loader.new_pattern_factor # {name: float,} pattern factor(实时: 更新, 其他: 保持/更新(设定用水量时)) + tank_init_level = dataset_loader.tank_data # {name: float,} 调节池初始液位(实时: 更新, 其他: 保持/更新(设定液位时)) + reservoir_level = dataset_loader.reservoir_data # {name: float,} 水库液位(实时: 更新, 其他: 保持) + pump_freq = dataset_loader.pump_data # {name: [float,]} 水泵频率(实时: 更新, 其他: 保持/更新(设定状态时)) + print(datetime.now(pytz.timezone('Asia/Shanghai')).strftime("%Y-%m-%d %H:%M:%S") + " -- Loading data ok.\n") + + pattern_name_list = get_patterns(name_c) # 所有pattern + # 修改node pattern/demand + # nodes = get_nodes(name_c) # nodes + # for node_name in nodes: # 遍历nodes + # demands_dict = get_demand(name_c, node_name) # {'demands':[{'demand':, 'pattern':}]} + # for demands in demands_dict['demands']: + # if demands['pattern'] in monitor_single_patterns: # 1) + # demands_dict['demands'][ + # demands_dict['demands'].index(demands) + # ]['demand'] = 1 # 修改demand + # pattern = get_pattern(name_c, demands['pattern']) + # pattern['factors'][modify_index] = flow_new[demands['pattern']] # 修改pattern + # cs = ChangeSet() + # cs.append(pattern) + # set_pattern(name_c, cs) + # if demands['pattern'] in pattern_name_list: + # pattern_name_list.remove(demands['pattern']) # 移出待修改pattern列表 + # else: # 2) + # continue + # cs = ChangeSet() + # cs.append(demands_dict) + # set_demand(name_c, cs) + for pattern_name in monitor_patterns: # 遍历patterns + if not np.isnan(new_patterns[pattern_name][0]): + pattern = get_pattern(name_c, pattern_name) + pattern['factors'][modify_index: + modify_index + len(new_patterns[pattern_name])] \ + = new_patterns[pattern_name] + cs = ChangeSet() + cs.append(pattern) + set_pattern(name_c, cs) + if pattern_name in pattern_name_list: + pattern_name_list.remove(pattern_name) # 移出待修改pattern列表 + + # 修改清水池(reservoir)液位pattern + for reservoir_name in reservoirs: # 遍历reservoirs + if (not np.isnan(reservoir_level[reservoir_name])) and (reservoir_level[reservoir_name] != 0): + reservoir_pattern = get_pattern(name_c, get_reservoir(name_c, reservoir_name)['pattern']) + reservoir_pattern['factors'][modify_index] = reservoir_level[reservoir_name] + cs = ChangeSet() + cs.append(reservoir_pattern) + set_pattern(name_c, cs) + if reservoir_pattern['id'] in pattern_name_list: + pattern_name_list.remove(reservoir_pattern['id']) # 移出待修改pattern列表 + + # 修改调节池(tank)初始液位 + for tank_name in tanks: # 遍历tanks + if (not np.isnan(tank_init_level[tank_name])) and (tank_init_level[tank_name] != 0): + tank = get_tank(name_c, tank_name) + tank['init_level'] = tank_init_level[tank_name] + cs = ChangeSet() + cs.append(tank) + set_tank(name_c, cs) + + # 修改水泵(pump)pattern + for pump_name in pumps: # 遍历pumps + if not np.isnan(pump_freq[pump_name][0]): + pump_pattern = get_pattern(name_c, get_pump(name_c, pump_name)['pattern']) + pump_pattern['factors'][modify_index + :modify_index + len(pump_freq[pump_name])] \ + = pump_freq[pump_name] + cs = ChangeSet() + cs.append(pump_pattern) + set_pattern(name_c, cs) + if pump_pattern['id'] in pattern_name_list: + pattern_name_list.remove(pump_pattern['id']) # 移出待修改pattern列表 + # 修改阀门(valve)status和setting + if valve_control is not None: + for valve in valve_control.keys(): + status = get_status(name_c, valve) + if 'status' in valve_control[valve].keys(): + status['status'] = valve_control[valve]['status'] + if 'setting' in valve_control[valve].keys(): + status['setting'] = valve_control[valve]['setting'] + if 'k' in valve_control[valve].keys(): + valve_k = valve_control[valve]['k'] + if valve_k == 0: + status['status'] = 'CLOSED' + else: + status['setting'] = 0.1036 * pow(valve_k, -3.105) + cs = ChangeSet() + cs.append(status) + set_status(name_c, cs) + + print('Finish demands amending, unmodified patterns: {}.'.format(pattern_name_list)) + + # 修改时间信息 + str_pattern_start = get_pattern_index_str( + DataLoader.round_time(start_time, int(PATTERN_TIME_STEP)).strftime("%Y-%m-%d %H:%M:%S")) + dic_time = get_time(name_c) + dic_time['PATTERN START'] = str_pattern_start + + if duration is not None: + dic_time['DURATION'] = from_seconds_to_clock(duration) + else: + dic_time['DURATION'] = dic_time['HYDRAULIC TIMESTEP'] + cs = ChangeSet() + cs.operations.append(dic_time) + set_time(name_c, cs) + + # 运行并返回结果 + result = run_project(name_c) + + time_cost_end = time.perf_counter() + print('{} -- Hydraulic simulation finished, cost time: {:.2f} s.'.format( + datetime.now(pytz.timezone('Asia/Shanghai')).strftime('%Y-%m-%d %H:%M:%S'), + time_cost_end - time_cost_start)) + + close_project(name_c) + + return result + + +if __name__ == '__main__': + # if get_current_data()==True: + # tQ=get_current_total_Q() + # print(f"the current tQ is {tQ}\n") + # data=get_hist_data(ids,conver_beingtime_to_ucttime('2024-04-10 15:05:00'),conver_beingtime_to_ucttime('2024-04-10 15:10:00')) + # open_project("beibeizone") + # read_inp("beibeizone","beibeizone-export_nochinese.inp") + # run_simulation("beibeizone","2024-04-01T08:00:00Z") + # read_inp('bb_server', 'model20_en.inp') + run_simulation_ex( + name='bb', simulation_type='extended', start_datetime='2024-11-09T02:30:00Z', + # end_datetime='2024-05-30T16:00:00Z', + # duration=0, + # pump_control={'PU00006': [45, 40]} + # region_demand_control={'hp': 6000, 'lp': 2000} + )