Files
TJWaterServer/run_simulation.py
2025-02-07 23:26:52 +08:00

857 lines
43 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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}
)