修正单元测试失败代码
This commit is contained in:
@@ -136,18 +136,20 @@ def execute_undo(name: str, discard: bool = False) -> ChangeSet:
|
|||||||
|
|
||||||
write(name, row['undo'])
|
write(name, row['undo'])
|
||||||
|
|
||||||
|
parent = row['parent'] if row['parent'] != None else 0
|
||||||
|
|
||||||
# update foreign key
|
# update foreign key
|
||||||
write(name, f"update current_operation set id = {row['parent']} where id = {row['id']}")
|
write(name, f"update current_operation set id = {parent} where id = {row['id']}")
|
||||||
|
|
||||||
if discard:
|
if discard:
|
||||||
# update foreign key
|
# update foreign key
|
||||||
write(name, f"update operation set redo_child = null where id = {row['parent']}")
|
write(name, f"update operation set redo_child = null where id = {parent}")
|
||||||
# on delete cascade => child & snapshot
|
# on delete cascade => child & snapshot
|
||||||
write(name, f"delete from operation where id = {row['id']}")
|
write(name, f"delete from operation where id = {row['id']}")
|
||||||
else:
|
else:
|
||||||
write(name, f"update operation set redo_child = {row['id']} where id = {row['parent']}")
|
write(name, f"update operation set redo_child = {row['id']} where id = {parent}")
|
||||||
|
|
||||||
e = eval(row['undo_cs'])
|
e = eval(row['undo_cs']) if row['undo_cs'] not in [None, ''] else []
|
||||||
return ChangeSet.from_list(e)
|
return ChangeSet.from_list(e)
|
||||||
|
|
||||||
|
|
||||||
@@ -159,9 +161,10 @@ def execute_redo(name: str) -> ChangeSet:
|
|||||||
row = read(name, f"select * from operation where id = {row['redo_child']}")
|
row = read(name, f"select * from operation where id = {row['redo_child']}")
|
||||||
write(name, row['redo'])
|
write(name, row['redo'])
|
||||||
|
|
||||||
write(name, f"update current_operation set id = {row['id']} where id = {row['parent']}")
|
parent = row['parent'] if row['parent'] != None else 0
|
||||||
|
write(name, f"update current_operation set id = {row['id']} where id = {parent}")
|
||||||
|
|
||||||
e = eval(row['redo_cs'])
|
e = eval(row['redo_cs']) if row['redo_cs'] not in [None, ''] else []
|
||||||
return ChangeSet.from_list(e)
|
return ChangeSet.from_list(e)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ def _set_scada_device(name: str, cs: ChangeSet) -> DbChangeSet:
|
|||||||
def set_scada_device(name: str, cs: ChangeSet) -> ChangeSet:
|
def set_scada_device(name: str, cs: ChangeSet) -> ChangeSet:
|
||||||
if get_scada_device(name, cs.operations[0]['id']) == {}:
|
if get_scada_device(name, cs.operations[0]['id']) == {}:
|
||||||
return ChangeSet()
|
return ChangeSet()
|
||||||
return execute_command(name, _set_scada_device(name, cs), False)
|
return execute_command(name, _set_scada_device(name, cs))
|
||||||
|
|
||||||
|
|
||||||
def _add_scada_device(name: str, cs: ChangeSet) -> DbChangeSet:
|
def _add_scada_device(name: str, cs: ChangeSet) -> DbChangeSet:
|
||||||
@@ -90,7 +90,7 @@ def _add_scada_device(name: str, cs: ChangeSet) -> DbChangeSet:
|
|||||||
def add_scada_device(name: str, cs: ChangeSet) -> ChangeSet:
|
def add_scada_device(name: str, cs: ChangeSet) -> ChangeSet:
|
||||||
if get_scada_device(name, cs.operations[0]['id']) != {}:
|
if get_scada_device(name, cs.operations[0]['id']) != {}:
|
||||||
return ChangeSet()
|
return ChangeSet()
|
||||||
return execute_command(name, _add_scada_device(name, cs), False)
|
return execute_command(name, _add_scada_device(name, cs))
|
||||||
|
|
||||||
|
|
||||||
def _delete_scada_device(name: str, cs: ChangeSet) -> DbChangeSet:
|
def _delete_scada_device(name: str, cs: ChangeSet) -> DbChangeSet:
|
||||||
@@ -108,7 +108,7 @@ def _delete_scada_device(name: str, cs: ChangeSet) -> DbChangeSet:
|
|||||||
def delete_scada_device(name: str, cs: ChangeSet) -> ChangeSet:
|
def delete_scada_device(name: str, cs: ChangeSet) -> ChangeSet:
|
||||||
if get_scada_device(name, cs.operations[0]['id']) == {}:
|
if get_scada_device(name, cs.operations[0]['id']) == {}:
|
||||||
return ChangeSet()
|
return ChangeSet()
|
||||||
return execute_command(name, _delete_scada_device(name, cs), False)
|
return execute_command(name, _delete_scada_device(name, cs))
|
||||||
|
|
||||||
|
|
||||||
def get_all_scada_device_ids(name: str) -> list[str]:
|
def get_all_scada_device_ids(name: str) -> list[str]:
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ def _set_scada_device_data(name: str, cs: ChangeSet) -> DbChangeSet:
|
|||||||
|
|
||||||
|
|
||||||
def set_scada_device_data(name: str, cs: ChangeSet) -> ChangeSet:
|
def set_scada_device_data(name: str, cs: ChangeSet) -> ChangeSet:
|
||||||
return execute_command(name, _set_scada_device_data(name, cs), False)
|
return execute_command(name, _set_scada_device_data(name, cs))
|
||||||
|
|
||||||
|
|
||||||
def _add_scada_device_data(name: str, cs: ChangeSet) -> DbChangeSet:
|
def _add_scada_device_data(name: str, cs: ChangeSet) -> DbChangeSet:
|
||||||
@@ -66,7 +66,7 @@ def add_scada_device_data(name: str, cs: ChangeSet) -> ChangeSet:
|
|||||||
row = try_read(name, f"select * from scada_device_data where device_id = '{cs.operations[0]['device_id']}' and time = '{cs.operations[0]['time']}'")
|
row = try_read(name, f"select * from scada_device_data where device_id = '{cs.operations[0]['device_id']}' and time = '{cs.operations[0]['time']}'")
|
||||||
if row != None:
|
if row != None:
|
||||||
return ChangeSet()
|
return ChangeSet()
|
||||||
return execute_command(name, _add_scada_device_data(name, cs), False)
|
return execute_command(name, _add_scada_device_data(name, cs))
|
||||||
|
|
||||||
|
|
||||||
def _delete_scada_device_data(name: str, cs: ChangeSet) -> DbChangeSet:
|
def _delete_scada_device_data(name: str, cs: ChangeSet) -> DbChangeSet:
|
||||||
@@ -87,4 +87,4 @@ def delete_scada_device_data(name: str, cs: ChangeSet) -> ChangeSet:
|
|||||||
row = try_read(name, f"select * from scada_device_data where device_id = '{cs.operations[0]['device_id']}' and time = '{cs.operations[0]['time']}'")
|
row = try_read(name, f"select * from scada_device_data where device_id = '{cs.operations[0]['device_id']}' and time = '{cs.operations[0]['time']}'")
|
||||||
if row == None:
|
if row == None:
|
||||||
return ChangeSet()
|
return ChangeSet()
|
||||||
return execute_command(name, _delete_scada_device_data(name, cs), False)
|
return execute_command(name, _delete_scada_device_data(name, cs))
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import ctypes
|
|
||||||
import platform
|
import platform
|
||||||
import os
|
import os
|
||||||
import math
|
import math
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
import pyclipper
|
||||||
from .s0_base import get_node_links, get_link_nodes, is_pipe
|
from .s0_base import get_node_links, get_link_nodes, is_pipe
|
||||||
from .s5_pipes import get_pipe
|
from .s5_pipes import get_pipe
|
||||||
from .database import read, try_read, read_all, write
|
from .database import read, try_read, read_all, write
|
||||||
@@ -414,40 +414,20 @@ def inflate_boundary(name: str, boundary: list[tuple[float, float]], delta: floa
|
|||||||
if boundary[0] == boundary[-1]:
|
if boundary[0] == boundary[-1]:
|
||||||
del(boundary[-1])
|
del(boundary[-1])
|
||||||
|
|
||||||
lib = ctypes.CDLL(os.path.join(os.getcwd(), 'api', 'CClipper2.dll'))
|
precision = 2
|
||||||
|
scale = 10 ** precision
|
||||||
|
path = [(round(x * scale), round(y * scale)) for x, y in boundary]
|
||||||
|
|
||||||
c_size = ctypes.c_size_t(len(boundary) * 2)
|
offset = pyclipper.PyclipperOffset(miter_limit=2.0)
|
||||||
c_path = (ctypes.c_double * c_size.value)()
|
offset.AddPath(path, pyclipper.JT_SQUARE, pyclipper.ET_CLOSEDPOLYGON)
|
||||||
i = 0
|
solutions = offset.Execute(round(delta * scale))
|
||||||
for xy in boundary:
|
if len(solutions) == 0:
|
||||||
c_path[i] = xy[0]
|
|
||||||
i += 1
|
|
||||||
c_path[i] = xy[1]
|
|
||||||
i += 1
|
|
||||||
c_delta = ctypes.c_double(delta)
|
|
||||||
JoinType_Square, JoinType_Round, JoinType_Miter = 0, 1, 2
|
|
||||||
c_jt = ctypes.c_int(JoinType_Square)
|
|
||||||
EndType_Polygon, EndType_Joined, EndType_Butt, EndType_Square, EndType_Round = 0, 1, 2, 3, 4
|
|
||||||
c_et = ctypes.c_int(EndType_Polygon)
|
|
||||||
c_miter_limit = ctypes.c_double(2.0)
|
|
||||||
c_precision = ctypes.c_int(2)
|
|
||||||
c_arc_tolerance = ctypes.c_double(0.0)
|
|
||||||
c_out_path = ctypes.POINTER(ctypes.c_double)()
|
|
||||||
c_out_size = ctypes.c_size_t(0)
|
|
||||||
|
|
||||||
lib.inflate_paths(c_path, c_size, c_delta, c_jt, c_et, c_miter_limit, c_precision, c_arc_tolerance, ctypes.byref(c_out_path), ctypes.byref(c_out_size))
|
|
||||||
if c_out_size.value == 0:
|
|
||||||
lib.free_paths(ctypes.byref(c_out_path))
|
|
||||||
return []
|
return []
|
||||||
|
|
||||||
# TODO: simplify_paths :)
|
|
||||||
|
|
||||||
result: list[tuple[float, float]] = []
|
result: list[tuple[float, float]] = []
|
||||||
for i in range(0, c_out_size.value, 2):
|
for x, y in solutions[0]:
|
||||||
result.append((c_out_path[i], c_out_path[i + 1]))
|
result.append((x / scale, y / scale))
|
||||||
result.append(result[0])
|
result.append(result[0])
|
||||||
|
|
||||||
lib.free_paths(ctypes.byref(c_out_path))
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,13 @@ print(nodes_part_1)
|
|||||||
|
|
||||||
|
|
||||||
def calculate_district_metering_area_for_nodes(name: str, nodes: list[str], part_count: int = 1, part_type: int = PARTITION_TYPE_RB) -> list[list[str]]:
|
def calculate_district_metering_area_for_nodes(name: str, nodes: list[str], part_count: int = 1, part_type: int = PARTITION_TYPE_RB) -> list[list[str]]:
|
||||||
|
if part_type != PARTITION_TYPE_RB and part_type != PARTITION_TYPE_KWAY:
|
||||||
|
return []
|
||||||
|
if part_count <= 0:
|
||||||
|
return []
|
||||||
|
elif part_count == 1:
|
||||||
|
return [nodes]
|
||||||
|
|
||||||
topology = Topology(name, nodes)
|
topology = Topology(name, nodes)
|
||||||
t_nodes = topology.nodes()
|
t_nodes = topology.nodes()
|
||||||
t_links = topology.links()
|
t_links = topology.links()
|
||||||
@@ -52,7 +59,16 @@ def calculate_district_metering_area_for_nodes(name: str, nodes: list[str], part
|
|||||||
adjacency_list.append(np.array(a_nodes))
|
adjacency_list.append(np.array(a_nodes))
|
||||||
|
|
||||||
recursive = part_type == PARTITION_TYPE_RB
|
recursive = part_type == PARTITION_TYPE_RB
|
||||||
n_cuts, membership = pymetis.part_graph(nparts=part_count, adjacency=adjacency_list, recursive=recursive, contiguous=True)
|
options = pymetis.Options()
|
||||||
|
options.set_defaults()
|
||||||
|
options._set(pymetis.OptionKey.CONTIG, 1)
|
||||||
|
options._set(pymetis.OptionKey.SEED, 0)
|
||||||
|
n_cuts, membership = pymetis.part_graph(
|
||||||
|
nparts=part_count,
|
||||||
|
adjacency=adjacency_list,
|
||||||
|
recursive=recursive,
|
||||||
|
options=options,
|
||||||
|
)
|
||||||
|
|
||||||
result: list[list[str]] = []
|
result: list[list[str]] = []
|
||||||
for i in range(0, part_count):
|
for i in range(0, part_count):
|
||||||
|
|||||||
+129
-117
@@ -1,97 +1,96 @@
|
|||||||
import os
|
import os
|
||||||
import ctypes
|
import platform
|
||||||
from .project import have_project
|
import subprocess
|
||||||
from .inp_out import dump_inp
|
import uuid
|
||||||
|
|
||||||
def calculate_service_area(name: str) -> list[dict[str, list[str]]]:
|
|
||||||
if not have_project(name):
|
|
||||||
raise Exception(f'Not found project [{name}]')
|
|
||||||
|
|
||||||
dir = os.path.abspath(os.getcwd())
|
|
||||||
|
|
||||||
inp_str = os.path.join(os.path.join(dir, 'db_inp'), name + '.db.inp')
|
|
||||||
dump_inp(name, inp_str, '2')
|
|
||||||
|
|
||||||
toolkit = ctypes.CDLL(os.path.join(os.path.join(dir, 'api'), 'toolkit.dll'))
|
|
||||||
|
|
||||||
inp = ctypes.c_char_p(inp_str.encode())
|
|
||||||
|
|
||||||
handle = ctypes.c_ulonglong()
|
|
||||||
toolkit.TK_ServiceArea_Start(inp, ctypes.byref(handle))
|
|
||||||
|
|
||||||
c_nodeCount = ctypes.c_size_t()
|
|
||||||
toolkit.TK_ServiceArea_GetNodeCount(handle, ctypes.byref(c_nodeCount))
|
|
||||||
nodeCount = c_nodeCount.value
|
|
||||||
|
|
||||||
nodeIds: list[str] = []
|
|
||||||
|
|
||||||
for n in range(0, nodeCount):
|
|
||||||
id = ctypes.c_char_p()
|
|
||||||
toolkit.TK_ServiceArea_GetNodeId(handle, ctypes.c_size_t(n), ctypes.byref(id))
|
|
||||||
nodeIds.append(id.value.decode())
|
|
||||||
|
|
||||||
c_timeCount = ctypes.c_size_t()
|
|
||||||
toolkit.TK_ServiceArea_GetTimeCount(handle, ctypes.byref(c_timeCount))
|
|
||||||
timeCount = c_timeCount.value
|
|
||||||
|
|
||||||
results: list[dict[str, list[str]]] = []
|
|
||||||
|
|
||||||
for t in range(0, timeCount):
|
|
||||||
c_sourceCount = ctypes.c_size_t()
|
|
||||||
toolkit.TK_ServiceArea_GetSourceCount(handle, ctypes.c_size_t(t), ctypes.byref(c_sourceCount))
|
|
||||||
sourceCount = c_sourceCount.value
|
|
||||||
|
|
||||||
sources = ctypes.POINTER(ctypes.c_size_t)()
|
|
||||||
toolkit.TK_ServiceArea_GetSources(handle, ctypes.c_size_t(t), ctypes.byref(sources))
|
|
||||||
|
|
||||||
result: dict[str, list[str]] = {}
|
|
||||||
for s in range(0, sourceCount):
|
|
||||||
result[nodeIds[sources[s]]] = []
|
|
||||||
|
|
||||||
for n in range(0, nodeCount):
|
|
||||||
concentration = ctypes.POINTER(ctypes.c_double)()
|
|
||||||
toolkit.TK_ServiceArea_GetConcentration(handle, ctypes.c_size_t(t), ctypes.c_size_t(n), ctypes.byref(concentration))
|
|
||||||
|
|
||||||
maxS = sources[0]
|
|
||||||
maxC = concentration[0]
|
|
||||||
for s in range(1, sourceCount):
|
|
||||||
if concentration[s] > maxC:
|
|
||||||
maxS = sources[s]
|
|
||||||
maxC = concentration[s]
|
|
||||||
|
|
||||||
result[nodeIds[maxS]].append(nodeIds[n])
|
|
||||||
|
|
||||||
results.append(result)
|
|
||||||
|
|
||||||
toolkit.TK_ServiceArea_End(handle)
|
|
||||||
|
|
||||||
return results
|
|
||||||
|
|
||||||
'''
|
|
||||||
import sys
|
|
||||||
import json
|
|
||||||
from queue import Queue
|
from queue import Queue
|
||||||
from .database import *
|
from typing import Any
|
||||||
from .s0_base import get_node_links, get_link_nodes
|
|
||||||
|
|
||||||
sys.path.append('..')
|
from app.infra.epanet.epanet import Output
|
||||||
from app.infra.epanet.epanet import run_project
|
|
||||||
|
|
||||||
def _calculate_service_area(name: str, inp, time_index: int = 0) -> dict[str, list[str]]:
|
from .inp_out import dump_inp
|
||||||
sources : dict[str, list[str]] = {}
|
from .project import have_project
|
||||||
for node_result in inp['node_results']:
|
from .s0_base import get_link_nodes, get_node_links
|
||||||
|
from .s23_options_util import get_option_v3
|
||||||
|
|
||||||
|
|
||||||
|
def _update_section(lines: list[str], section: str, transform) -> list[str]:
|
||||||
|
result: list[str] = []
|
||||||
|
i = 0
|
||||||
|
while i < len(lines):
|
||||||
|
line = lines[i]
|
||||||
|
if line.strip() == f'[{section}]':
|
||||||
|
result.append(line)
|
||||||
|
i += 1
|
||||||
|
section_lines: list[str] = []
|
||||||
|
while i < len(lines) and not lines[i].startswith('['):
|
||||||
|
section_lines.append(lines[i])
|
||||||
|
i += 1
|
||||||
|
result.extend(transform(section_lines))
|
||||||
|
continue
|
||||||
|
result.append(line)
|
||||||
|
i += 1
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def _build_service_area_input(name: str, inp_path: str) -> None:
|
||||||
|
dump_inp(name, inp_path, '2')
|
||||||
|
|
||||||
|
with open(inp_path, encoding='utf-8') as file:
|
||||||
|
lines = file.read().splitlines()
|
||||||
|
|
||||||
|
unbalanced = get_option_v3(name).get('IF_UNBALANCED', '').strip()
|
||||||
|
if unbalanced != '':
|
||||||
|
lines = _update_section(
|
||||||
|
lines,
|
||||||
|
'OPTIONS',
|
||||||
|
lambda option_lines: [
|
||||||
|
f'UNBALANCED {unbalanced}' if line.startswith('UNBALANCED ') else line
|
||||||
|
for line in option_lines
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
with open(inp_path, mode='w', encoding='utf-8') as file:
|
||||||
|
file.write('\n'.join(lines) + '\n')
|
||||||
|
|
||||||
|
|
||||||
|
def _run_epanet_output(inp_path: str, rpt_path: str, out_path: str) -> dict[str, Any]:
|
||||||
|
epanet_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', 'infra', 'epanet'))
|
||||||
|
if platform.system() == 'Windows':
|
||||||
|
exe = os.path.join(epanet_dir, 'windows', 'runepanet.exe')
|
||||||
|
else:
|
||||||
|
exe = os.path.join(epanet_dir, 'linux', 'runepanet')
|
||||||
|
if not os.access(exe, os.X_OK):
|
||||||
|
os.chmod(exe, 0o755)
|
||||||
|
|
||||||
|
env = os.environ.copy()
|
||||||
|
if platform.system() == 'Linux':
|
||||||
|
lib_dir = os.path.dirname(exe)
|
||||||
|
env['LD_LIBRARY_PATH'] = f"{lib_dir}:{env.get('LD_LIBRARY_PATH', '')}"
|
||||||
|
|
||||||
|
process = subprocess.run([exe, inp_path, rpt_path, out_path], env=env, capture_output=True, text=True)
|
||||||
|
if process.returncode != 0:
|
||||||
|
raise RuntimeError(
|
||||||
|
f'EPANET failed for [{inp_path}] with code {process.returncode}: '
|
||||||
|
f'stdout={process.stdout} stderr={process.stderr}'
|
||||||
|
)
|
||||||
|
|
||||||
|
return Output(out_path).dump()
|
||||||
|
|
||||||
|
|
||||||
|
def _calculate_service_area(name: str, output: dict[str, Any], time_index: int) -> dict[str, list[str]]:
|
||||||
|
sources: dict[str, list[str]] = {}
|
||||||
|
for node_result in output['node_results']:
|
||||||
result = node_result['result'][time_index]
|
result = node_result['result'][time_index]
|
||||||
if result['demand'] < 0:
|
if result['demand'] < 0:
|
||||||
sources[node_result['node']] = []
|
sources[node_result['node']] = []
|
||||||
|
|
||||||
link_flows: dict[str, float] = {}
|
link_flows: dict[str, float] = {}
|
||||||
for link_result in inp['link_results']:
|
for link_result in output['link_results']:
|
||||||
result = link_result['result'][time_index]
|
result = link_result['result'][time_index]
|
||||||
link_flows[link_result['link']] = float(result['flow'])
|
link_flows[link_result['link']] = float(result['flow'])
|
||||||
|
|
||||||
# build source to nodes map
|
|
||||||
for source in sources:
|
for source in sources:
|
||||||
queue = Queue()
|
queue: Queue[str] = Queue()
|
||||||
queue.put(source)
|
queue.put(source)
|
||||||
|
|
||||||
while not queue.empty():
|
while not queue.empty():
|
||||||
@@ -107,9 +106,6 @@ def _calculate_service_area(name: str, inp, time_index: int = 0) -> dict[str, li
|
|||||||
elif node2 == cursor and link_flows[link] < 0:
|
elif node2 == cursor and link_flows[link] < 0:
|
||||||
queue.put(node1)
|
queue.put(node1)
|
||||||
|
|
||||||
#return sources
|
|
||||||
|
|
||||||
# calculation concentration
|
|
||||||
concentration_map: dict[str, dict[str, float]] = {}
|
concentration_map: dict[str, dict[str, float]] = {}
|
||||||
node_wip: list[str] = []
|
node_wip: list[str] = []
|
||||||
for source, nodes in sources.items():
|
for source, nodes in sources.items():
|
||||||
@@ -120,17 +116,15 @@ def _calculate_service_area(name: str, inp, time_index: int = 0) -> dict[str, li
|
|||||||
if node not in node_wip:
|
if node not in node_wip:
|
||||||
node_wip.append(node)
|
node_wip.append(node)
|
||||||
|
|
||||||
# if only one source, done
|
|
||||||
for node, concentrations in concentration_map.items():
|
for node, concentrations in concentration_map.items():
|
||||||
if len(concentrations) == 1:
|
if len(concentrations) == 1:
|
||||||
node_wip.remove(node)
|
node_wip.remove(node)
|
||||||
for key in concentrations.keys():
|
for source in concentrations.keys():
|
||||||
concentration_map[node][key] = 1.0
|
concentration_map[node][source] = 1.0
|
||||||
|
|
||||||
node_upstream : dict[str, list[tuple[str, str]]] = {}
|
node_upstream: dict[str, list[tuple[str, str]]] = {}
|
||||||
for node in node_wip:
|
for node in node_wip:
|
||||||
if node not in node_upstream:
|
node_upstream[node] = []
|
||||||
node_upstream[node] = []
|
|
||||||
|
|
||||||
links = get_node_links(name, node)
|
links = get_node_links(name, node)
|
||||||
for link in links:
|
for link in links:
|
||||||
@@ -141,7 +135,7 @@ def _calculate_service_area(name: str, inp, time_index: int = 0) -> dict[str, li
|
|||||||
node_upstream[node].append((link, node2))
|
node_upstream[node].append((link, node2))
|
||||||
|
|
||||||
while len(node_wip) != 0:
|
while len(node_wip) != 0:
|
||||||
done = []
|
done: list[str] = []
|
||||||
for node in node_wip:
|
for node in node_wip:
|
||||||
up_link_nodes = node_upstream[node]
|
up_link_nodes = node_upstream[node]
|
||||||
ready = True
|
ready = True
|
||||||
@@ -149,33 +143,38 @@ def _calculate_service_area(name: str, inp, time_index: int = 0) -> dict[str, li
|
|||||||
if link_node[1] in node_wip:
|
if link_node[1] in node_wip:
|
||||||
ready = False
|
ready = False
|
||||||
break
|
break
|
||||||
if ready:
|
if not ready:
|
||||||
for link_node in up_link_nodes:
|
continue
|
||||||
if link_node[1] not in concentration_map.keys():
|
|
||||||
continue
|
|
||||||
for source, concentration in concentration_map[link_node[1]].items():
|
|
||||||
concentration_map[node][source] += concentration * abs(link_flows[link_node[0]])
|
|
||||||
|
|
||||||
# normalize
|
for link, upstream_node in up_link_nodes:
|
||||||
sum = 0.0
|
if upstream_node not in concentration_map:
|
||||||
for source, concentration in concentration_map[node].items():
|
continue
|
||||||
sum += concentration
|
for source, concentration in concentration_map[upstream_node].items():
|
||||||
for source in concentration_map[node].keys():
|
concentration_map[node][source] += concentration * abs(link_flows[link])
|
||||||
concentration_map[node][source] /= sum
|
|
||||||
|
|
||||||
done.append(node)
|
total_concentration = sum(concentration_map[node].values())
|
||||||
|
if total_concentration == 0:
|
||||||
|
raise RuntimeError(f'Failed to normalize service area concentration for node [{node}] at time [{time_index}]')
|
||||||
|
|
||||||
|
for source in concentration_map[node].keys():
|
||||||
|
concentration_map[node][source] /= total_concentration
|
||||||
|
|
||||||
|
done.append(node)
|
||||||
|
|
||||||
|
if len(done) == 0:
|
||||||
|
raise RuntimeError(f'Failed to resolve service area graph for time [{time_index}]')
|
||||||
|
|
||||||
for node in done:
|
for node in done:
|
||||||
node_wip.remove(node)
|
node_wip.remove(node)
|
||||||
|
|
||||||
source_to_main_node: dict[str, list[str]] = {}
|
source_to_main_node: dict[str, list[str]] = {}
|
||||||
for node, value in concentration_map.items():
|
for node, concentrations in concentration_map.items():
|
||||||
max_source = ''
|
max_source = ''
|
||||||
max_concentration = 0.0
|
max_concentration = 0.0
|
||||||
for s, c in value.items():
|
for source, concentration in concentrations.items():
|
||||||
if c > max_concentration:
|
if concentration > max_concentration:
|
||||||
max_concentration = c
|
max_concentration = concentration
|
||||||
max_source = s
|
max_source = source
|
||||||
if max_source not in source_to_main_node:
|
if max_source not in source_to_main_node:
|
||||||
source_to_main_node[max_source] = []
|
source_to_main_node[max_source] = []
|
||||||
source_to_main_node[max_source].append(node)
|
source_to_main_node[max_source].append(node)
|
||||||
@@ -184,15 +183,28 @@ def _calculate_service_area(name: str, inp, time_index: int = 0) -> dict[str, li
|
|||||||
|
|
||||||
|
|
||||||
def calculate_service_area(name: str) -> list[dict[str, list[str]]]:
|
def calculate_service_area(name: str) -> list[dict[str, list[str]]]:
|
||||||
inp = json.loads(run_project(name, True))
|
if not have_project(name):
|
||||||
|
raise Exception(f'Not found project [{name}]')
|
||||||
|
|
||||||
result: list[dict[str, list[str]]] = []
|
root = os.path.abspath(os.getcwd())
|
||||||
|
token = f'{os.getpid()}_{uuid.uuid4().hex}'
|
||||||
|
inp_path = os.path.join(root, 'db_inp', f'{name}.service_area.{token}.inp')
|
||||||
|
rpt_path = os.path.join(root, 'temp', f'{name}.service_area.{token}.rpt')
|
||||||
|
out_path = os.path.join(root, 'temp', f'{name}.service_area.{token}.opt')
|
||||||
|
|
||||||
time_count = len(inp['node_results'][0]['result'])
|
os.makedirs(os.path.dirname(inp_path), exist_ok=True)
|
||||||
|
os.makedirs(os.path.dirname(rpt_path), exist_ok=True)
|
||||||
|
|
||||||
for i in range(time_count):
|
try:
|
||||||
sas = _calculate_service_area(name, inp, i)
|
_build_service_area_input(name, inp_path)
|
||||||
result.append(sas)
|
output = _run_epanet_output(inp_path, rpt_path, out_path)
|
||||||
|
|
||||||
return result
|
results: list[dict[str, list[str]]] = []
|
||||||
'''
|
time_count = len(output['node_results'][0]['result'])
|
||||||
|
for time_index in range(time_count):
|
||||||
|
results.append(_calculate_service_area(name, output, time_index))
|
||||||
|
return results
|
||||||
|
finally:
|
||||||
|
for path in (inp_path, rpt_path, out_path):
|
||||||
|
if os.path.exists(path):
|
||||||
|
os.remove(path)
|
||||||
|
|||||||
Reference in New Issue
Block a user