refactor(db)!: finalize pooled WNDB v2 migration
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
"""Service package.
|
||||
|
||||
Keep package initialization lightweight. Import concrete service modules directly,
|
||||
for example: `from app.services.tjnetwork import open_project`.
|
||||
for example: `from app.services.tjnetwork import get_junction`.
|
||||
"""
|
||||
|
||||
+291
-264
@@ -9,7 +9,6 @@ from app.services.tjnetwork import (
|
||||
get_status,
|
||||
get_tank,
|
||||
get_time,
|
||||
open_project,
|
||||
read_all,
|
||||
run_project,
|
||||
set_demand,
|
||||
@@ -34,7 +33,8 @@ import logging
|
||||
import app.services.globals as globals
|
||||
import app.services.project_info as project_info
|
||||
from app.services.time_api import parse_beijing_time, parse_clock_duration_seconds
|
||||
from app.native.wndb.core.connection import project_connection
|
||||
from app.native.wndb.core.connection import project_connection, project_transaction
|
||||
from app.native.wndb.core.database import refresh_materialized_views_after_commit
|
||||
from app.infra.db.timescaledb.internal_queries import (
|
||||
InternalQueries as TimescaleInternalQueries,
|
||||
)
|
||||
@@ -48,6 +48,31 @@ logging.basicConfig(
|
||||
)
|
||||
|
||||
|
||||
def _primary_demand(demand_set: dict) -> dict:
|
||||
"""Return sequence-zero demand, creating it when a junction has none."""
|
||||
demands = demand_set.setdefault("demands", [])
|
||||
if not demands:
|
||||
demands.append({"demand": 0.0, "pattern": None, "category": None})
|
||||
return demands[0]
|
||||
|
||||
|
||||
def _primary_demand_pattern(demand_set: dict) -> str:
|
||||
"""Return the first configured pattern in deterministic sequence order."""
|
||||
pattern = next(
|
||||
(
|
||||
demand.get("pattern")
|
||||
for demand in demand_set.get("demands", [])
|
||||
if demand.get("pattern")
|
||||
),
|
||||
None,
|
||||
)
|
||||
if pattern is None:
|
||||
raise ValueError(
|
||||
f"Junction {demand_set.get('junction')!r} has no demand pattern"
|
||||
)
|
||||
return str(pattern)
|
||||
|
||||
|
||||
def query_corresponding_element_id_and_query_id(name: str) -> None:
|
||||
"""Load realtime device-to-element mappings from the new asset schema."""
|
||||
target_maps = {
|
||||
@@ -221,281 +246,283 @@ def run_simulation(
|
||||
# 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)')
|
||||
name_c = name
|
||||
# 打开数据库
|
||||
open_project(name_c)
|
||||
dic_time = get_time(name_c)
|
||||
with project_transaction(name_c):
|
||||
dic_time = get_time(name_c)
|
||||
|
||||
print(dic_time)
|
||||
print(dic_time)
|
||||
|
||||
# 获取水力模拟步长,如’0:15:00‘
|
||||
globals.hydraulic_timestep = dic_time["HYDRAULIC TIMESTEP"]
|
||||
# 转换为分钟浮点数,兼容 EPANET 的 H:MM 和 H:MM:SS 写法
|
||||
globals.PATTERN_TIME_STEP = (
|
||||
parse_clock_duration_seconds(
|
||||
globals.hydraulic_timestep,
|
||||
field_name="HYDRAULIC TIMESTEP",
|
||||
# 获取水力模拟步长,如’0:15:00‘
|
||||
globals.hydraulic_timestep = dic_time["HYDRAULIC TIMESTEP"]
|
||||
# 转换为分钟浮点数,兼容 EPANET 的 H:MM 和 H:MM:SS 写法
|
||||
globals.PATTERN_TIME_STEP = (
|
||||
parse_clock_duration_seconds(
|
||||
globals.hydraulic_timestep,
|
||||
field_name="HYDRAULIC TIMESTEP",
|
||||
)
|
||||
/ 60
|
||||
)
|
||||
/ 60
|
||||
)
|
||||
# 对输入的时间参数进行处理
|
||||
pattern_start_time = convert_time_format(modify_pattern_start_time)
|
||||
# 获取模拟开始时间是对应pattern的第几个数
|
||||
modify_index = get_pattern_index(pattern_start_time)
|
||||
# 遍历水泵的pattern_id,并根据输入的pump_pattern修改pattern的值
|
||||
# for pump_pattern_id in pump_pattern_ids:
|
||||
# # 检查pump_pattern中pump_pattern_id对应的第一个频率值是否为有效数字(非空、非NaN)。如果该值有效,则继续执行代码块。
|
||||
# if not np.isnan(modify_pump_pattern[pump_pattern_id][0]):
|
||||
# # 取出数据库中的pattern
|
||||
# pump_pattern = get_pattern(name_c, get_pump(name_c, pump_pattern_id)['pattern'])
|
||||
# # 替换数据库中的pattern为modify_pump_pattern
|
||||
# pump_pattern['factors'][modify_index: modify_index + len(modify_pump_pattern[pump_pattern_id])] \
|
||||
# = modify_pump_pattern[pump_pattern_id]
|
||||
# cs = ChangeSet()
|
||||
# cs.append(pump_pattern)
|
||||
# set_pattern(name_c, cs)
|
||||
# 修改模拟开始的时间
|
||||
str_pattern_start = get_pattern_index_str(
|
||||
convert_time_format(modify_pattern_start_time)
|
||||
)
|
||||
dic_time = get_time(name_c)
|
||||
dic_time["PATTERN START"] = str_pattern_start
|
||||
dic_time["DURATION"] = from_seconds_to_clock(modify_total_duration)
|
||||
if simulation_type.upper() == "REALTIME":
|
||||
dic_time["DURATION"] = 0
|
||||
cs = ChangeSet()
|
||||
cs.operations.append(dic_time)
|
||||
set_time(name_c, cs)
|
||||
# 根据SCADA实时数据进行修改,如果没有对应的SCADA数据,如未来的时间点,则不改变pg数据库的数据
|
||||
if globals.reservoirs_id:
|
||||
# reservoirs_id = {'ZBBDJSCP000002': '2497', 'R00003': '2571'}
|
||||
# 1.获取reservoir的SCADA数据,形式如{'2497': '3.1231', '2571': '2.7387'}
|
||||
reservoir_SCADA_data_dict = TimescaleInternalQueries.query_scada_by_ids_time(
|
||||
device_ids=list(globals.reservoirs_id.values()),
|
||||
query_time=modify_pattern_start_time,
|
||||
db_name=name,
|
||||
# 对输入的时间参数进行处理
|
||||
pattern_start_time = convert_time_format(modify_pattern_start_time)
|
||||
# 获取模拟开始时间是对应pattern的第几个数
|
||||
modify_index = get_pattern_index(pattern_start_time)
|
||||
# 遍历水泵的pattern_id,并根据输入的pump_pattern修改pattern的值
|
||||
# for pump_pattern_id in pump_pattern_ids:
|
||||
# # 检查pump_pattern中pump_pattern_id对应的第一个频率值是否为有效数字(非空、非NaN)。如果该值有效,则继续执行代码块。
|
||||
# if not np.isnan(modify_pump_pattern[pump_pattern_id][0]):
|
||||
# # 取出数据库中的pattern
|
||||
# pump_pattern = get_pattern(name_c, get_pump(name_c, pump_pattern_id)['pattern'])
|
||||
# # 替换数据库中的pattern为modify_pump_pattern
|
||||
# pump_pattern['factors'][modify_index: modify_index + len(modify_pump_pattern[pump_pattern_id])] \
|
||||
# = modify_pump_pattern[pump_pattern_id]
|
||||
# cs = ChangeSet()
|
||||
# cs.append(pump_pattern)
|
||||
# set_pattern(name_c, cs)
|
||||
# 修改模拟开始的时间
|
||||
str_pattern_start = get_pattern_index_str(
|
||||
convert_time_format(modify_pattern_start_time)
|
||||
)
|
||||
# 2.构建出新字典,形式如{'ZBBDJSCP000002': '3.1231', 'R00003': '2.7387'}
|
||||
reservoir_dict = {
|
||||
key: reservoir_SCADA_data_dict[value]
|
||||
for key, value in globals.reservoirs_id.items()
|
||||
}
|
||||
# 3.修改reservoir液位模式
|
||||
for reservoir_name, value in reservoir_dict.items():
|
||||
if value and float(value) != 0:
|
||||
# 先根据reservoir获取对应的pattern,再对pattern进行修改
|
||||
reservoir_pattern = get_pattern(
|
||||
name_c, get_reservoir(name_c, reservoir_name)["pattern"]
|
||||
)
|
||||
reservoir_pattern["factors"][modify_index] = (
|
||||
float(value) + globals.RESERVOIR_BASIC_HEIGHT
|
||||
)
|
||||
cs = ChangeSet()
|
||||
cs.append(reservoir_pattern)
|
||||
set_pattern(name_c, cs)
|
||||
if globals.tanks_id:
|
||||
# 修改tank初始液位
|
||||
tank_SCADA_data_dict = TimescaleInternalQueries.query_scada_by_ids_time(
|
||||
device_ids=list(globals.tanks_id.values()),
|
||||
query_time=modify_pattern_start_time,
|
||||
db_name=name,
|
||||
)
|
||||
tank_dict = {
|
||||
key: tank_SCADA_data_dict[value] for key, value in globals.tanks_id.items()
|
||||
}
|
||||
for tank_name, value in tank_dict.items():
|
||||
if value and float(value) != 0:
|
||||
tank = get_tank(name_c, tank_name)
|
||||
tank["init_level"] = float(value)
|
||||
cs = ChangeSet()
|
||||
cs.append(tank)
|
||||
set_tank(name_c, cs)
|
||||
if globals.fixed_pumps_id:
|
||||
# 修改工频泵的pattern
|
||||
fixed_pump_SCADA_data_dict = TimescaleInternalQueries.query_scada_by_ids_time(
|
||||
device_ids=list(globals.fixed_pumps_id.values()),
|
||||
query_time=modify_pattern_start_time,
|
||||
db_name=name,
|
||||
)
|
||||
# print(fixed_pump_SCADA_data_dict)
|
||||
fixed_pump_dict = {
|
||||
key: fixed_pump_SCADA_data_dict[value]
|
||||
for key, value in globals.fixed_pumps_id.items()
|
||||
}
|
||||
# print(fixed_pump_dict)
|
||||
for fixed_pump_name, value in fixed_pump_dict.items():
|
||||
if value:
|
||||
pump_pattern = get_pattern(
|
||||
name_c, get_pump(name_c, fixed_pump_name)["pattern"]
|
||||
)
|
||||
# print(pump_pattern)
|
||||
pump_pattern["factors"][modify_index] = float(value)
|
||||
# print(pump_pattern['factors'][modify_index])
|
||||
cs = ChangeSet()
|
||||
cs.append(pump_pattern)
|
||||
set_pattern(name_c, cs)
|
||||
if globals.variable_pumps_id:
|
||||
# 修改变频泵的pattern
|
||||
variable_pump_SCADA_data_dict = (
|
||||
TimescaleInternalQueries.query_scada_by_ids_time(
|
||||
device_ids=list(globals.variable_pumps_id.values()),
|
||||
dic_time = get_time(name_c)
|
||||
dic_time["PATTERN START"] = str_pattern_start
|
||||
dic_time["DURATION"] = from_seconds_to_clock(modify_total_duration)
|
||||
if simulation_type.upper() == "REALTIME":
|
||||
dic_time["DURATION"] = 0
|
||||
cs = ChangeSet()
|
||||
cs.operations.append(dic_time)
|
||||
set_time(name_c, cs)
|
||||
# 根据SCADA实时数据进行修改,如果没有对应的SCADA数据,如未来的时间点,则不改变pg数据库的数据
|
||||
if globals.reservoirs_id:
|
||||
# reservoirs_id = {'ZBBDJSCP000002': '2497', 'R00003': '2571'}
|
||||
# 1.获取reservoir的SCADA数据,形式如{'2497': '3.1231', '2571': '2.7387'}
|
||||
reservoir_SCADA_data_dict = TimescaleInternalQueries.query_scada_by_ids_time(
|
||||
device_ids=list(globals.reservoirs_id.values()),
|
||||
query_time=modify_pattern_start_time,
|
||||
db_name=name,
|
||||
)
|
||||
)
|
||||
variable_pump_dict = {
|
||||
key: variable_pump_SCADA_data_dict[value]
|
||||
for key, value in globals.variable_pumps_id.items()
|
||||
}
|
||||
for variable_pump_name, value in variable_pump_dict.items():
|
||||
if value:
|
||||
pump_pattern = get_pattern(
|
||||
name_c, get_pump(name_c, variable_pump_name)["pattern"]
|
||||
)
|
||||
pump_pattern["factors"][modify_index] = float(value) / 50
|
||||
cs = ChangeSet()
|
||||
cs.append(pump_pattern)
|
||||
set_pattern(name_c, cs)
|
||||
if globals.demand_id:
|
||||
# 基于实时数据,修改大用户节点的pattern
|
||||
demand_SCADA_data_dict = TimescaleInternalQueries.query_scada_by_ids_time(
|
||||
device_ids=list(globals.demand_id.values()),
|
||||
query_time=modify_pattern_start_time,
|
||||
db_name=name,
|
||||
)
|
||||
demand_dict = {
|
||||
key: demand_SCADA_data_dict[value]
|
||||
for key, value in globals.demand_id.items()
|
||||
}
|
||||
for demand_name, value in demand_dict.items():
|
||||
if value:
|
||||
demand_pattern = get_pattern(
|
||||
name_c, get_demand(name_c, demand_name)["pattern"]
|
||||
)
|
||||
if get_option(name_c)["UNITS"] == "LPS":
|
||||
demand_pattern["factors"][modify_index] = (
|
||||
float(value) / 3.6
|
||||
) # 默认SCADA数据获取的是流量单位是m3/h, 转换为 L/s
|
||||
elif get_option(name_c)["UNITS"] == "CMH":
|
||||
demand_pattern["factors"][modify_index] = float(value)
|
||||
cs = ChangeSet()
|
||||
cs.append(demand_pattern)
|
||||
set_pattern(name_c, cs)
|
||||
# 水质、压力实时数据使用方法待补充
|
||||
#############################
|
||||
# 显式请求参数覆盖实时设备数据,用于扩展模拟。
|
||||
# 修改清水池(reservoir)液位的pattern
|
||||
if modify_reservoir_head_pattern:
|
||||
for reservoir_name in modify_reservoir_head_pattern.keys():
|
||||
# 这句代码的作用是判断modify_reservoir_head_pattern[reservoir_name][0]是否不是NaN。
|
||||
# 如果modify_reservoir_head_pattern[reservoir_name][0]不是NaN,则条件成立,代码块会执行
|
||||
if not np.isnan(modify_reservoir_head_pattern[reservoir_name][0]):
|
||||
# 给 list 中的所有元素加上 RESERVOIR_BASIC_HEIGHT
|
||||
modified_values = [
|
||||
value + globals.RESERVOIR_BASIC_HEIGHT
|
||||
for value in modify_reservoir_head_pattern[reservoir_name]
|
||||
]
|
||||
reservoir_pattern = get_pattern(
|
||||
name_c, get_reservoir(name_c, reservoir_name)["pattern"]
|
||||
)
|
||||
reservoir_pattern["factors"][
|
||||
modify_index : modify_index + len(modified_values)
|
||||
] = modified_values
|
||||
cs = ChangeSet()
|
||||
cs.append(reservoir_pattern)
|
||||
set_pattern(name_c, cs)
|
||||
# 修改调节池(tank)初始液位
|
||||
if modify_tank_initial_level:
|
||||
for tank_name in modify_tank_initial_level.keys():
|
||||
if (not np.isnan(modify_tank_initial_level[tank_name])) and (
|
||||
modify_tank_initial_level[tank_name] != 0
|
||||
):
|
||||
tank = get_tank(name_c, tank_name)
|
||||
tank["init_level"] = modify_tank_initial_level[tank_name]
|
||||
cs = ChangeSet()
|
||||
cs.append(tank)
|
||||
set_tank(name_c, cs)
|
||||
# 修改节点(junction)基础水量(demand)
|
||||
if modify_junction_base_demand:
|
||||
for junction_name in modify_junction_base_demand.keys():
|
||||
if not np.isnan(modify_junction_base_demand[junction_name]):
|
||||
junction = get_demand(name_c, junction_name)
|
||||
junction["demand"] = modify_junction_base_demand[junction_name]
|
||||
cs = ChangeSet()
|
||||
cs.append(junction)
|
||||
set_demand(name_c, cs)
|
||||
# 修改节点(junction)的水量模式(pattern)
|
||||
if modify_junction_damand_pattern:
|
||||
for pattern_name in modify_junction_damand_pattern.keys():
|
||||
if not np.isnan(modify_junction_damand_pattern[pattern_name][0]):
|
||||
junction_pattern = get_pattern(name_c, pattern_name)
|
||||
junction_pattern["factors"][
|
||||
modify_index : modify_index
|
||||
+ len(modify_junction_damand_pattern[pattern_name])
|
||||
] = modify_junction_damand_pattern[pattern_name]
|
||||
cs = ChangeSet()
|
||||
cs.append(junction_pattern)
|
||||
set_pattern(name_c, cs)
|
||||
# 修改工频水泵(fixed_pump)的pattern
|
||||
if modify_fixed_pump_pattern:
|
||||
for pump_name in modify_fixed_pump_pattern.keys():
|
||||
if not np.isnan(modify_fixed_pump_pattern[pump_name][0]):
|
||||
pump_pattern = get_pattern(
|
||||
name_c, get_pump(name_c, pattern_name)["pattern"]
|
||||
)
|
||||
pump_pattern["factors"][
|
||||
modify_index : modify_index + len(modify_fixed_pump_pattern)
|
||||
] = modify_fixed_pump_pattern[pump_name]
|
||||
cs = ChangeSet()
|
||||
cs.append(pump_pattern)
|
||||
set_pattern(name_c, cs)
|
||||
# 修改变频水泵(variable_pump)的pattern
|
||||
if modify_variable_pump_pattern:
|
||||
for pump_name in modify_variable_pump_pattern.keys():
|
||||
if not np.isnan(modify_variable_pump_pattern[pump_name][0]):
|
||||
# 给 list 中的所有元素除以 50Hz
|
||||
modified_values = [
|
||||
value / 50 for value in modify_variable_pump_pattern[pump_name]
|
||||
]
|
||||
pump_pattern = get_pattern(
|
||||
name_c, get_pump(name_c, pattern_name)["pattern"]
|
||||
)
|
||||
pump_pattern["factors"][
|
||||
modify_index : modify_index + len(modified_values)
|
||||
] = modified_values
|
||||
cs = ChangeSet()
|
||||
cs.append(pump_pattern)
|
||||
set_pattern(name_c, cs)
|
||||
# 显式阀门控制沿用 run_simulation_ex 的处理顺序和覆盖规则。
|
||||
if valve_control is not None:
|
||||
_apply_valve_control(name_c, valve_control)
|
||||
# 保留原开度参数逻辑,兼容现有方案调用。
|
||||
elif modify_valve_opening:
|
||||
for valve_name in modify_valve_opening.keys():
|
||||
if not np.isnan(modify_valve_opening[valve_name]):
|
||||
valve_status = get_status(name_c, valve_name)
|
||||
if modify_valve_opening[valve_name] == 0:
|
||||
valve_status["status"] = "CLOSED"
|
||||
valve_status["setting"] = 0
|
||||
elif modify_valve_opening[valve_name] < 1:
|
||||
valve_status["status"] = "OPEN"
|
||||
valve_status["setting"] = 0.1036 * pow(
|
||||
modify_valve_opening[valve_name], -3.105
|
||||
# 2.构建出新字典,形式如{'ZBBDJSCP000002': '3.1231', 'R00003': '2.7387'}
|
||||
reservoir_dict = {
|
||||
key: reservoir_SCADA_data_dict[value]
|
||||
for key, value in globals.reservoirs_id.items()
|
||||
}
|
||||
# 3.修改reservoir液位模式
|
||||
for reservoir_name, value in reservoir_dict.items():
|
||||
if value and float(value) != 0:
|
||||
# 先根据reservoir获取对应的pattern,再对pattern进行修改
|
||||
reservoir_pattern = get_pattern(
|
||||
name_c, get_reservoir(name_c, reservoir_name)["pattern"]
|
||||
)
|
||||
elif modify_valve_opening[valve_name] == 1:
|
||||
valve_status["status"] = "OPEN"
|
||||
valve_status["setting"] = 0
|
||||
cs = ChangeSet()
|
||||
cs.append(valve_status)
|
||||
set_status(name_c, cs)
|
||||
# 运行并返回结果
|
||||
result_data = json.loads(run_project(name_c))
|
||||
reservoir_pattern["factors"][modify_index] = (
|
||||
float(value) + globals.RESERVOIR_BASIC_HEIGHT
|
||||
)
|
||||
cs = ChangeSet()
|
||||
cs.append(reservoir_pattern)
|
||||
set_pattern(name_c, cs)
|
||||
if globals.tanks_id:
|
||||
# 修改tank初始液位
|
||||
tank_SCADA_data_dict = TimescaleInternalQueries.query_scada_by_ids_time(
|
||||
device_ids=list(globals.tanks_id.values()),
|
||||
query_time=modify_pattern_start_time,
|
||||
db_name=name,
|
||||
)
|
||||
tank_dict = {
|
||||
key: tank_SCADA_data_dict[value] for key, value in globals.tanks_id.items()
|
||||
}
|
||||
for tank_name, value in tank_dict.items():
|
||||
if value and float(value) != 0:
|
||||
tank = get_tank(name_c, tank_name)
|
||||
tank["init_level"] = float(value)
|
||||
cs = ChangeSet()
|
||||
cs.append(tank)
|
||||
set_tank(name_c, cs)
|
||||
if globals.fixed_pumps_id:
|
||||
# 修改工频泵的pattern
|
||||
fixed_pump_SCADA_data_dict = TimescaleInternalQueries.query_scada_by_ids_time(
|
||||
device_ids=list(globals.fixed_pumps_id.values()),
|
||||
query_time=modify_pattern_start_time,
|
||||
db_name=name,
|
||||
)
|
||||
# print(fixed_pump_SCADA_data_dict)
|
||||
fixed_pump_dict = {
|
||||
key: fixed_pump_SCADA_data_dict[value]
|
||||
for key, value in globals.fixed_pumps_id.items()
|
||||
}
|
||||
# print(fixed_pump_dict)
|
||||
for fixed_pump_name, value in fixed_pump_dict.items():
|
||||
if value:
|
||||
pump_pattern = get_pattern(
|
||||
name_c, get_pump(name_c, fixed_pump_name)["pattern"]
|
||||
)
|
||||
# print(pump_pattern)
|
||||
pump_pattern["factors"][modify_index] = float(value)
|
||||
# print(pump_pattern['factors'][modify_index])
|
||||
cs = ChangeSet()
|
||||
cs.append(pump_pattern)
|
||||
set_pattern(name_c, cs)
|
||||
if globals.variable_pumps_id:
|
||||
# 修改变频泵的pattern
|
||||
variable_pump_SCADA_data_dict = (
|
||||
TimescaleInternalQueries.query_scada_by_ids_time(
|
||||
device_ids=list(globals.variable_pumps_id.values()),
|
||||
query_time=modify_pattern_start_time,
|
||||
db_name=name,
|
||||
)
|
||||
)
|
||||
variable_pump_dict = {
|
||||
key: variable_pump_SCADA_data_dict[value]
|
||||
for key, value in globals.variable_pumps_id.items()
|
||||
}
|
||||
for variable_pump_name, value in variable_pump_dict.items():
|
||||
if value:
|
||||
pump_pattern = get_pattern(
|
||||
name_c, get_pump(name_c, variable_pump_name)["pattern"]
|
||||
)
|
||||
pump_pattern["factors"][modify_index] = float(value) / 50
|
||||
cs = ChangeSet()
|
||||
cs.append(pump_pattern)
|
||||
set_pattern(name_c, cs)
|
||||
if globals.demand_id:
|
||||
# 基于实时数据,修改大用户节点的pattern
|
||||
demand_SCADA_data_dict = TimescaleInternalQueries.query_scada_by_ids_time(
|
||||
device_ids=list(globals.demand_id.values()),
|
||||
query_time=modify_pattern_start_time,
|
||||
db_name=name,
|
||||
)
|
||||
demand_dict = {
|
||||
key: demand_SCADA_data_dict[value]
|
||||
for key, value in globals.demand_id.items()
|
||||
}
|
||||
for demand_name, value in demand_dict.items():
|
||||
if value is not None and not np.isnan(float(value)):
|
||||
demand_set = get_demand(name_c, demand_name)
|
||||
demand_pattern = get_pattern(
|
||||
name_c, _primary_demand_pattern(demand_set)
|
||||
)
|
||||
if get_option(name_c)["UNITS"] == "LPS":
|
||||
demand_pattern["factors"][modify_index] = (
|
||||
float(value) / 3.6
|
||||
) # 默认SCADA数据获取的是流量单位是m3/h, 转换为 L/s
|
||||
elif get_option(name_c)["UNITS"] == "CMH":
|
||||
demand_pattern["factors"][modify_index] = float(value)
|
||||
cs = ChangeSet()
|
||||
cs.append(demand_pattern)
|
||||
set_pattern(name_c, cs)
|
||||
# 水质、压力实时数据使用方法待补充
|
||||
#############################
|
||||
# 显式请求参数覆盖实时设备数据,用于扩展模拟。
|
||||
# 修改清水池(reservoir)液位的pattern
|
||||
if modify_reservoir_head_pattern:
|
||||
for reservoir_name in modify_reservoir_head_pattern.keys():
|
||||
# 这句代码的作用是判断modify_reservoir_head_pattern[reservoir_name][0]是否不是NaN。
|
||||
# 如果modify_reservoir_head_pattern[reservoir_name][0]不是NaN,则条件成立,代码块会执行
|
||||
if not np.isnan(modify_reservoir_head_pattern[reservoir_name][0]):
|
||||
# 给 list 中的所有元素加上 RESERVOIR_BASIC_HEIGHT
|
||||
modified_values = [
|
||||
value + globals.RESERVOIR_BASIC_HEIGHT
|
||||
for value in modify_reservoir_head_pattern[reservoir_name]
|
||||
]
|
||||
reservoir_pattern = get_pattern(
|
||||
name_c, get_reservoir(name_c, reservoir_name)["pattern"]
|
||||
)
|
||||
reservoir_pattern["factors"][
|
||||
modify_index : modify_index + len(modified_values)
|
||||
] = modified_values
|
||||
cs = ChangeSet()
|
||||
cs.append(reservoir_pattern)
|
||||
set_pattern(name_c, cs)
|
||||
# 修改调节池(tank)初始液位
|
||||
if modify_tank_initial_level:
|
||||
for tank_name in modify_tank_initial_level.keys():
|
||||
if (not np.isnan(modify_tank_initial_level[tank_name])) and (
|
||||
modify_tank_initial_level[tank_name] != 0
|
||||
):
|
||||
tank = get_tank(name_c, tank_name)
|
||||
tank["init_level"] = modify_tank_initial_level[tank_name]
|
||||
cs = ChangeSet()
|
||||
cs.append(tank)
|
||||
set_tank(name_c, cs)
|
||||
# 修改节点(junction)基础水量(demand)
|
||||
if modify_junction_base_demand:
|
||||
for junction_name in modify_junction_base_demand.keys():
|
||||
if not np.isnan(modify_junction_base_demand[junction_name]):
|
||||
junction = get_demand(name_c, junction_name)
|
||||
_primary_demand(junction)["demand"] = (
|
||||
modify_junction_base_demand[junction_name]
|
||||
)
|
||||
cs = ChangeSet()
|
||||
cs.append(junction)
|
||||
set_demand(name_c, cs)
|
||||
# 修改节点(junction)的水量模式(pattern)
|
||||
if modify_junction_damand_pattern:
|
||||
for pattern_name in modify_junction_damand_pattern.keys():
|
||||
if not np.isnan(modify_junction_damand_pattern[pattern_name][0]):
|
||||
junction_pattern = get_pattern(name_c, pattern_name)
|
||||
junction_pattern["factors"][
|
||||
modify_index : modify_index
|
||||
+ len(modify_junction_damand_pattern[pattern_name])
|
||||
] = modify_junction_damand_pattern[pattern_name]
|
||||
cs = ChangeSet()
|
||||
cs.append(junction_pattern)
|
||||
set_pattern(name_c, cs)
|
||||
# 修改工频水泵(fixed_pump)的pattern
|
||||
if modify_fixed_pump_pattern:
|
||||
for pump_name in modify_fixed_pump_pattern.keys():
|
||||
if not np.isnan(modify_fixed_pump_pattern[pump_name][0]):
|
||||
pump_pattern = get_pattern(
|
||||
name_c, get_pump(name_c, pump_name)["pattern"]
|
||||
)
|
||||
pump_pattern["factors"][
|
||||
modify_index
|
||||
: modify_index + len(modify_fixed_pump_pattern[pump_name])
|
||||
] = modify_fixed_pump_pattern[pump_name]
|
||||
cs = ChangeSet()
|
||||
cs.append(pump_pattern)
|
||||
set_pattern(name_c, cs)
|
||||
# 修改变频水泵(variable_pump)的pattern
|
||||
if modify_variable_pump_pattern:
|
||||
for pump_name in modify_variable_pump_pattern.keys():
|
||||
if not np.isnan(modify_variable_pump_pattern[pump_name][0]):
|
||||
# 给 list 中的所有元素除以 50Hz
|
||||
modified_values = [
|
||||
value / 50 for value in modify_variable_pump_pattern[pump_name]
|
||||
]
|
||||
pump_pattern = get_pattern(
|
||||
name_c, get_pump(name_c, pump_name)["pattern"]
|
||||
)
|
||||
pump_pattern["factors"][
|
||||
modify_index : modify_index + len(modified_values)
|
||||
] = modified_values
|
||||
cs = ChangeSet()
|
||||
cs.append(pump_pattern)
|
||||
set_pattern(name_c, cs)
|
||||
# 显式阀门控制沿用 run_simulation_ex 的处理顺序和覆盖规则。
|
||||
if valve_control is not None:
|
||||
_apply_valve_control(name_c, valve_control)
|
||||
# 保留原开度参数逻辑,兼容现有方案调用。
|
||||
elif modify_valve_opening:
|
||||
for valve_name in modify_valve_opening.keys():
|
||||
if not np.isnan(modify_valve_opening[valve_name]):
|
||||
valve_status = get_status(name_c, valve_name)
|
||||
if modify_valve_opening[valve_name] == 0:
|
||||
valve_status["status"] = "CLOSED"
|
||||
valve_status["setting"] = 0
|
||||
elif modify_valve_opening[valve_name] < 1:
|
||||
valve_status["status"] = "OPEN"
|
||||
valve_status["setting"] = 0.1036 * pow(
|
||||
modify_valve_opening[valve_name], -3.105
|
||||
)
|
||||
elif modify_valve_opening[valve_name] == 1:
|
||||
valve_status["status"] = "OPEN"
|
||||
valve_status["setting"] = 0
|
||||
cs = ChangeSet()
|
||||
cs.append(valve_status)
|
||||
set_status(name_c, cs)
|
||||
# 运行并返回结果
|
||||
result_data = json.loads(run_project(name_c))
|
||||
refresh_materialized_views_after_commit(name_c)
|
||||
time_cost_end = time.perf_counter()
|
||||
print(
|
||||
"{} -- Hydraulic simulation finished, cost time: {:.2f} s.".format(
|
||||
|
||||
@@ -1,55 +1,60 @@
|
||||
import json
|
||||
from datetime import datetime
|
||||
from functools import wraps
|
||||
from math import pi
|
||||
|
||||
import pytz
|
||||
|
||||
from app.algorithms.simulation.runner import run_simulation_ex
|
||||
from app.native.wndb.core.projects import temporary_project_database
|
||||
from app.services.tjnetwork import (
|
||||
close_project,
|
||||
copy_project,
|
||||
delete_project,
|
||||
get_pipe,
|
||||
get_tank,
|
||||
have_project,
|
||||
is_project_open,
|
||||
open_project,
|
||||
)
|
||||
|
||||
|
||||
def _isolated_operation(purpose: str):
|
||||
def decorator(func):
|
||||
@wraps(func)
|
||||
def wrapper(prj_name: str, *args, **kwargs):
|
||||
with temporary_project_database(prj_name, purpose) as temporary:
|
||||
kwargs["_temporary_project"] = temporary
|
||||
return func(prj_name, *args, **kwargs)
|
||||
|
||||
return wrapper
|
||||
|
||||
return decorator
|
||||
|
||||
|
||||
############################################################
|
||||
# project management 07 ***暂时不使用,与业务需求无关***
|
||||
############################################################
|
||||
|
||||
|
||||
@_isolated_operation("project_management")
|
||||
def project_management(
|
||||
prj_name,
|
||||
start_datetime,
|
||||
pump_control,
|
||||
tank_initial_level_control=None,
|
||||
region_demand_control=None,
|
||||
_temporary_project=None,
|
||||
) -> str:
|
||||
print(
|
||||
datetime.now(pytz.timezone("Asia/Shanghai")).strftime("%Y-%m-%d %H:%M:%S")
|
||||
+ " -- Start Analysis."
|
||||
)
|
||||
new_name = f"project_management_{prj_name}"
|
||||
if have_project(new_name):
|
||||
if is_project_open(new_name):
|
||||
close_project(new_name)
|
||||
delete_project(new_name)
|
||||
# if is_project_open(prj_name):
|
||||
# close_project(prj_name)
|
||||
if _temporary_project is None:
|
||||
raise RuntimeError("Project-management isolation was not prepared")
|
||||
new_name = _temporary_project
|
||||
print(
|
||||
datetime.now(pytz.timezone("Asia/Shanghai")).strftime("%Y-%m-%d %H:%M:%S")
|
||||
+ " -- Start Copying Database."
|
||||
)
|
||||
copy_project(prj_name + "_template", new_name)
|
||||
print(
|
||||
datetime.now(pytz.timezone("Asia/Shanghai")).strftime("%Y-%m-%d %H:%M:%S")
|
||||
+ " -- Start Opening Database."
|
||||
)
|
||||
open_project(new_name)
|
||||
print(
|
||||
datetime.now(pytz.timezone("Asia/Shanghai")).strftime("%Y-%m-%d %H:%M:%S")
|
||||
+ " -- Database Loading OK."
|
||||
@@ -64,9 +69,6 @@ def project_management(
|
||||
region_demand_control=region_demand_control,
|
||||
downloading_prohibition=True,
|
||||
)
|
||||
if is_project_open(new_name):
|
||||
close_project(new_name)
|
||||
delete_project(new_name)
|
||||
return result
|
||||
|
||||
|
||||
@@ -75,31 +77,31 @@ def project_management(
|
||||
############################################################
|
||||
|
||||
|
||||
@_isolated_operation("scheduling")
|
||||
def scheduling_simulation(
|
||||
prj_name, start_time, pump_control, tank_id, water_plant_output_id, time_delta=300
|
||||
prj_name,
|
||||
start_time,
|
||||
pump_control,
|
||||
tank_id,
|
||||
water_plant_output_id,
|
||||
time_delta=300,
|
||||
_temporary_project=None,
|
||||
) -> str:
|
||||
print(
|
||||
datetime.now(pytz.timezone("Asia/Shanghai")).strftime("%Y-%m-%d %H:%M:%S")
|
||||
+ " -- Start Analysis."
|
||||
)
|
||||
new_name = f"scheduling_{prj_name}"
|
||||
|
||||
if have_project(new_name):
|
||||
if is_project_open(new_name):
|
||||
close_project(new_name)
|
||||
delete_project(new_name)
|
||||
# if is_project_open(prj_name):
|
||||
# close_project(prj_name)
|
||||
if _temporary_project is None:
|
||||
raise RuntimeError("Scheduling isolation was not prepared")
|
||||
new_name = _temporary_project
|
||||
print(
|
||||
datetime.now(pytz.timezone("Asia/Shanghai")).strftime("%Y-%m-%d %H:%M:%S")
|
||||
+ " -- Start Copying Database."
|
||||
)
|
||||
copy_project(prj_name + "_template", new_name)
|
||||
print(
|
||||
datetime.now(pytz.timezone("Asia/Shanghai")).strftime("%Y-%m-%d %H:%M:%S")
|
||||
+ " -- Start Opening Database."
|
||||
)
|
||||
open_project(new_name)
|
||||
print(
|
||||
datetime.now(pytz.timezone("Asia/Shanghai")).strftime("%Y-%m-%d %H:%M:%S")
|
||||
+ " -- Database Loading OK."
|
||||
@@ -115,9 +117,6 @@ def scheduling_simulation(
|
||||
if not isinstance(output_data, dict):
|
||||
raise RuntimeError("run_simulation_ex did not return JSON output content")
|
||||
|
||||
if not is_project_open(new_name):
|
||||
open_project(new_name)
|
||||
|
||||
tank = get_tank(new_name, tank_id) # 水塔信息
|
||||
tank_floor_space = pi * pow(tank["diameter"] / 2, 2) # 水塔底面积(m^2)
|
||||
tank_init_level = tank["init_level"] # 水塔初始水位(m)
|
||||
@@ -158,38 +157,34 @@ def scheduling_simulation(
|
||||
"tank_level": tank_level,
|
||||
}
|
||||
|
||||
if is_project_open(new_name):
|
||||
close_project(new_name)
|
||||
delete_project(new_name)
|
||||
|
||||
return json.dumps(simulation_results)
|
||||
|
||||
|
||||
@_isolated_operation("daily_scheduling")
|
||||
def daily_scheduling_simulation(
|
||||
prj_name, start_time, pump_control, reservoir_id, tank_id, water_plant_output_id
|
||||
prj_name,
|
||||
start_time,
|
||||
pump_control,
|
||||
reservoir_id,
|
||||
tank_id,
|
||||
water_plant_output_id,
|
||||
_temporary_project=None,
|
||||
) -> str:
|
||||
print(
|
||||
datetime.now(pytz.timezone("Asia/Shanghai")).strftime("%Y-%m-%d %H:%M:%S")
|
||||
+ " -- Start Analysis."
|
||||
)
|
||||
new_name = f"daily_scheduling_{prj_name}"
|
||||
|
||||
if have_project(new_name):
|
||||
if is_project_open(new_name):
|
||||
close_project(new_name)
|
||||
delete_project(new_name)
|
||||
# if is_project_open(prj_name):
|
||||
# close_project(prj_name)
|
||||
if _temporary_project is None:
|
||||
raise RuntimeError("Daily-scheduling isolation was not prepared")
|
||||
new_name = _temporary_project
|
||||
print(
|
||||
datetime.now(pytz.timezone("Asia/Shanghai")).strftime("%Y-%m-%d %H:%M:%S")
|
||||
+ " -- Start Copying Database."
|
||||
)
|
||||
copy_project(prj_name + "_template", new_name)
|
||||
print(
|
||||
datetime.now(pytz.timezone("Asia/Shanghai")).strftime("%Y-%m-%d %H:%M:%S")
|
||||
+ " -- Start Opening Database."
|
||||
)
|
||||
open_project(new_name)
|
||||
print(
|
||||
datetime.now(pytz.timezone("Asia/Shanghai")).strftime("%Y-%m-%d %H:%M:%S")
|
||||
+ " -- Database Loading OK."
|
||||
@@ -209,9 +204,6 @@ def daily_scheduling_simulation(
|
||||
if not isinstance(output_data, dict):
|
||||
raise RuntimeError("run_simulation_ex did not return JSON output content")
|
||||
|
||||
if not is_project_open(new_name):
|
||||
open_project(new_name)
|
||||
|
||||
node_results = output_data.get("node_results") or [] # [{'node': str, 'result': [{'pressure': float, 'head': float}]}]
|
||||
water_plant_output_pressure = []
|
||||
reservoir_level = []
|
||||
@@ -235,8 +227,4 @@ def daily_scheduling_simulation(
|
||||
"tank_level": tank_level,
|
||||
}
|
||||
|
||||
if is_project_open(new_name):
|
||||
close_project(new_name)
|
||||
delete_project(new_name)
|
||||
|
||||
return json.dumps(simulation_results)
|
||||
|
||||
@@ -32,14 +32,11 @@ from app.native.wndb.commands.api import (
|
||||
)
|
||||
from app.native.wndb.core.database import ChangeSet, read_all
|
||||
from app.native.wndb.core.projects import (
|
||||
close_project,
|
||||
copy_project,
|
||||
create_project,
|
||||
delete_project,
|
||||
have_project,
|
||||
is_project_open,
|
||||
list_project,
|
||||
open_project,
|
||||
)
|
||||
from app.native.wndb.gis.backdrop import (
|
||||
get_backdrop,
|
||||
@@ -54,6 +51,12 @@ from app.native.wndb.gis.labels import (
|
||||
get_label_schema,
|
||||
set_label,
|
||||
)
|
||||
from app.native.wndb.gis.network_views import (
|
||||
get_major_node_coords,
|
||||
get_major_pipe_nodes,
|
||||
get_network_link_nodes,
|
||||
get_network_node_coords,
|
||||
)
|
||||
from app.native.wndb.gis.region_geometry import get_nodes_in_region
|
||||
from app.native.wndb.gis.regions import (
|
||||
add_region,
|
||||
@@ -89,16 +92,11 @@ from app.native.wndb.model.elements import (
|
||||
get_curves,
|
||||
get_element_type,
|
||||
get_element_type_value,
|
||||
get_link_nodes,
|
||||
get_link_type,
|
||||
get_links,
|
||||
get_links_id_and_type,
|
||||
get_major_nodes,
|
||||
get_major_pipes,
|
||||
get_node_links,
|
||||
get_node_type,
|
||||
get_nodes,
|
||||
get_nodes_id_and_type,
|
||||
get_patterns,
|
||||
get_regions,
|
||||
is_curve,
|
||||
@@ -280,39 +278,6 @@ def get_element_properties(name: str, element_id: str) -> dict[str, Any]:
|
||||
return get_scada_info(name, element_id)
|
||||
|
||||
|
||||
def get_network_node_coords(name: str) -> dict[str, dict[str, Any]]:
|
||||
nodes = get_nodes_id_and_type(name)
|
||||
return {
|
||||
node_id: {**get_node_coord(name, node_id), "type": node_type}
|
||||
for node_id, node_type in nodes.items()
|
||||
}
|
||||
|
||||
|
||||
def get_major_node_coords(name: str, diameter: int) -> dict[str, dict[str, Any]]:
|
||||
node_types = get_nodes_id_and_type(name)
|
||||
return {
|
||||
node_id: {**get_node_coord(name, node_id), "type": node_types[node_id]}
|
||||
for node_id in get_major_nodes(name, diameter)
|
||||
}
|
||||
|
||||
|
||||
def get_network_link_nodes(name: str) -> list[str]:
|
||||
links = get_links_id_and_type(name)
|
||||
return [
|
||||
f"{link_id}:{link_type}:{nodes[0]}:{nodes[1]}"
|
||||
for link_id, link_type in links.items()
|
||||
if (nodes := get_link_nodes(name, link_id))
|
||||
]
|
||||
|
||||
|
||||
def get_major_pipe_nodes(name: str, diameter: int) -> list[str]:
|
||||
return [
|
||||
f"{link_id}:pipe:{nodes[0]}:{nodes[1]}"
|
||||
for link_id in get_major_pipes(name, diameter)
|
||||
if (nodes := get_link_nodes(name, link_id))
|
||||
]
|
||||
|
||||
|
||||
def get_network_in_extent(
|
||||
name: str, x1: float, y1: float, x2: float, y2: float
|
||||
) -> dict[str, Any]:
|
||||
|
||||
Reference in New Issue
Block a user