refactor(db)!: finalize pooled WNDB v2 migration
This commit is contained in:
@@ -1,9 +1,7 @@
|
||||
import numpy as np
|
||||
from functools import wraps
|
||||
from app.services.tjnetwork import (
|
||||
ChangeSet,
|
||||
close_project,
|
||||
copy_project,
|
||||
delete_project,
|
||||
get_pattern,
|
||||
get_patterns,
|
||||
get_pump,
|
||||
@@ -11,9 +9,6 @@ from app.services.tjnetwork import (
|
||||
get_status,
|
||||
get_tank,
|
||||
get_time,
|
||||
have_project,
|
||||
is_project_open,
|
||||
open_project,
|
||||
read_all,
|
||||
run_project,
|
||||
set_pattern,
|
||||
@@ -29,6 +24,7 @@ import pytz
|
||||
import requests
|
||||
import time
|
||||
import app.services.project_info as project_info
|
||||
from app.native.wndb.core.projects import temporary_project_database
|
||||
from app.services.time_api import parse_clock_duration_seconds
|
||||
|
||||
url_path = 'http://10.101.15.16:9000/loong' # 内网
|
||||
@@ -572,9 +568,6 @@ def trim_time_flag(url_date_time:str)->str:
|
||||
|
||||
# 单时间步长模拟
|
||||
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
|
||||
@@ -654,32 +647,38 @@ def run_simulation(name:str,start_datetime:str,end_datetime:str=None, duration:i
|
||||
|
||||
|
||||
# 在线模拟
|
||||
def _clean_extended_simulation(func):
|
||||
@wraps(func)
|
||||
def wrapper(name: str, simulation_type: str, *args, **kwargs):
|
||||
if simulation_type.upper() != "EXTENDED":
|
||||
return func(name, simulation_type, *args, **kwargs)
|
||||
with temporary_project_database(name, "extended_simulation") as temporary:
|
||||
kwargs["_temporary_project"] = temporary
|
||||
return func(name, simulation_type, *args, **kwargs)
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
@_clean_extended_simulation
|
||||
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:
|
||||
downloading_prohibition: bool = False,
|
||||
_temporary_project: str | None = None) -> 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) # 备份项目
|
||||
if _temporary_project is None:
|
||||
raise RuntimeError("Extended simulation isolation was not prepared")
|
||||
name_c = _temporary_project
|
||||
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
|
||||
@@ -848,8 +847,6 @@ def run_simulation_ex(name: str, simulation_type: str, start_datetime: str,
|
||||
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
|
||||
|
||||
|
||||
@@ -858,7 +855,6 @@ if __name__ == '__main__':
|
||||
# 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')
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import json
|
||||
from datetime import datetime
|
||||
from functools import wraps
|
||||
from math import pi, sqrt
|
||||
|
||||
import pytz
|
||||
@@ -9,6 +10,7 @@ from app.algorithms.simulation.runner import (
|
||||
run_simulation_ex,
|
||||
from_clock_to_seconds_2,
|
||||
)
|
||||
from app.native.wndb.core.projects import temporary_project_database
|
||||
from app.services.tjnetwork import (
|
||||
ChangeSet,
|
||||
OPTION_DEMAND_MODEL_PDA,
|
||||
@@ -16,9 +18,6 @@ from app.services.tjnetwork import (
|
||||
SOURCE_TYPE_SETPOINT,
|
||||
add_pattern,
|
||||
add_source,
|
||||
close_project,
|
||||
copy_project,
|
||||
delete_project,
|
||||
get_demand,
|
||||
get_emitter,
|
||||
get_node_links,
|
||||
@@ -27,10 +26,7 @@ from app.services.tjnetwork import (
|
||||
get_pipe,
|
||||
get_source,
|
||||
get_time,
|
||||
have_project,
|
||||
is_junction,
|
||||
is_project_open,
|
||||
open_project,
|
||||
set_demand,
|
||||
set_emitter,
|
||||
set_option,
|
||||
@@ -39,11 +35,23 @@ from app.services.tjnetwork import (
|
||||
)
|
||||
|
||||
|
||||
def _isolated_analysis(purpose: str):
|
||||
def decorator(func):
|
||||
@wraps(func)
|
||||
def wrapper(name: str, *args, **kwargs):
|
||||
with temporary_project_database(name, purpose) as temporary:
|
||||
kwargs["_temporary_project"] = temporary
|
||||
return func(name, *args, **kwargs)
|
||||
|
||||
return wrapper
|
||||
|
||||
return decorator
|
||||
|
||||
|
||||
############################################################
|
||||
# burst analysis 01
|
||||
############################################################
|
||||
def convert_to_local_unit(proj: str, emitters: float) -> float:
|
||||
open_project(proj)
|
||||
proj_opt = get_option(proj)
|
||||
str_unit = proj_opt.get("UNITS")
|
||||
|
||||
@@ -61,6 +69,7 @@ def convert_to_local_unit(proj: str, emitters: float) -> float:
|
||||
return emitters
|
||||
|
||||
|
||||
@_isolated_analysis("burst_analysis")
|
||||
def burst_analysis(
|
||||
name: str,
|
||||
modify_pattern_start_time: str,
|
||||
@@ -72,6 +81,7 @@ def burst_analysis(
|
||||
modify_valve_opening: dict[str, float] = None,
|
||||
scheme_name: str = None,
|
||||
username: str | None = None,
|
||||
_temporary_project: str | None = None,
|
||||
) -> None:
|
||||
"""
|
||||
爆管模拟
|
||||
@@ -101,21 +111,17 @@ def burst_analysis(
|
||||
datetime.now(pytz.timezone("Asia/Shanghai")).strftime("%Y-%m-%d %H:%M:%S")
|
||||
+ " -- Start Analysis."
|
||||
)
|
||||
new_name = f"burst_Anal_{name}"
|
||||
if have_project(new_name):
|
||||
if is_project_open(new_name):
|
||||
close_project(new_name)
|
||||
delete_project(new_name)
|
||||
if _temporary_project is None:
|
||||
raise RuntimeError("Burst analysis 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(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)
|
||||
simulation.run_simulation(
|
||||
name=new_name,
|
||||
simulation_type="manually_temporary",
|
||||
@@ -207,20 +213,19 @@ def burst_analysis(
|
||||
)
|
||||
# step 3. restore the base model status
|
||||
# execute_undo(name) #有疑惑
|
||||
if is_project_open(new_name):
|
||||
close_project(new_name)
|
||||
delete_project(new_name)
|
||||
|
||||
|
||||
############################################################
|
||||
# valve closing analysis 02
|
||||
############################################################
|
||||
@_isolated_analysis("valve_close_analysis")
|
||||
def valve_close_analysis(
|
||||
name: str,
|
||||
modify_pattern_start_time: str,
|
||||
modify_total_duration: int = 900,
|
||||
modify_valve_opening: dict[str, float] = None,
|
||||
scheme_name: str = None,
|
||||
_temporary_project: str | None = None,
|
||||
) -> None:
|
||||
"""
|
||||
关阀模拟
|
||||
@@ -235,21 +240,17 @@ def valve_close_analysis(
|
||||
datetime.now(pytz.timezone("Asia/Shanghai")).strftime("%Y-%m-%d %H:%M:%S")
|
||||
+ " -- Start Analysis."
|
||||
)
|
||||
new_name = f"valve_close_Anal_{name}"
|
||||
if have_project(new_name):
|
||||
if is_project_open(new_name):
|
||||
close_project(new_name)
|
||||
delete_project(new_name)
|
||||
if _temporary_project is None:
|
||||
raise RuntimeError("Valve-close analysis 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(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."
|
||||
@@ -287,9 +288,6 @@ def valve_close_analysis(
|
||||
# step 3. restore the base model
|
||||
# for valve in valves:
|
||||
# execute_undo(name)
|
||||
if is_project_open(new_name):
|
||||
close_project(new_name)
|
||||
delete_project(new_name)
|
||||
# return result
|
||||
|
||||
|
||||
@@ -297,6 +295,7 @@ def valve_close_analysis(
|
||||
# flushing analysis 03
|
||||
# Pipe_Flushing_Analysis(prj_name,date_time, Valve_id_list, Drainage_Node_Id, Flushing_flow[opt], Flushing_duration[opt])->out_file:string
|
||||
############################################################
|
||||
@_isolated_analysis("flushing_analysis")
|
||||
def flushing_analysis(
|
||||
name: str,
|
||||
modify_pattern_start_time: str,
|
||||
@@ -307,6 +306,7 @@ def flushing_analysis(
|
||||
scheme_name: str = None,
|
||||
username: str | None = None,
|
||||
valve_control: dict[str, dict] = None,
|
||||
_temporary_project: str | None = None,
|
||||
) -> None:
|
||||
"""
|
||||
管道冲洗模拟
|
||||
@@ -334,23 +334,17 @@ def flushing_analysis(
|
||||
datetime.now(pytz.timezone("Asia/Shanghai")).strftime("%Y-%m-%d %H:%M:%S")
|
||||
+ " -- Start Analysis."
|
||||
)
|
||||
new_name = f"flushing_Anal_{name}"
|
||||
if have_project(new_name):
|
||||
if is_project_open(new_name):
|
||||
close_project(new_name)
|
||||
delete_project(new_name)
|
||||
# if is_project_open(name):
|
||||
# close_project(name)
|
||||
if _temporary_project is None:
|
||||
raise RuntimeError("Flushing analysis 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(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."
|
||||
@@ -449,9 +443,6 @@ def flushing_analysis(
|
||||
scheme_detail=scheme_detail,
|
||||
)
|
||||
# step 4. restore the base model
|
||||
if is_project_open(new_name):
|
||||
close_project(new_name)
|
||||
delete_project(new_name)
|
||||
# return result
|
||||
|
||||
|
||||
@@ -459,6 +450,7 @@ def flushing_analysis(
|
||||
# Contaminant simulation 04
|
||||
#
|
||||
############################################################
|
||||
@_isolated_analysis("contaminant_simulation")
|
||||
def contaminant_simulation(
|
||||
name: str,
|
||||
modify_pattern_start_time: str, # 模拟开始时间,格式为'2024-11-25T09:00:00+08:00'
|
||||
@@ -468,6 +460,7 @@ def contaminant_simulation(
|
||||
scheme_name: str = None,
|
||||
source_pattern: str = None, # 污染源时间变化模式名称
|
||||
username: str | None = None,
|
||||
_temporary_project: str | None = None,
|
||||
) -> None:
|
||||
"""
|
||||
污染模拟
|
||||
@@ -494,23 +487,17 @@ def contaminant_simulation(
|
||||
datetime.now(pytz.timezone("Asia/Shanghai")).strftime("%Y-%m-%d %H:%M:%S")
|
||||
+ " -- Start Analysis."
|
||||
)
|
||||
new_name = f"contaminant_Sim_{name}"
|
||||
if have_project(new_name):
|
||||
if is_project_open(new_name):
|
||||
close_project(new_name)
|
||||
delete_project(new_name)
|
||||
# if is_project_open(name):
|
||||
# close_project(name)
|
||||
if _temporary_project is None:
|
||||
raise RuntimeError("Contaminant simulation 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(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."
|
||||
@@ -599,9 +586,6 @@ def contaminant_simulation(
|
||||
|
||||
# for i in range(1,operation_step):
|
||||
# execute_undo(name)
|
||||
if is_project_open(new_name):
|
||||
close_project(new_name)
|
||||
delete_project(new_name)
|
||||
|
||||
|
||||
############################################################
|
||||
@@ -623,45 +607,19 @@ def age_analysis(
|
||||
datetime.now(pytz.timezone("Asia/Shanghai")).strftime("%Y-%m-%d %H:%M:%S")
|
||||
+ " -- Start Analysis."
|
||||
)
|
||||
new_name = f"age_Anal_{name}"
|
||||
if have_project(new_name):
|
||||
if is_project_open(new_name):
|
||||
close_project(new_name)
|
||||
delete_project(new_name)
|
||||
# if is_project_open(name):
|
||||
# close_project(name)
|
||||
print(
|
||||
datetime.now(pytz.timezone("Asia/Shanghai")).strftime("%Y-%m-%d %H:%M:%S")
|
||||
+ " -- Start Copying Database."
|
||||
)
|
||||
copy_project(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."
|
||||
)
|
||||
# step 1. run simulation
|
||||
with temporary_project_database(name, "age_analysis") as new_name:
|
||||
result = run_simulation_ex(
|
||||
new_name,
|
||||
"realtime",
|
||||
modify_pattern_start_time,
|
||||
duration=modify_total_duration,
|
||||
downloading_prohibition=True,
|
||||
)
|
||||
simulation_result = json.loads(result)
|
||||
output_data = simulation_result.get("output")
|
||||
if not isinstance(output_data, dict):
|
||||
raise RuntimeError("run_simulation_ex did not return JSON output content")
|
||||
|
||||
result = run_simulation_ex(
|
||||
new_name,
|
||||
"realtime",
|
||||
modify_pattern_start_time,
|
||||
duration=modify_total_duration,
|
||||
downloading_prohibition=True,
|
||||
)
|
||||
simulation_result = json.loads(result)
|
||||
output_data = simulation_result.get("output")
|
||||
if not isinstance(output_data, dict):
|
||||
raise RuntimeError("run_simulation_ex did not return JSON output content")
|
||||
# step 2. restore the base model status
|
||||
# execute_undo(name) #有疑惑
|
||||
if is_project_open(new_name):
|
||||
close_project(new_name)
|
||||
delete_project(new_name)
|
||||
nodes_age = []
|
||||
node_result = output_data.get("node_results") or []
|
||||
for node in node_result:
|
||||
@@ -680,6 +638,7 @@ def age_analysis(
|
||||
############################################################
|
||||
|
||||
|
||||
@_isolated_analysis("pressure_regulation")
|
||||
def pressure_regulation(
|
||||
name: str,
|
||||
modify_pattern_start_time: str,
|
||||
@@ -688,6 +647,7 @@ def pressure_regulation(
|
||||
modify_fixed_pump_pattern: dict[str, list] = None,
|
||||
modify_variable_pump_pattern: dict[str, list] = None,
|
||||
scheme_name: str = None,
|
||||
_temporary_project: str | None = None,
|
||||
) -> None:
|
||||
"""
|
||||
区域调压模拟,用来模拟未来15分钟内,开关水泵对区域压力的影响
|
||||
@@ -704,23 +664,17 @@ def pressure_regulation(
|
||||
datetime.now(pytz.timezone("Asia/Shanghai")).strftime("%Y-%m-%d %H:%M:%S")
|
||||
+ " -- Start Analysis."
|
||||
)
|
||||
new_name = f"pressure_regulation_{name}"
|
||||
if have_project(new_name):
|
||||
if is_project_open(new_name):
|
||||
close_project(new_name)
|
||||
delete_project(new_name)
|
||||
# if is_project_open(name):
|
||||
# close_project(name)
|
||||
if _temporary_project is None:
|
||||
raise RuntimeError("Pressure-regulation 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(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."
|
||||
@@ -751,7 +705,4 @@ def pressure_regulation(
|
||||
scheme_name=scheme_name,
|
||||
result_db_name=name,
|
||||
)
|
||||
if is_project_open(new_name):
|
||||
close_project(new_name)
|
||||
delete_project(new_name)
|
||||
# return result
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
from app.native.wndb.commands.executor import execute_batch_command
|
||||
from app.native.wndb.core.database import ChangeSet
|
||||
from app.native.wndb.gis.region_geometry import Topology, get_nodes_in_region
|
||||
from app.native.wndb.model.demands import get_demand
|
||||
from app.native.wndb.model.elements import get_nodes, is_junction
|
||||
from app.native.wndb.gis.network_views import (
|
||||
get_junction_demands,
|
||||
sum_junction_base_demand,
|
||||
)
|
||||
from app.native.wndb.model.elements import get_nodes
|
||||
|
||||
|
||||
DISTRIBUTION_TYPE_ADD = 'ADD'
|
||||
@@ -28,7 +31,7 @@ def calculate_demand_to_nodes(name: str, demand: float, nodes: list[str]) -> dic
|
||||
|
||||
result: dict[str, float] = {}
|
||||
for node, value in t_nodes.items():
|
||||
if not is_junction(name, node):
|
||||
if value["type"] != "junction":
|
||||
continue
|
||||
demand_per_node = 0.0
|
||||
for link in value['links']:
|
||||
@@ -68,15 +71,19 @@ def distribute_demand_to_nodes(name: str, demand: float, nodes: list[str], type:
|
||||
demand_per_length = demand / length_sum
|
||||
|
||||
cs = ChangeSet()
|
||||
demands_by_junction = get_junction_demands(
|
||||
name,
|
||||
[node for node, value in t_nodes.items() if value["type"] == "junction"],
|
||||
)
|
||||
|
||||
for node, value in t_nodes.items():
|
||||
if not is_junction(name, node):
|
||||
if value["type"] != "junction":
|
||||
continue
|
||||
demand_per_node = 0.0
|
||||
for link in value['links']:
|
||||
demand_per_node += abs(t_links[link]['length']) * demand_per_length * 0.5
|
||||
|
||||
ds = get_demand(name, node)['demands']
|
||||
ds = demands_by_junction.get(node, [])
|
||||
if len(ds) == 0:
|
||||
ds = [{'demand': demand_per_node, 'pattern': None, 'category': None}]
|
||||
elif type == DISTRIBUTION_TYPE_ADD:
|
||||
@@ -92,13 +99,6 @@ def distribute_demand_to_region(name: str, demand: float, region: str, type: str
|
||||
nodes = get_nodes_in_region(name, region)
|
||||
return distribute_demand_to_nodes(name, demand, nodes, type)
|
||||
|
||||
def get_total_base_demand(name:str,region:str)->float:
|
||||
def get_total_base_demand(name: str, region: str) -> float:
|
||||
nodes = get_nodes_in_region(name, region)
|
||||
t_demands=0.0
|
||||
for node in nodes:
|
||||
if not is_junction(name, node):
|
||||
continue
|
||||
ds = get_demand(name, node)['demands']
|
||||
t_demands= t_demands+ds[0]['demand']
|
||||
|
||||
return t_demands
|
||||
return sum_junction_base_demand(name, nodes)
|
||||
|
||||
Reference in New Issue
Block a user