From 28ea0e377899282951e651e1a5f6c7d411092289 Mon Sep 17 00:00:00 2001 From: "WQY\\qiong" Date: Wed, 10 May 2023 20:42:05 +0800 Subject: [PATCH 01/18] Add dma interface, no implementation --- tjnetwork.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tjnetwork.py b/tjnetwork.py index 1de012f..c0ef4ed 100644 --- a/tjnetwork.py +++ b/tjnetwork.py @@ -1032,6 +1032,22 @@ def delete_region(name: str, cs: ChangeSet) -> ChangeSet: def calculate_district_metering_area(name: str, nodes: list[str], part_count: int = 1, part_type: int = PARTITION_TYPE_RB) -> list[list[str]]: return api.calculate_district_metering_area(name, nodes, part_count, part_type) +def get_district_metering_area_schema(name: str) -> dict[str, dict[str, Any]]: + return {}#api.get_district_metering_area_schema(name) + +def get_district_metering_area(name: str, id: str) -> dict[str, Any]: + return {}#api.get_district_metering_area(name, id) + +def set_district_metering_area(name: str, cs: ChangeSet) -> ChangeSet: + return ChangeSet()#api.set_district_metering_area(name, cs) + +# example: add_district_metering_area(p, ChangeSet({'id': 'r', 'boundary': [(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 0.0)]})) +def add_district_metering_area(name: str, cs: ChangeSet) -> ChangeSet: + return ChangeSet()#api.add_district_metering_area(name, cs) + +def delete_district_metering_area(name: str, cs: ChangeSet) -> ChangeSet: + return ChangeSet()#api.delete_district_metering_area(name, cs) + ############################################################ # service_area 34 From 49a9865eb2601e2c3c790466bb4145b80520c128 Mon Sep 17 00:00:00 2001 From: "WQY\\qiong" Date: Wed, 10 May 2023 20:49:34 +0800 Subject: [PATCH 02/18] Add dma convenient interface, no implementation --- tjnetwork.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tjnetwork.py b/tjnetwork.py index c0ef4ed..04a8cdd 100644 --- a/tjnetwork.py +++ b/tjnetwork.py @@ -1048,6 +1048,9 @@ def add_district_metering_area(name: str, cs: ChangeSet) -> ChangeSet: def delete_district_metering_area(name: str, cs: ChangeSet) -> ChangeSet: return ChangeSet()#api.delete_district_metering_area(name, cs) +def generate_district_metering_area(name: str, region: None | str, part_count: int = 1, part_type: int = PARTITION_TYPE_RB) -> ChangeSet: + return ChangeSet() + ############################################################ # service_area 34 From b75a717f954155974fa8f08cde1760241525fc1a Mon Sep 17 00:00:00 2001 From: "WQY\\qiong" Date: Wed, 10 May 2023 20:55:11 +0800 Subject: [PATCH 03/18] Add more dma interface --- tjnetwork.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/tjnetwork.py b/tjnetwork.py index 04a8cdd..043e215 100644 --- a/tjnetwork.py +++ b/tjnetwork.py @@ -1008,6 +1008,9 @@ def calculate_demand_to_nodes(name: str, demand: float, nodes: list[str]) -> dic def calculate_demand_to_region(name: str, demand: float, region: str) -> dict[str, float]: return api.calculate_demand_to_region(name, demand, region) +def calculate_demand_to_network(name: str, demand: float) -> dict[str, float]: + return {} + def get_region_schema(name: str) -> dict[str, dict[str, Any]]: return api.get_region_schema(name) @@ -1029,9 +1032,15 @@ def delete_region(name: str, cs: ChangeSet) -> ChangeSet: # district_metering_area 33 ############################################################ -def calculate_district_metering_area(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]]: return api.calculate_district_metering_area(name, nodes, part_count, part_type) +def calculate_district_metering_area_for_region(name: str, region: str, part_count: int = 1, part_type: int = PARTITION_TYPE_RB) -> list[list[str]]: + return [] + +def calculate_district_metering_area_for_network(name: str, part_count: int = 1, part_type: int = PARTITION_TYPE_RB) -> list[list[str]]: + return [] + def get_district_metering_area_schema(name: str) -> dict[str, dict[str, Any]]: return {}#api.get_district_metering_area_schema(name) @@ -1048,9 +1057,6 @@ def add_district_metering_area(name: str, cs: ChangeSet) -> ChangeSet: def delete_district_metering_area(name: str, cs: ChangeSet) -> ChangeSet: return ChangeSet()#api.delete_district_metering_area(name, cs) -def generate_district_metering_area(name: str, region: None | str, part_count: int = 1, part_type: int = PARTITION_TYPE_RB) -> ChangeSet: - return ChangeSet() - ############################################################ # service_area 34 From e6ec0018a06ff20edbeaab89c20dbd1da61eb46e Mon Sep 17 00:00:00 2001 From: "WQY\\qiong" Date: Fri, 12 May 2023 19:47:19 +0800 Subject: [PATCH 04/18] Support calculate_demand_to_network --- api/__init__.py | 2 +- api/s32_water_distribution.py | 7 ++++++- test_tjnetwork.py | 23 +++++++++++++++++------ tjnetwork.py | 2 +- 4 files changed, 25 insertions(+), 9 deletions(-) diff --git a/api/__init__.py b/api/__init__.py index be80c25..5239992 100644 --- a/api/__init__.py +++ b/api/__init__.py @@ -142,7 +142,7 @@ from .s32_region_util import get_nodes_in_boundary, get_nodes_in_region, calcula from .s32_region import get_region_schema, get_region, set_region, add_region, delete_region -from .s32_water_distribution import calculate_demand_to_nodes, calculate_demand_to_region +from .s32_water_distribution import calculate_demand_to_nodes, calculate_demand_to_region, calculate_demand_to_network from .s33_district_metering_area import PARTITION_TYPE_RB, PARTITION_TYPE_KWAY from .s33_district_metering_area import calculate_district_metering_area diff --git a/api/s32_water_distribution.py b/api/s32_water_distribution.py index 06896e0..b2b9f8a 100644 --- a/api/s32_water_distribution.py +++ b/api/s32_water_distribution.py @@ -1,5 +1,5 @@ from .database import ChangeSet -from .s0_base import is_junction +from .s0_base import is_junction, get_nodes from .s9_demands import get_demand from .s32_region_util import Topology, get_nodes_in_region from .batch_exe import execute_batch_command @@ -43,6 +43,11 @@ def calculate_demand_to_region(name: str, demand: float, region: str) -> dict[st return calculate_demand_to_nodes(name, demand, nodes) +def calculate_demand_to_network(name: str, demand: float) -> dict[str, float]: + nodes = get_nodes(name) + return calculate_demand_to_nodes(name, demand, nodes) + + def distribute_demand_to_nodes(name: str, demand: float, nodes: list[str], type: str = DISTRIBUTION_TYPE_ADD) -> ChangeSet: if len(nodes) == 0 or demand == 0.0: return ChangeSet() diff --git a/test_tjnetwork.py b/test_tjnetwork.py index 49dcd5d..66bea40 100644 --- a/test_tjnetwork.py +++ b/test_tjnetwork.py @@ -5854,15 +5854,26 @@ class TestApi: self.leave(p) - # 33 district_metering_area - - - def test_calculate_district_metering_area(self): - p = 'test_calculate_district_metering_area' + def test_calculate_demand_to_network(self): + p = 'test_calculate_demand_to_network' read_inp(p, f'./inp/net3.inp', '3') open_project(p) - dmas = calculate_district_metering_area(p, get_nodes(p), 3) + result = calculate_demand_to_network(p, 100.0) + assert result == {'10': 3.2923444181778216, '101': 4.194261304565972, '103': 1.226514223391597, '105': 1.561545046227298, '107': 0.7929449232512782, '109': 1.3772201298574833, '111': 1.7690554866687873, '113': 1.2381069854274345, '115': 1.6902247048250931, '117': 1.088560355165132, '119': 1.9174428407275061, '120': 1.2276734995951808, '121': 1.502421959844527, '123': 10.897196313687157, '125': 2.0240962514572103, '127': 1.141887060529984, '129': 2.3486935884606575, '131': 1.5024219598445272, '139': 1.1129051554403906, '141': 1.6137124753885663, '143': 0.7071584841860814, '145': 1.3238934244926313, '147': 0.7141141414075839, '149': 0.44052495736182123, '15': 0.38256114718263423, '151': 1.3099821100496263, '153': 1.328530529306966, '157': 1.1569576511765725, '159': 1.1384092319192327, '161': 1.0966752885902182, '163': 0.41270232847581145, '164': 0.14838735405871872, '166': 0.11360906795120652, '167': 0.01391131444300488, '169': 0.5949405476791755, '171': 0.48225890069083593, '173': 0.9390137249028294, '179': 0.32459733700344723, '181': 0.06723801980785693, '183': 0.4799403482836684, '184': 1.0734665789944717, '185': 0.4486167052628357, '187': 0.732639375140852, '189': 0.8638926269106031, '191': 0.9807476682318441, '193': 0.8601829430591352, '195': 0.6167349403065497, '197': 0.9135096484239872, '199': 1.2415848140381855, '20': 0.20496003279360525, '201': 0.2434480027525854, '203': 0.02782262888600976, '204': 0.33037053249729426, '205': 1.4780771595692686, '206': 0.22258103108807809, '207': 0.7141141414075839, '208': 0.3234380607998635, '209': 0.48573672930158707, '211': 0.9923404302676815, '213': 1.7331179243576913, '215': 1.378379406061067, '217': 1.2218771185772621, '219': 0.47530324346933345, '225': 0.3616941755181269, '229': 1.1476834415479027, '231': 0.4544362718048261, '237': 0.7836707136226083, '239': 0.22605885969882933, '241': 0.6213720451208846, '243': 0.5100815295768456, '247': 0.42777291912240006, '249': 0.4382064049546538, '251': 0.5912308638277075, '253': 0.2550407647884228, '255': 1.0468264118361172, '257': 0.5552933015166115, '259': 0.405746671254309, '261': 0.5552933015166115, '263': 1.0375522022074473, '265': 0.7813521612154408, '267': 0.9227838580526571, '269': 0.5986502315306435, '271': 0.535585606055688, '273': 0.8346788665802929, '275': 0.9181467532383222, '35': 0.00695565722150244, '40': 0.2988614052838882, '50': 0.23741976649394997, '60': 0.28564565656303353, '601': 0.00046371048143349603, '61': 10.549645307852751} + + self.leave(p) + + + # 33 district_metering_area + + + def test_calculate_district_metering_area_for_nodes(self): + p = 'test_calculate_district_metering_area_for_nodes' + read_inp(p, f'./inp/net3.inp', '3') + open_project(p) + + dmas = calculate_district_metering_area_for_nodes(p, get_nodes(p), 3) assert len(dmas) == 3 assert dmas[0] == ['173', '184', '185', '199', '2', '201', '203', '205', '206', '207', '208', '209', '211', '213', '215', '217', '219', '225', '229', '231', '237', '239', '241', '243', '247', '249', '251', '253', '255', '273', '275', '50'] assert dmas[1] == ['1', '10', '101', '103', '109', '111', '113', '161', '163', '164', '166', '167', '169', '171', '179', '181', '183', '187', '189', '191', '193', '195', '197', '204', '265', '267', '269', '271', '35', '40', 'Lake'] diff --git a/tjnetwork.py b/tjnetwork.py index 043e215..d5361e1 100644 --- a/tjnetwork.py +++ b/tjnetwork.py @@ -1009,7 +1009,7 @@ def calculate_demand_to_region(name: str, demand: float, region: str) -> dict[st return api.calculate_demand_to_region(name, demand, region) def calculate_demand_to_network(name: str, demand: float) -> dict[str, float]: - return {} + return api.calculate_demand_to_network(name, demand) def get_region_schema(name: str) -> dict[str, dict[str, Any]]: return api.get_region_schema(name) From 7054dd5457a8b55ed319ea2f6c6437cba1d5a260 Mon Sep 17 00:00:00 2001 From: "WQY\\qiong" Date: Sat, 13 May 2023 08:37:05 +0800 Subject: [PATCH 05/18] Add more api for dma --- api/__init__.py | 2 +- api/s33_district_metering_area.py | 14 +++++++++++++- test_tjnetwork.py | 32 +++++++++++++++++++++++++++++++ tjnetwork.py | 8 ++++---- 4 files changed, 50 insertions(+), 6 deletions(-) diff --git a/api/__init__.py b/api/__init__.py index 5239992..a9088e5 100644 --- a/api/__init__.py +++ b/api/__init__.py @@ -145,7 +145,7 @@ from .s32_region import get_region_schema, get_region, set_region, add_region, d from .s32_water_distribution import calculate_demand_to_nodes, calculate_demand_to_region, calculate_demand_to_network from .s33_district_metering_area import PARTITION_TYPE_RB, PARTITION_TYPE_KWAY -from .s33_district_metering_area import calculate_district_metering_area +from .s33_district_metering_area import calculate_district_metering_area_for_nodes, calculate_district_metering_area_for_region, calculate_district_metering_area_for_network from .s34_service_area import calculate_service_area diff --git a/api/s33_district_metering_area.py b/api/s33_district_metering_area.py index d907374..592d5df 100644 --- a/api/s33_district_metering_area.py +++ b/api/s33_district_metering_area.py @@ -1,6 +1,8 @@ import ctypes import os from .database import * +from .s0_base import get_nodes +from .s32_region_util import get_nodes_in_region from .s32_region_util import Topology @@ -8,7 +10,7 @@ PARTITION_TYPE_RB = 0 PARTITION_TYPE_KWAY = 1 -def calculate_district_metering_area(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: @@ -83,3 +85,13 @@ def calculate_district_metering_area(name: str, nodes: list[str], part_count: in dmas[c_out_part[i]].append(t_node_list[i]) return dmas + + +def calculate_district_metering_area_for_region(name: str, region: str, part_count: int = 1, part_type: int = PARTITION_TYPE_RB) -> list[list[str]]: + nodes = get_nodes_in_region(name, region) + return calculate_district_metering_area_for_nodes(name, nodes, part_count, part_type) + + +def calculate_district_metering_area_for_network(name: str, part_count: int = 1, part_type: int = PARTITION_TYPE_RB) -> list[list[str]]: + nodes = get_nodes(name) + return calculate_district_metering_area_for_nodes(name, nodes, part_count, part_type) diff --git a/test_tjnetwork.py b/test_tjnetwork.py index 66bea40..c2aa259 100644 --- a/test_tjnetwork.py +++ b/test_tjnetwork.py @@ -5882,6 +5882,38 @@ class TestApi: self.leave(p) + def test_calculate_district_metering_area_for_region(self): + p = 'test_calculate_district_metering_area_for_region' + read_inp(p, f'./inp/net3.inp', '3') + open_project(p) + + assert get_node_coord(p, '177') == { 'x' : 0.0, 'y' : 0.0 } + + add_region(p, ChangeSet({'id': 'r', 'boundary': [(-10000.0, -10000.0), (10000.0, -10000.0), (10000.0, 10000.0), (-10000.0, 10000.0), (-10000.0, -10000.0)]})) + + nodes = get_nodes_in_region(p, 'r') + assert len(nodes) == 97 + + dmas = calculate_district_metering_area_for_region(p, 'r', 3) + assert dmas == [['10', '60', '601', '61', '101', '103', '105', '107', '109', '111', '113', '115', '117', '119', '120', '121', '123', '125', '145', '147', '149', '151', '153', '157', '159', '197', '257', '259', '261', '263', 'River', 'Lake'], ['35', '40', '161', '163', '164', '166', '167', '169', '171', '173', '179', '181', '183', '184', '185', '187', '189', '191', '193', '195', '199', '201', '203', '204', '205', '207', '265', '267', '269', '271', '273', '275', '1', '177'], ['15', '20', '50', '127', '129', '131', '139', '141', '143', '206', '208', '209', '211', '213', '215', '217', '219', '225', '229', '231', '237', '239', '241', '243', '247', '249', '251', '253', '255', '2', '3']] + + self.leave(p) + + + def test_calculate_district_metering_area_for_network(self): + p = 'test_calculate_district_metering_area_for_region' + read_inp(p, f'./inp/net3.inp', '3') + open_project(p) + + dmas = calculate_district_metering_area_for_network(p, 3) + assert len(dmas) == 3 + assert dmas[0] == ['173', '184', '185', '199', '2', '201', '203', '205', '206', '207', '208', '209', '211', '213', '215', '217', '219', '225', '229', '231', '237', '239', '241', '243', '247', '249', '251', '253', '255', '273', '275', '50'] + assert dmas[1] == ['1', '10', '101', '103', '109', '111', '113', '161', '163', '164', '166', '167', '169', '171', '179', '181', '183', '187', '189', '191', '193', '195', '197', '204', '265', '267', '269', '271', '35', '40', 'Lake'] + assert dmas[2] == ['105', '107', '115', '117', '119', '120', '121', '123', '125', '127', '129', '131', '139', '141', '143', '145', '147', '149', '15', '151', '153', '157', '159', '20', '257', '259', '261', '263', '3', '60', '601', '61', 'River'] + + self.leave(p) + + # 34 service_area diff --git a/tjnetwork.py b/tjnetwork.py index d5361e1..2a26955 100644 --- a/tjnetwork.py +++ b/tjnetwork.py @@ -826,7 +826,7 @@ def set_option_v3(name: str, cs: ChangeSet) -> ChangeSet: # coord 24.[COORDINATES] ############################################################ -def get_node_coord(name: str, node_id: str) -> dict[str, float] | None: +def get_node_coord(name: str, node_id: str) -> dict[str, float]: return api.get_node_coord(name, node_id) @@ -1033,13 +1033,13 @@ def delete_region(name: str, cs: ChangeSet) -> ChangeSet: ############################################################ 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]]: - return api.calculate_district_metering_area(name, nodes, part_count, part_type) + return api.calculate_district_metering_area_for_nodes(name, nodes, part_count, part_type) def calculate_district_metering_area_for_region(name: str, region: str, part_count: int = 1, part_type: int = PARTITION_TYPE_RB) -> list[list[str]]: - return [] + return api.calculate_district_metering_area_for_region(name, region, part_count, part_type) def calculate_district_metering_area_for_network(name: str, part_count: int = 1, part_type: int = PARTITION_TYPE_RB) -> list[list[str]]: - return [] + return api.calculate_district_metering_area_for_network(name, part_count, part_type) def get_district_metering_area_schema(name: str) -> dict[str, dict[str, Any]]: return {}#api.get_district_metering_area_schema(name) From 99775af649f705388ff162daa6044fb332aa5110 Mon Sep 17 00:00:00 2001 From: "WQY\\qiong" Date: Sat, 13 May 2023 17:59:01 +0800 Subject: [PATCH 06/18] Fix sql ddl --- create_template.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/create_template.py b/create_template.py index 80f2126..0887059 100644 --- a/create_template.py +++ b/create_template.py @@ -40,7 +40,7 @@ sql_create = [ sql_drop = [ "script/sql/drop/operation.sql", - "script/sql/create/extension_data.sql", + "script/sql/drop/extension_data.sql", "script/sql/drop/32.region.sql", "script/sql/drop/31.scada_element.sql", "script/sql/drop/30.scada_device_data.sql", From 6d30bf7d70ebabdc64450ec68960dd1848b2a06e Mon Sep 17 00:00:00 2001 From: "WQY\\qiong" Date: Sat, 13 May 2023 18:00:52 +0800 Subject: [PATCH 07/18] Add table for sub region type --- script/sql/create/32.region.sql | 3 +++ script/sql/create/33.dma.sql | 5 +++++ script/sql/create/34.sa.sql | 6 ++++++ script/sql/create/35.vd.sql | 5 +++++ script/sql/create/36.wda.sql | 5 +++++ script/sql/drop/32.region.sql | 2 ++ script/sql/drop/33.dma.sql | 1 + script/sql/drop/34.sa.sql | 1 + script/sql/drop/35.vd.sql | 1 + script/sql/drop/36.wda.sql | 1 + 10 files changed, 30 insertions(+) create mode 100644 script/sql/create/33.dma.sql create mode 100644 script/sql/create/34.sa.sql create mode 100644 script/sql/create/35.vd.sql create mode 100644 script/sql/create/36.wda.sql create mode 100644 script/sql/drop/33.dma.sql create mode 100644 script/sql/drop/34.sa.sql create mode 100644 script/sql/drop/35.vd.sql create mode 100644 script/sql/drop/36.wda.sql diff --git a/script/sql/create/32.region.sql b/script/sql/create/32.region.sql index b4ec745..9b59a10 100644 --- a/script/sql/create/32.region.sql +++ b/script/sql/create/32.region.sql @@ -1,6 +1,9 @@ +create type region_type as enum ('NONE', 'DMA', 'SA', 'VD', 'WDA'); + create table region ( id text primary key +, r_type region_type not null default 'NONE' , boundary geometry not null unique ); diff --git a/script/sql/create/33.dma.sql b/script/sql/create/33.dma.sql new file mode 100644 index 0000000..85df39a --- /dev/null +++ b/script/sql/create/33.dma.sql @@ -0,0 +1,5 @@ +create table region_dma +( + id text primary key references region(id) +, parent text references region_dma(id) +); diff --git a/script/sql/create/34.sa.sql b/script/sql/create/34.sa.sql new file mode 100644 index 0000000..5e9a4dc --- /dev/null +++ b/script/sql/create/34.sa.sql @@ -0,0 +1,6 @@ +create table region_sa +( + id text primary key references region(id) +, source varchar(32) not null -- references _node(id) +, time_index integer not null +); diff --git a/script/sql/create/35.vd.sql b/script/sql/create/35.vd.sql new file mode 100644 index 0000000..7878151 --- /dev/null +++ b/script/sql/create/35.vd.sql @@ -0,0 +1,5 @@ +create table region_vd +( + id text primary key references region(id) +, source varchar(32) not null -- references _node(id) +); diff --git a/script/sql/create/36.wda.sql b/script/sql/create/36.wda.sql new file mode 100644 index 0000000..6492d1a --- /dev/null +++ b/script/sql/create/36.wda.sql @@ -0,0 +1,5 @@ +create table region_wda +( + id text primary key references region(id) +, demand float8 not null default 0.0 +); diff --git a/script/sql/drop/32.region.sql b/script/sql/drop/32.region.sql index 693aa07..9b38883 100644 --- a/script/sql/drop/32.region.sql +++ b/script/sql/drop/32.region.sql @@ -11,3 +11,5 @@ drop table if exists temp_region; drop index if exists region_gist; drop table if exists region; + +drop type if exists region_type; diff --git a/script/sql/drop/33.dma.sql b/script/sql/drop/33.dma.sql new file mode 100644 index 0000000..04469bb --- /dev/null +++ b/script/sql/drop/33.dma.sql @@ -0,0 +1 @@ +drop table if exists region_dma; diff --git a/script/sql/drop/34.sa.sql b/script/sql/drop/34.sa.sql new file mode 100644 index 0000000..ab8d9a7 --- /dev/null +++ b/script/sql/drop/34.sa.sql @@ -0,0 +1 @@ +drop table if exists region_sa; diff --git a/script/sql/drop/35.vd.sql b/script/sql/drop/35.vd.sql new file mode 100644 index 0000000..bc87202 --- /dev/null +++ b/script/sql/drop/35.vd.sql @@ -0,0 +1 @@ +drop table if exists region_vd; diff --git a/script/sql/drop/36.wda.sql b/script/sql/drop/36.wda.sql new file mode 100644 index 0000000..119650d --- /dev/null +++ b/script/sql/drop/36.wda.sql @@ -0,0 +1 @@ +drop table if exists region_wda; From b06d8260ff6a1a61ace8d9830b7e736972d23350 Mon Sep 17 00:00:00 2001 From: "WQY\\qiong" Date: Sat, 13 May 2023 18:01:19 +0800 Subject: [PATCH 08/18] Add script item to create/drop sub region table --- create_template.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/create_template.py b/create_template.py index 0887059..27f10b9 100644 --- a/create_template.py +++ b/create_template.py @@ -34,6 +34,10 @@ sql_create = [ "script/sql/create/30.scada_device_data.sql", "script/sql/create/31.scada_element.sql", "script/sql/create/32.region.sql", + "script/sql/create/33.dma.sql", + "script/sql/create/34.sa.sql", + "script/sql/create/35.vd.sql", + "script/sql/create/36.wda.sql", "script/sql/create/extension_data.sql", "script/sql/create/operation.sql" ] @@ -41,6 +45,10 @@ sql_create = [ sql_drop = [ "script/sql/drop/operation.sql", "script/sql/drop/extension_data.sql", + "script/sql/drop/36.wda.sql", + "script/sql/drop/35.vd.sql", + "script/sql/drop/34.sa.sql", + "script/sql/drop/33.dma.sql", "script/sql/drop/32.region.sql", "script/sql/drop/31.scada_element.sql", "script/sql/drop/30.scada_device_data.sql", From 48ecdf7285bae02c8001e12be1c4085d0d46143f Mon Sep 17 00:00:00 2001 From: "WQY\\qiong" Date: Sat, 13 May 2023 18:01:46 +0800 Subject: [PATCH 09/18] Rename file --- api/__init__.py | 8 ++++---- api/{s33_district_metering_area.py => s33_dma.py} | 0 api/{s34_service_area.py => s34_sa.py} | 0 api/{s35_virtual_district.py => s35_vd.py} | 0 4 files changed, 4 insertions(+), 4 deletions(-) rename api/{s33_district_metering_area.py => s33_dma.py} (100%) rename api/{s34_service_area.py => s34_sa.py} (100%) rename api/{s35_virtual_district.py => s35_vd.py} (100%) diff --git a/api/__init__.py b/api/__init__.py index a9088e5..07c3f12 100644 --- a/api/__init__.py +++ b/api/__init__.py @@ -144,9 +144,9 @@ from .s32_region import get_region_schema, get_region, set_region, add_region, d from .s32_water_distribution import calculate_demand_to_nodes, calculate_demand_to_region, calculate_demand_to_network -from .s33_district_metering_area import PARTITION_TYPE_RB, PARTITION_TYPE_KWAY -from .s33_district_metering_area import calculate_district_metering_area_for_nodes, calculate_district_metering_area_for_region, calculate_district_metering_area_for_network +from .s33_dma import PARTITION_TYPE_RB, PARTITION_TYPE_KWAY +from .s33_dma import calculate_district_metering_area_for_nodes, calculate_district_metering_area_for_region, calculate_district_metering_area_for_network -from .s34_service_area import calculate_service_area +from .s34_sa import calculate_service_area -from .s35_virtual_district import calculate_virtual_district +from .s35_vd import calculate_virtual_district diff --git a/api/s33_district_metering_area.py b/api/s33_dma.py similarity index 100% rename from api/s33_district_metering_area.py rename to api/s33_dma.py diff --git a/api/s34_service_area.py b/api/s34_sa.py similarity index 100% rename from api/s34_service_area.py rename to api/s34_sa.py diff --git a/api/s35_virtual_district.py b/api/s35_vd.py similarity index 100% rename from api/s35_virtual_district.py rename to api/s35_vd.py From a4ea72fffa42301173b40475b6ef0cf432174980 Mon Sep 17 00:00:00 2001 From: "WQY\\qiong" Date: Sat, 13 May 2023 18:02:29 +0800 Subject: [PATCH 10/18] Add wda python file --- api/s36_wda.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 api/s36_wda.py diff --git a/api/s36_wda.py b/api/s36_wda.py new file mode 100644 index 0000000..e69de29 From 553dad1d2c5bd2acb22ac3542b4ba5c0e82dd582 Mon Sep 17 00:00:00 2001 From: "WQY\\qiong" Date: Sat, 13 May 2023 18:09:17 +0800 Subject: [PATCH 11/18] Add nodes for generated region --- script/sql/create/33.dma.sql | 1 + script/sql/create/34.sa.sql | 3 ++- script/sql/create/35.vd.sql | 3 ++- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/script/sql/create/33.dma.sql b/script/sql/create/33.dma.sql index 85df39a..4644a11 100644 --- a/script/sql/create/33.dma.sql +++ b/script/sql/create/33.dma.sql @@ -2,4 +2,5 @@ create table region_dma ( id text primary key references region(id) , parent text references region_dma(id) +, nodes text not null ); diff --git a/script/sql/create/34.sa.sql b/script/sql/create/34.sa.sql index 5e9a4dc..626b472 100644 --- a/script/sql/create/34.sa.sql +++ b/script/sql/create/34.sa.sql @@ -1,6 +1,7 @@ create table region_sa ( id text primary key references region(id) -, source varchar(32) not null -- references _node(id) , time_index integer not null +, source varchar(32) not null -- references _node(id) +, nodes text not null ); diff --git a/script/sql/create/35.vd.sql b/script/sql/create/35.vd.sql index 7878151..29bd4f5 100644 --- a/script/sql/create/35.vd.sql +++ b/script/sql/create/35.vd.sql @@ -1,5 +1,6 @@ create table region_vd ( id text primary key references region(id) -, source varchar(32) not null -- references _node(id) +, center varchar(32) not null -- references _node(id) +, nodes text not null ); From ee3100a61de68fce3250ca1d4e72ae27e40e5262 Mon Sep 17 00:00:00 2001 From: "WQY\\qiong" Date: Sat, 13 May 2023 19:12:07 +0800 Subject: [PATCH 12/18] Clean --- script/sql/create/32.region.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/sql/create/32.region.sql b/script/sql/create/32.region.sql index 9b59a10..78e0bfd 100644 --- a/script/sql/create/32.region.sql +++ b/script/sql/create/32.region.sql @@ -3,8 +3,8 @@ create type region_type as enum ('NONE', 'DMA', 'SA', 'VD', 'WDA'); create table region ( id text primary key -, r_type region_type not null default 'NONE' , boundary geometry not null unique +, r_type region_type not null default 'NONE' ); create index region_gist on region using gist(boundary); From 48f4e58029002644606ee838d903f2f76afab111 Mon Sep 17 00:00:00 2001 From: "WQY\\qiong" Date: Sat, 13 May 2023 20:06:55 +0800 Subject: [PATCH 13/18] Add new api, new implementation --- tjnetwork.py | 64 ++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 59 insertions(+), 5 deletions(-) diff --git a/tjnetwork.py b/tjnetwork.py index 2a26955..294bcd3 100644 --- a/tjnetwork.py +++ b/tjnetwork.py @@ -1027,6 +1027,9 @@ def add_region(name: str, cs: ChangeSet) -> ChangeSet: def delete_region(name: str, cs: ChangeSet) -> ChangeSet: return api.delete_region(name, cs) +def get_all_regions(name: str) -> list[str]: + return [] + ############################################################ # district_metering_area 33 @@ -1042,20 +1045,29 @@ def calculate_district_metering_area_for_network(name: str, part_count: int = 1, return api.calculate_district_metering_area_for_network(name, part_count, part_type) def get_district_metering_area_schema(name: str) -> dict[str, dict[str, Any]]: - return {}#api.get_district_metering_area_schema(name) + return {} def get_district_metering_area(name: str, id: str) -> dict[str, Any]: - return {}#api.get_district_metering_area(name, id) + return {} def set_district_metering_area(name: str, cs: ChangeSet) -> ChangeSet: - return ChangeSet()#api.set_district_metering_area(name, cs) + return ChangeSet() # example: add_district_metering_area(p, ChangeSet({'id': 'r', 'boundary': [(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 0.0)]})) def add_district_metering_area(name: str, cs: ChangeSet) -> ChangeSet: - return ChangeSet()#api.add_district_metering_area(name, cs) + return ChangeSet() def delete_district_metering_area(name: str, cs: ChangeSet) -> ChangeSet: - return ChangeSet()#api.delete_district_metering_area(name, cs) + return ChangeSet() + +def get_all_district_metering_areas(name: str) -> list[dict[str, Any]]: + return [] + +def generate_district_metering_area(name: str, part_count: int = 1, part_type: int = PARTITION_TYPE_RB) -> ChangeSet: + return ChangeSet() + +def generate_sub_district_metering_area(name: str, dma: str, part_count: int = 1, part_type: int = PARTITION_TYPE_RB) -> ChangeSet: + return ChangeSet() ############################################################ @@ -1065,6 +1077,27 @@ def delete_district_metering_area(name: str, cs: ChangeSet) -> ChangeSet: def calculate_service_area(name: str, time_index: int = 0) -> dict[str, Any]: return api.calculate_service_area(name, time_index) +def get_service_area_schema(name: str) -> dict[str, dict[str, Any]]: + return {} + +def get_service_area(name: str, id: str) -> dict[str, Any]: + return {} + +def set_service_area(name: str, cs: ChangeSet) -> ChangeSet: + return ChangeSet() + +def add_service_area(name: str, cs: ChangeSet) -> ChangeSet: + return ChangeSet() + +def delete_service_area(name: str, cs: ChangeSet) -> ChangeSet: + return ChangeSet() + +def get_all_service_areas(name: str) -> list[dict[str, Any]]: + return [] + +def generate_service_area(name: str) -> ChangeSet: + return ChangeSet() + ############################################################ # virtual_district 35 @@ -1072,3 +1105,24 @@ def calculate_service_area(name: str, time_index: int = 0) -> dict[str, Any]: def calculate_virtual_district(name: str, centers: list[str]) -> dict[str, list[Any]]: return api.calculate_virtual_district(name, centers) + +def get_virtual_district_schema(name: str) -> dict[str, dict[str, Any]]: + return {} + +def get_virtual_district(name: str, id: str) -> dict[str, Any]: + return {} + +def set_virtual_district(name: str, cs: ChangeSet) -> ChangeSet: + return ChangeSet() + +def add_virtual_district(name: str, cs: ChangeSet) -> ChangeSet: + return ChangeSet() + +def delete_virtual_district(name: str, cs: ChangeSet) -> ChangeSet: + return ChangeSet() + +def get_all_virtual_district(name: str) -> list[dict[str, Any]]: + return [] + +def generate_virtual_district(name: str, centers: list[str]) -> ChangeSet: + return ChangeSet() From 16cb5ccb8226201be6d64c01034f223ba45ffff5 Mon Sep 17 00:00:00 2001 From: "WQY\\qiong" Date: Sun, 14 May 2023 08:55:10 +0800 Subject: [PATCH 14/18] Update script --- clean_projects.py | 1 + 1 file changed, 1 insertion(+) diff --git a/clean_projects.py b/clean_projects.py index 20cd7e4..e0f0e44 100644 --- a/clean_projects.py +++ b/clean_projects.py @@ -2,3 +2,4 @@ from tjnetwork import * if __name__ == '__main__': clean_project() + delete_project('project') From 28cce528e7a781056e5619a0a640262a327944a9 Mon Sep 17 00:00:00 2001 From: "WQY\\qiong" Date: Sun, 14 May 2023 08:55:47 +0800 Subject: [PATCH 15/18] Clean code --- api/__init__.py | 4 +- ...2_water_distribution.py => s36_wda_cal.py} | 0 test_tjnetwork.py | 86 +++++++++---------- tjnetwork.py | 32 ++++--- 4 files changed, 67 insertions(+), 55 deletions(-) rename api/{s32_water_distribution.py => s36_wda_cal.py} (100%) diff --git a/api/__init__.py b/api/__init__.py index 07c3f12..37ab551 100644 --- a/api/__init__.py +++ b/api/__init__.py @@ -142,11 +142,11 @@ from .s32_region_util import get_nodes_in_boundary, get_nodes_in_region, calcula from .s32_region import get_region_schema, get_region, set_region, add_region, delete_region -from .s32_water_distribution import calculate_demand_to_nodes, calculate_demand_to_region, calculate_demand_to_network - from .s33_dma import PARTITION_TYPE_RB, PARTITION_TYPE_KWAY from .s33_dma import calculate_district_metering_area_for_nodes, calculate_district_metering_area_for_region, calculate_district_metering_area_for_network from .s34_sa import calculate_service_area from .s35_vd import calculate_virtual_district + +from .s36_wda_cal import calculate_demand_to_nodes, calculate_demand_to_region, calculate_demand_to_network diff --git a/api/s32_water_distribution.py b/api/s36_wda_cal.py similarity index 100% rename from api/s32_water_distribution.py rename to api/s36_wda_cal.py diff --git a/test_tjnetwork.py b/test_tjnetwork.py index c2aa259..ee7d7ff 100644 --- a/test_tjnetwork.py +++ b/test_tjnetwork.py @@ -5822,49 +5822,6 @@ class TestApi: self.leave(p) - # 32 water_distribution - - - def test_calculate_demand_to_nodes(self): - p = 'test_calculate_demand_to_nodes' - read_inp(p, f'./inp/net3.inp', '3') - open_project(p) - - vds = calculate_virtual_district(p, ['107', '139', '267', '211'])['virtual_districts'] - result = calculate_demand_to_nodes(p, 100.0, vds[0]['nodes']) - assert result == {'10': 17.357291284684024, '101': 22.112211221122113, '103': 6.466202175773133, '105': 8.232489915658233, '107': 4.180418041804181, '109': 7.260726072607261, '111': 3.862608483070529, '115': 6.466202175773133, '117': 5.738907224055739, '119': 0.892311453367559, '120': 3.9665077618873, '257': 2.9275149737195942, '259': 2.1391027991688056, '261': 2.9275149737195942, '263': 5.469991443588803} - - self.leave(p) - - - def test_calculate_demand_to_region(self): - p = 'test_calculate_demand_to_region' - read_inp(p, f'./inp/net3.inp', '3') - open_project(p) - - vds = calculate_virtual_district(p, ['107', '139', '267', '211'])['virtual_districts'] - nodes = vds[0]['nodes'] - boundary = calculate_boundary(p, nodes) - boundary = inflate_boundary(p, boundary, 0.1) - add_region(p, ChangeSet({'id': 'r', 'boundary': boundary})) - - result = calculate_demand_to_region(p, 100.0, 'r') - assert result == {'10': 17.357291284684024, '101': 22.112211221122113, '103': 6.466202175773133, '105': 8.232489915658233, '107': 4.180418041804181, '109': 7.260726072607261, '111': 3.862608483070529, '115': 6.466202175773133, '117': 5.738907224055739, '119': 0.892311453367559, '120': 3.9665077618873, '257': 2.9275149737195942, '259': 2.1391027991688056, '261': 2.9275149737195942, '263': 5.469991443588803} - - self.leave(p) - - - def test_calculate_demand_to_network(self): - p = 'test_calculate_demand_to_network' - read_inp(p, f'./inp/net3.inp', '3') - open_project(p) - - result = calculate_demand_to_network(p, 100.0) - assert result == {'10': 3.2923444181778216, '101': 4.194261304565972, '103': 1.226514223391597, '105': 1.561545046227298, '107': 0.7929449232512782, '109': 1.3772201298574833, '111': 1.7690554866687873, '113': 1.2381069854274345, '115': 1.6902247048250931, '117': 1.088560355165132, '119': 1.9174428407275061, '120': 1.2276734995951808, '121': 1.502421959844527, '123': 10.897196313687157, '125': 2.0240962514572103, '127': 1.141887060529984, '129': 2.3486935884606575, '131': 1.5024219598445272, '139': 1.1129051554403906, '141': 1.6137124753885663, '143': 0.7071584841860814, '145': 1.3238934244926313, '147': 0.7141141414075839, '149': 0.44052495736182123, '15': 0.38256114718263423, '151': 1.3099821100496263, '153': 1.328530529306966, '157': 1.1569576511765725, '159': 1.1384092319192327, '161': 1.0966752885902182, '163': 0.41270232847581145, '164': 0.14838735405871872, '166': 0.11360906795120652, '167': 0.01391131444300488, '169': 0.5949405476791755, '171': 0.48225890069083593, '173': 0.9390137249028294, '179': 0.32459733700344723, '181': 0.06723801980785693, '183': 0.4799403482836684, '184': 1.0734665789944717, '185': 0.4486167052628357, '187': 0.732639375140852, '189': 0.8638926269106031, '191': 0.9807476682318441, '193': 0.8601829430591352, '195': 0.6167349403065497, '197': 0.9135096484239872, '199': 1.2415848140381855, '20': 0.20496003279360525, '201': 0.2434480027525854, '203': 0.02782262888600976, '204': 0.33037053249729426, '205': 1.4780771595692686, '206': 0.22258103108807809, '207': 0.7141141414075839, '208': 0.3234380607998635, '209': 0.48573672930158707, '211': 0.9923404302676815, '213': 1.7331179243576913, '215': 1.378379406061067, '217': 1.2218771185772621, '219': 0.47530324346933345, '225': 0.3616941755181269, '229': 1.1476834415479027, '231': 0.4544362718048261, '237': 0.7836707136226083, '239': 0.22605885969882933, '241': 0.6213720451208846, '243': 0.5100815295768456, '247': 0.42777291912240006, '249': 0.4382064049546538, '251': 0.5912308638277075, '253': 0.2550407647884228, '255': 1.0468264118361172, '257': 0.5552933015166115, '259': 0.405746671254309, '261': 0.5552933015166115, '263': 1.0375522022074473, '265': 0.7813521612154408, '267': 0.9227838580526571, '269': 0.5986502315306435, '271': 0.535585606055688, '273': 0.8346788665802929, '275': 0.9181467532383222, '35': 0.00695565722150244, '40': 0.2988614052838882, '50': 0.23741976649394997, '60': 0.28564565656303353, '601': 0.00046371048143349603, '61': 10.549645307852751} - - self.leave(p) - - # 33 district_metering_area @@ -5962,5 +5919,48 @@ class TestApi: self.leave(p) + # 36 water_distribution + + + def test_calculate_demand_to_nodes(self): + p = 'test_calculate_demand_to_nodes' + read_inp(p, f'./inp/net3.inp', '3') + open_project(p) + + vds = calculate_virtual_district(p, ['107', '139', '267', '211'])['virtual_districts'] + result = calculate_demand_to_nodes(p, 100.0, vds[0]['nodes']) + assert result == {'10': 17.357291284684024, '101': 22.112211221122113, '103': 6.466202175773133, '105': 8.232489915658233, '107': 4.180418041804181, '109': 7.260726072607261, '111': 3.862608483070529, '115': 6.466202175773133, '117': 5.738907224055739, '119': 0.892311453367559, '120': 3.9665077618873, '257': 2.9275149737195942, '259': 2.1391027991688056, '261': 2.9275149737195942, '263': 5.469991443588803} + + self.leave(p) + + + def test_calculate_demand_to_region(self): + p = 'test_calculate_demand_to_region' + read_inp(p, f'./inp/net3.inp', '3') + open_project(p) + + vds = calculate_virtual_district(p, ['107', '139', '267', '211'])['virtual_districts'] + nodes = vds[0]['nodes'] + boundary = calculate_boundary(p, nodes) + boundary = inflate_boundary(p, boundary, 0.1) + add_region(p, ChangeSet({'id': 'r', 'boundary': boundary})) + + result = calculate_demand_to_region(p, 100.0, 'r') + assert result == {'10': 17.357291284684024, '101': 22.112211221122113, '103': 6.466202175773133, '105': 8.232489915658233, '107': 4.180418041804181, '109': 7.260726072607261, '111': 3.862608483070529, '115': 6.466202175773133, '117': 5.738907224055739, '119': 0.892311453367559, '120': 3.9665077618873, '257': 2.9275149737195942, '259': 2.1391027991688056, '261': 2.9275149737195942, '263': 5.469991443588803} + + self.leave(p) + + + def test_calculate_demand_to_network(self): + p = 'test_calculate_demand_to_network' + read_inp(p, f'./inp/net3.inp', '3') + open_project(p) + + result = calculate_demand_to_network(p, 100.0) + assert result == {'10': 3.2923444181778216, '101': 4.194261304565972, '103': 1.226514223391597, '105': 1.561545046227298, '107': 0.7929449232512782, '109': 1.3772201298574833, '111': 1.7690554866687873, '113': 1.2381069854274345, '115': 1.6902247048250931, '117': 1.088560355165132, '119': 1.9174428407275061, '120': 1.2276734995951808, '121': 1.502421959844527, '123': 10.897196313687157, '125': 2.0240962514572103, '127': 1.141887060529984, '129': 2.3486935884606575, '131': 1.5024219598445272, '139': 1.1129051554403906, '141': 1.6137124753885663, '143': 0.7071584841860814, '145': 1.3238934244926313, '147': 0.7141141414075839, '149': 0.44052495736182123, '15': 0.38256114718263423, '151': 1.3099821100496263, '153': 1.328530529306966, '157': 1.1569576511765725, '159': 1.1384092319192327, '161': 1.0966752885902182, '163': 0.41270232847581145, '164': 0.14838735405871872, '166': 0.11360906795120652, '167': 0.01391131444300488, '169': 0.5949405476791755, '171': 0.48225890069083593, '173': 0.9390137249028294, '179': 0.32459733700344723, '181': 0.06723801980785693, '183': 0.4799403482836684, '184': 1.0734665789944717, '185': 0.4486167052628357, '187': 0.732639375140852, '189': 0.8638926269106031, '191': 0.9807476682318441, '193': 0.8601829430591352, '195': 0.6167349403065497, '197': 0.9135096484239872, '199': 1.2415848140381855, '20': 0.20496003279360525, '201': 0.2434480027525854, '203': 0.02782262888600976, '204': 0.33037053249729426, '205': 1.4780771595692686, '206': 0.22258103108807809, '207': 0.7141141414075839, '208': 0.3234380607998635, '209': 0.48573672930158707, '211': 0.9923404302676815, '213': 1.7331179243576913, '215': 1.378379406061067, '217': 1.2218771185772621, '219': 0.47530324346933345, '225': 0.3616941755181269, '229': 1.1476834415479027, '231': 0.4544362718048261, '237': 0.7836707136226083, '239': 0.22605885969882933, '241': 0.6213720451208846, '243': 0.5100815295768456, '247': 0.42777291912240006, '249': 0.4382064049546538, '251': 0.5912308638277075, '253': 0.2550407647884228, '255': 1.0468264118361172, '257': 0.5552933015166115, '259': 0.405746671254309, '261': 0.5552933015166115, '263': 1.0375522022074473, '265': 0.7813521612154408, '267': 0.9227838580526571, '269': 0.5986502315306435, '271': 0.535585606055688, '273': 0.8346788665802929, '275': 0.9181467532383222, '35': 0.00695565722150244, '40': 0.2988614052838882, '50': 0.23741976649394997, '60': 0.28564565656303353, '601': 0.00046371048143349603, '61': 10.549645307852751} + + self.leave(p) + + if __name__ == '__main__': pytest.main() diff --git a/tjnetwork.py b/tjnetwork.py index 294bcd3..7c918f9 100644 --- a/tjnetwork.py +++ b/tjnetwork.py @@ -984,9 +984,13 @@ def get_all_scada_elements(name: str) -> list[dict[str, Any]]: # general_region 32 ############################################################ +# pure geometry function, test point is in polygon def get_nodes_in_boundary(name: str, boundary: list[tuple[float, float]]) -> list[str]: return api.get_nodes_in_boundary(name, boundary) +# if region is general or wda => get_nodes_in_boundary +# if region is dma, sa or vd => get stored nodes in table +# TODO: more test def get_nodes_in_region(name: str, region_id: str) -> list[str]: return api.get_nodes_in_region(name, region_id) @@ -1002,15 +1006,6 @@ def inflate_boundary(name: str, boundary: list[tuple[float, float]], delta: floa def inflate_region(name: str, region_id: str, delta: float = 0.5) -> list[tuple[float, float]]: return api.inflate_region(name, region_id, delta) -def calculate_demand_to_nodes(name: str, demand: float, nodes: list[str]) -> dict[str, float]: - return api.calculate_demand_to_nodes(name, demand, nodes) - -def calculate_demand_to_region(name: str, demand: float, region: str) -> dict[str, float]: - return api.calculate_demand_to_region(name, demand, region) - -def calculate_demand_to_network(name: str, demand: float) -> dict[str, float]: - return api.calculate_demand_to_network(name, demand) - def get_region_schema(name: str) -> dict[str, dict[str, Any]]: return api.get_region_schema(name) @@ -1027,7 +1022,7 @@ def add_region(name: str, cs: ChangeSet) -> ChangeSet: def delete_region(name: str, cs: ChangeSet) -> ChangeSet: return api.delete_region(name, cs) -def get_all_regions(name: str) -> list[str]: +def get_all_regions(name: str) -> list[dict[str, Any]]: return [] @@ -1126,3 +1121,20 @@ def get_all_virtual_district(name: str) -> list[dict[str, Any]]: def generate_virtual_district(name: str, centers: list[str]) -> ChangeSet: return ChangeSet() + + +############################################################ +# water_distribution_area 36 +############################################################ + +def calculate_demand_to_nodes(name: str, demand: float, nodes: list[str]) -> dict[str, float]: + return api.calculate_demand_to_nodes(name, demand, nodes) + +# if region is general or wda => get_nodes_in_boundary +# if region is dma, sa or vd => get stored nodes in table +# TODO: more test +def calculate_demand_to_region(name: str, demand: float, region: str) -> dict[str, float]: + return api.calculate_demand_to_region(name, demand, region) + +def calculate_demand_to_network(name: str, demand: float) -> dict[str, float]: + return api.calculate_demand_to_network(name, demand) From 3247794d5b67accd6210039a35922dbf8d44f750 Mon Sep 17 00:00:00 2001 From: "WQY\\qiong" Date: Sun, 14 May 2023 10:31:49 +0800 Subject: [PATCH 16/18] Support region batch command --- api/batch_exe.py | 7 +++++++ api/sections.py | 1 + 2 files changed, 8 insertions(+) diff --git a/api/batch_exe.py b/api/batch_exe.py index 4b82497..f594297 100644 --- a/api/batch_exe.py +++ b/api/batch_exe.py @@ -30,6 +30,7 @@ from .s27_backdrop import set_backdrop from .s29_scada_device import set_scada_device, add_scada_device, delete_scada_device from .s30_scada_device_data import set_scada_device_data, add_scada_device_data, delete_scada_device_data from .s31_scada_element import set_scada_element, add_scada_element, delete_scada_element +from .s32_region import set_region, add_region, delete_region from .batch_api_cs import rewrite_batch_api @@ -106,6 +107,8 @@ def _execute_add_command(name: str, cs: ChangeSet) -> ChangeSet: return add_scada_device_data(name, cs) elif type == s31_scada_element: return add_scada_element(name, cs) + elif type == s32_region: + return add_region(name, cs) return ChangeSet() @@ -185,6 +188,8 @@ def _execute_update_command(name: str, cs: ChangeSet) -> ChangeSet: return set_scada_device_data(name, cs) elif type == s31_scada_element: return set_scada_element(name, cs) + elif type == s32_region: + return set_region(name, cs) return ChangeSet() @@ -262,6 +267,8 @@ def _execute_delete_command(name: str, cs: ChangeSet) -> ChangeSet: return delete_scada_device_data(name, cs) elif type == s31_scada_element: return delete_scada_element(name, cs) + elif type == s32_region: + return delete_region(name, cs) return ChangeSet() diff --git a/api/sections.py b/api/sections.py index 0a9ee75..5ee8a01 100644 --- a/api/sections.py +++ b/api/sections.py @@ -33,6 +33,7 @@ s28_end = 'end' s29_scada_device = 'scada_device' s30_scada_device_data = 'scada_device_data' s31_scada_element = 'scada_element' +s32_region = 'region' TITLE = 'TITLE' JUNCTIONS = 'JUNCTIONS' From 1bc73f5bd57bf4e4d1bd3a3e99fa1c51f8ee2845 Mon Sep 17 00:00:00 2001 From: "WQY\\qiong" Date: Sun, 14 May 2023 13:12:34 +0800 Subject: [PATCH 17/18] Relax dma parent constraint --- script/sql/create/33.dma.sql | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/script/sql/create/33.dma.sql b/script/sql/create/33.dma.sql index 4644a11..9dd7b51 100644 --- a/script/sql/create/33.dma.sql +++ b/script/sql/create/33.dma.sql @@ -1,6 +1,6 @@ create table region_dma ( id text primary key references region(id) -, parent text references region_dma(id) -, nodes text not null +, parent text --references region_dma(id) +, nodes text not null default '' ); From 58fd285d2590e945906e2cd7969f6a4b9f316cca Mon Sep 17 00:00:00 2001 From: "WQY\\qiong" Date: Sun, 14 May 2023 13:20:54 +0800 Subject: [PATCH 18/18] Support DMA --- api/__init__.py | 9 +- api/batch_exe.py | 7 + api/s33_dma.py | 295 +++++++++++++++++++++++++---------- api/s33_dma_cal.py | 98 ++++++++++++ api/s33_dma_gen.py | 47 ++++++ api/sections.py | 1 + script/sql/create/33.dma.sql | 2 +- test_tjnetwork.py | 251 +++++++++++++++++++++++++++++ tjnetwork.py | 23 ++- 9 files changed, 637 insertions(+), 96 deletions(-) create mode 100644 api/s33_dma_cal.py create mode 100644 api/s33_dma_gen.py diff --git a/api/__init__.py b/api/__init__.py index 37ab551..41455dd 100644 --- a/api/__init__.py +++ b/api/__init__.py @@ -142,8 +142,13 @@ from .s32_region_util import get_nodes_in_boundary, get_nodes_in_region, calcula from .s32_region import get_region_schema, get_region, set_region, add_region, delete_region -from .s33_dma import PARTITION_TYPE_RB, PARTITION_TYPE_KWAY -from .s33_dma import calculate_district_metering_area_for_nodes, calculate_district_metering_area_for_region, calculate_district_metering_area_for_network +from .s33_dma_cal import PARTITION_TYPE_RB, PARTITION_TYPE_KWAY +from .s33_dma_cal import calculate_district_metering_area_for_nodes, calculate_district_metering_area_for_region, calculate_district_metering_area_for_network + +from .s33_dma import get_district_metering_area_schema, get_district_metering_area, set_district_metering_area, add_district_metering_area, delete_district_metering_area +from .s33_dma import get_all_district_metering_area_ids, get_all_district_metering_areas + +from .s33_dma_gen import generate_district_metering_area, generate_sub_district_metering_area from .s34_sa import calculate_service_area diff --git a/api/batch_exe.py b/api/batch_exe.py index f594297..49c5001 100644 --- a/api/batch_exe.py +++ b/api/batch_exe.py @@ -31,6 +31,7 @@ from .s29_scada_device import set_scada_device, add_scada_device, delete_scada_d from .s30_scada_device_data import set_scada_device_data, add_scada_device_data, delete_scada_device_data from .s31_scada_element import set_scada_element, add_scada_element, delete_scada_element from .s32_region import set_region, add_region, delete_region +from .s33_dma import set_district_metering_area, add_district_metering_area, delete_district_metering_area from .batch_api_cs import rewrite_batch_api @@ -109,6 +110,8 @@ def _execute_add_command(name: str, cs: ChangeSet) -> ChangeSet: return add_scada_element(name, cs) elif type == s32_region: return add_region(name, cs) + elif type == s33_dma: + return add_district_metering_area(name, cs) return ChangeSet() @@ -190,6 +193,8 @@ def _execute_update_command(name: str, cs: ChangeSet) -> ChangeSet: return set_scada_element(name, cs) elif type == s32_region: return set_region(name, cs) + elif type == s33_dma: + return set_district_metering_area(name, cs) return ChangeSet() @@ -269,6 +274,8 @@ def _execute_delete_command(name: str, cs: ChangeSet) -> ChangeSet: return delete_scada_element(name, cs) elif type == s32_region: return delete_region(name, cs) + elif type == s33_dma: + return delete_district_metering_area(name, cs) return ChangeSet() diff --git a/api/s33_dma.py b/api/s33_dma.py index 592d5df..838ed0f 100644 --- a/api/s33_dma.py +++ b/api/s33_dma.py @@ -1,97 +1,230 @@ -import ctypes -import os from .database import * -from .s0_base import get_nodes -from .s32_region_util import get_nodes_in_region -from .s32_region_util import Topology +from .s0_base import is_node +from .s32_region_util import to_postgis_polygon +from .s32_region import get_region + +def get_district_metering_area_schema(name: str) -> dict[str, dict[str, Any]]: + return { 'id' : {'type': 'str' , 'optional': False , 'readonly': True }, + 'boundary' : {'type': 'tuple_list' , 'optional': False , 'readonly': False }, + 'parent' : {'type': 'str' , 'optional': True , 'readonly': False }, + 'level' : {'type': 'str' , 'optional': False , 'readonly': True } } -PARTITION_TYPE_RB = 0 -PARTITION_TYPE_KWAY = 1 +def get_district_metering_area(name: str, id: str) -> dict[str, Any]: + dma = get_region(name, id) + if dma == {}: + return {} + r = try_read(name, f"select * from region_dma where id = '{id}'") + if r == None: + return {} + dma['parent'] = r['parent'] + dma['nodes'] = list(eval(r['nodes'])) + dma['level'] = 1 + + if dma['parent'] != None: + parent = dma['parent'] + while parent != None: + parent = read(name, f"select parent from region_dma where id = '{parent}'")['parent'] + dma['level'] += 1 + + return dma + +# no update for nodes +def _set_district_metering_area(name: str, cs: ChangeSet) -> DbChangeSet: + id = cs.operations[0]['id'] + + new_boundary = cs.operations[0]['boundary'] + old_boundary = get_region(name, id)['boundary'] + + new_parent = cs.operations[0]['parent'] + f_new_parent = f"'{new_parent}'" if new_parent != None else 'null' + + new_nodes = cs.operations[0]['nodes'] + str_new_nodes = str(new_nodes).replace("'", "''") + + old = get_district_metering_area(name, id) + old_parent = old['parent'] + f_old_parent = f"'{old_parent}'" if old_parent != None else 'null' + + old_nodes = old['nodes'] + str_old_nodes = str(old_nodes).replace("'", "''") + + redo_sql = f"update region set boundary = st_geomfromtext('{to_postgis_polygon(new_boundary)}') where id = '{id}';" + redo_sql += f"update region_dma set parent = {f_new_parent}, nodes = '{str_new_nodes}' where id = '{id}';" + + undo_sql = f"update region_dma set parent = {f_old_parent}, nodes = '{str_old_nodes}' where id = '{id}';" + undo_sql += f"update region set boundary = st_geomfromtext('{to_postgis_polygon(old_boundary)}') where id = '{id}';" + + redo_cs = g_update_prefix | { 'type': 'district_metering_area', 'id': id, 'boundary': new_boundary, 'parent': new_parent, 'nodes': new_nodes } + undo_cs = g_update_prefix | { 'type': 'district_metering_area', 'id': id, 'boundary': old_boundary, 'parent': old_parent, 'nodes': old_nodes } + + return DbChangeSet(redo_sql, undo_sql, [redo_cs], [undo_cs]) -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] +def set_district_metering_area(name: str, cs: ChangeSet) -> ChangeSet: + ops = cs.operations - lib = ctypes.CDLL(os.path.join(os.getcwd(), 'api', 'CMetis.dll')) + if len(cs.operations) == 0: + return ChangeSet() + + op = ops[0] - METIS_NOPTIONS = 40 - c_options = (ctypes.c_int64 * METIS_NOPTIONS)() + if 'id' not in op: + return ChangeSet() + + dma = get_district_metering_area(name, op['id']) + if dma == {}: + return ChangeSet() - METIS_OK = 1 - result = lib.set_default_options(c_options) - if result != METIS_OK: - return [] + if 'boundary' not in op: + op['boundary'] = dma['boundary'] + else: + b = op['boundary'] + if len(b) < 4 or b[0] != b[-1]: + return ChangeSet() - METIS_OPTION_PTYPE , METIS_OPTION_CONTIG = 0, 13 - c_options[METIS_OPTION_PTYPE] = part_type - c_options[METIS_OPTION_CONTIG] = 1 + if 'parent' not in op: + op['parent'] = dma['parent'] - topology = Topology(name, nodes) - t_nodes = topology.nodes() - t_links = topology.links() - t_node_list = topology.node_list() - t_link_list = topology.link_list() + if op['parent'] != None and get_district_metering_area(name, op['parent']) == {}: + return ChangeSet() - nedges = len(t_link_list) * 2 + if 'nodes' not in op: + op['nodes'] = dma['nodes'] + else: + for node in op['nodes']: + if not is_node(name, node): + return ChangeSet() - c_nvtxs = ctypes.c_int64(len(t_node_list)) - c_ncon = ctypes.c_int64(1) - c_xadj = (ctypes.c_int64 * (c_nvtxs.value + 1))() - c_adjncy = (ctypes.c_int64 * nedges)() - c_vwgt = (ctypes.c_int64 * (c_ncon.value * c_nvtxs.value))() - c_adjwgt = (ctypes.c_int64 * nedges)() - c_vsize = (ctypes.c_int64 * c_nvtxs.value)() - - c_xadj[0] = 0 - - l, n = 0, 0 - c_xadj_i = 1 - for node in t_node_list: - links = t_nodes[node]['links'] - for link in links: - node1 = t_links[link]['node1'] - node2 = t_links[link]['node2'] - c_adjncy[l] = t_node_list.index(node2) if node2 != node else t_node_list.index(node1) - c_adjwgt[l] = 1 - l += 1 - if len(links) > 0: - c_xadj[c_xadj_i] = l # adjncy.size() - c_xadj_i += 1 - c_vwgt[n] = 1 - c_vsize[n] = 1 - n += 1 - - part_func = lib.part_graph_recursive if part_type == PARTITION_TYPE_RB else lib.part_graph_kway - - c_nparts = ctypes.c_int64(part_count) - c_tpwgts = ctypes.POINTER(ctypes.c_double)() - c_ubvec = ctypes.POINTER(ctypes.c_double)() - c_out_edgecut = ctypes.c_int64(0) - c_out_part = (ctypes.c_int64 * c_nvtxs.value)() - result = part_func(ctypes.byref(c_nvtxs), ctypes.byref(c_ncon), c_xadj, c_adjncy, c_vwgt, c_vsize, c_adjwgt, ctypes.byref(c_nparts), c_tpwgts, c_ubvec, c_options, ctypes.byref(c_out_edgecut), c_out_part) - if result != METIS_OK: - return [] - - dmas : list[list[str]]= [] - for i in range(part_count): - dmas.append([]) - for i in range(c_nvtxs.value): - dmas[c_out_part[i]].append(t_node_list[i]) - - return dmas + return execute_command(name, _set_district_metering_area(name, cs)) -def calculate_district_metering_area_for_region(name: str, region: str, part_count: int = 1, part_type: int = PARTITION_TYPE_RB) -> list[list[str]]: - nodes = get_nodes_in_region(name, region) - return calculate_district_metering_area_for_nodes(name, nodes, part_count, part_type) +def _add_district_metering_area(name: str, cs: ChangeSet) -> DbChangeSet: + id = cs.operations[0]['id'] + + boundary = cs.operations[0]['boundary'] + + parent = cs.operations[0]['parent'] + f_parent = f"'{parent}'" if parent != None else 'null' + + nodes = cs.operations[0]['nodes'] + str_nodes = str(nodes).replace("'", "''") + + redo_sql = f"insert into region (id, boundary, r_type) values ('{id}', '{to_postgis_polygon(boundary)}', 'DMA');" + redo_sql += f"insert into region_dma (id, parent, nodes) values ('{id}', {f_parent}, '{str_nodes}');" + + undo_sql = f"delete from region_dma where id = '{id}';" + undo_sql += f"delete from region where id = '{id}';" + + redo_cs = g_add_prefix | { 'type': 'district_metering_area', 'id': id, 'boundary': boundary, 'parent': parent, 'nodes': nodes } + undo_cs = g_delete_prefix | { 'type': 'district_metering_area', 'id': id } + + return DbChangeSet(redo_sql, undo_sql, [redo_cs], [undo_cs]) -def calculate_district_metering_area_for_network(name: str, part_count: int = 1, part_type: int = PARTITION_TYPE_RB) -> list[list[str]]: - nodes = get_nodes(name) - return calculate_district_metering_area_for_nodes(name, nodes, part_count, part_type) +def add_district_metering_area(name: str, cs: ChangeSet) -> ChangeSet: + ops = cs.operations + + if len(cs.operations) == 0: + return ChangeSet() + + op = ops[0] + + if 'id' not in op: + return ChangeSet() + + dma = get_district_metering_area(name, op['id']) + if dma != {}: + return ChangeSet() + + if 'boundary' not in op: + return ChangeSet() + else: + b = op['boundary'] + if len(b) < 4 or b[0] != b[-1]: + return ChangeSet() + + if 'parent' not in op: + op['parent'] = None + + if op['parent'] != None and get_district_metering_area(name, op['parent']) == {}: + return ChangeSet() + + if 'nodes' not in op: + op['nodes'] = [] + else: + for node in op['nodes']: + if not is_node(name, node): + return ChangeSet() + + return execute_command(name, _add_district_metering_area(name, cs)) + + +def _delete_district_metering_area(name: str, cs: ChangeSet) -> DbChangeSet: + id = cs.operations[0]['id'] + dma = get_district_metering_area(name, id) + boundary = dma['boundary'] + parent = dma['parent'] + f_parent = f"'{parent}'" if parent != None else 'null' + nodes = dma['nodes'] + str_nodes = str(nodes).replace("'", "''") + + redo_sql = f"delete from region_dma where id = '{id}';" + redo_sql += f"delete from region where id = '{id}';" + + undo_sql = f"insert into region (id, boundary, r_type) values ('{id}', '{to_postgis_polygon(boundary)}', 'DMA');" + undo_sql += f"insert into region_dma (id, parent, nodes) values ('{id}', {f_parent}, '{str_nodes}');" + + redo_cs = g_delete_prefix | { 'type': 'district_metering_area', 'id': id } + undo_cs = g_add_prefix | { 'type': 'district_metering_area', 'id': id, 'boundary': boundary, 'parent': parent, 'nodes': nodes } + + return DbChangeSet(redo_sql, undo_sql, [redo_cs], [undo_cs]) + + +def _has_child(name: str, parent: str) -> bool: + return try_read(name, f"select * from region_dma where parent = '{parent}'") != None + + +def is_descendant_of(name: str, descendant: str, ancestor: str) -> bool: + parent = descendant + while parent != None: + parent = read(name, f"select parent from region_dma where id = '{parent}'")['parent'] + if parent == ancestor: + return True + return False + + +def delete_district_metering_area(name: str, cs: ChangeSet) -> ChangeSet: + ops = cs.operations + + if len(cs.operations) == 0: + return ChangeSet() + + op = ops[0] + + if 'id' not in op: + return ChangeSet() + + dma = get_district_metering_area(name, op['id']) + if dma == {}: + return ChangeSet() + + #TODO: cascade ? + if _has_child(name, dma['id']): + return ChangeSet() + + return execute_command(name, _delete_district_metering_area(name, cs)) + + +def get_all_district_metering_area_ids(name: str) -> list[str]: + ids = [] + for row in read_all(name, f"select id from region_dma"): + ids.append(row['id']) + return ids + + +def get_all_district_metering_areas(name: str) -> list[dict[str, Any]]: + result = [] + for id in get_all_district_metering_area_ids(name): + result.append(get_district_metering_area(name, id)) + return result diff --git a/api/s33_dma_cal.py b/api/s33_dma_cal.py new file mode 100644 index 0000000..c06f76b --- /dev/null +++ b/api/s33_dma_cal.py @@ -0,0 +1,98 @@ +import ctypes +import os +from .database import * +from .s0_base import get_nodes +from .s32_region_util import get_nodes_in_region, to_postgis_polygon +from .s32_region_util import Topology +from .s32_region import get_region + + +PARTITION_TYPE_RB = 0 +PARTITION_TYPE_KWAY = 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]]: + if part_type != PARTITION_TYPE_RB and part_type != PARTITION_TYPE_KWAY: + return [] + if part_count <= 0: + return [] + elif part_count == 1: + return [nodes] + + lib = ctypes.CDLL(os.path.join(os.getcwd(), 'api', 'CMetis.dll')) + + METIS_NOPTIONS = 40 + c_options = (ctypes.c_int64 * METIS_NOPTIONS)() + + METIS_OK = 1 + result = lib.set_default_options(c_options) + if result != METIS_OK: + return [] + + METIS_OPTION_PTYPE , METIS_OPTION_CONTIG = 0, 13 + c_options[METIS_OPTION_PTYPE] = part_type + c_options[METIS_OPTION_CONTIG] = 1 + + topology = Topology(name, nodes) + t_nodes = topology.nodes() + t_links = topology.links() + t_node_list = topology.node_list() + t_link_list = topology.link_list() + + nedges = len(t_link_list) * 2 + + c_nvtxs = ctypes.c_int64(len(t_node_list)) + c_ncon = ctypes.c_int64(1) + c_xadj = (ctypes.c_int64 * (c_nvtxs.value + 1))() + c_adjncy = (ctypes.c_int64 * nedges)() + c_vwgt = (ctypes.c_int64 * (c_ncon.value * c_nvtxs.value))() + c_adjwgt = (ctypes.c_int64 * nedges)() + c_vsize = (ctypes.c_int64 * c_nvtxs.value)() + + c_xadj[0] = 0 + + l, n = 0, 0 + c_xadj_i = 1 + for node in t_node_list: + links = t_nodes[node]['links'] + for link in links: + node1 = t_links[link]['node1'] + node2 = t_links[link]['node2'] + c_adjncy[l] = t_node_list.index(node2) if node2 != node else t_node_list.index(node1) + c_adjwgt[l] = 1 + l += 1 + if len(links) > 0: + c_xadj[c_xadj_i] = l # adjncy.size() + c_xadj_i += 1 + c_vwgt[n] = 1 + c_vsize[n] = 1 + n += 1 + + part_func = lib.part_graph_recursive if part_type == PARTITION_TYPE_RB else lib.part_graph_kway + + c_nparts = ctypes.c_int64(part_count) + c_tpwgts = ctypes.POINTER(ctypes.c_double)() + c_ubvec = ctypes.POINTER(ctypes.c_double)() + c_out_edgecut = ctypes.c_int64(0) + c_out_part = (ctypes.c_int64 * c_nvtxs.value)() + result = part_func(ctypes.byref(c_nvtxs), ctypes.byref(c_ncon), c_xadj, c_adjncy, c_vwgt, c_vsize, c_adjwgt, ctypes.byref(c_nparts), c_tpwgts, c_ubvec, c_options, ctypes.byref(c_out_edgecut), c_out_part) + if result != METIS_OK: + return [] + + dmas : list[list[str]]= [] + for i in range(part_count): + dmas.append([]) + for i in range(c_nvtxs.value): + dmas[c_out_part[i]].append(t_node_list[i]) + + return dmas + + +def calculate_district_metering_area_for_region(name: str, region: str, part_count: int = 1, part_type: int = PARTITION_TYPE_RB) -> list[list[str]]: + nodes = get_nodes_in_region(name, region) + return calculate_district_metering_area_for_nodes(name, nodes, part_count, part_type) + + +def calculate_district_metering_area_for_network(name: str, part_count: int = 1, part_type: int = PARTITION_TYPE_RB) -> list[list[str]]: + nodes = get_nodes(name) + return calculate_district_metering_area_for_nodes(name, nodes, part_count, part_type) diff --git a/api/s33_dma_gen.py b/api/s33_dma_gen.py new file mode 100644 index 0000000..1153ba4 --- /dev/null +++ b/api/s33_dma_gen.py @@ -0,0 +1,47 @@ +from .s32_region_util import calculate_boundary, inflate_boundary +from .s33_dma_cal import * +from .s33_dma import get_all_district_metering_area_ids, get_all_district_metering_areas, get_district_metering_area, is_descendant_of +from .batch_exe import execute_batch_command + + +def generate_district_metering_area(name: str, part_count: int = 1, part_type: int = PARTITION_TYPE_RB) -> ChangeSet: + cs = ChangeSet() + + dmas = get_all_district_metering_areas(name) + max_level = 0 + for dma in dmas: + if dma['level'] > max_level: + max_level = dma['level'] + while max_level > 0: + for dma in dmas: + if dma['level'] == max_level: + cs.delete({ 'type': 'district_metering_area', 'id': dma['id'] }) + max_level -= 1 + + i = 1 + for nodes in calculate_district_metering_area_for_network(name, part_count, part_type): + boundary = calculate_boundary(name, nodes) + boundary = inflate_boundary(name, boundary) + cs.add({ 'type': 'district_metering_area', 'id': f"DMA_1_{i}", 'boundary': boundary, 'parent': None, 'nodes': nodes }) + i += 1 + + return execute_batch_command(name, cs) + + +def generate_sub_district_metering_area(name: str, dma: str, part_count: int = 1, part_type: int = PARTITION_TYPE_RB) -> ChangeSet: + cs = ChangeSet() + + for id in get_all_district_metering_area_ids(name): + if is_descendant_of(name, id, dma): + cs.delete({ 'type': 'district_metering_area', 'id': id }) + + level = get_district_metering_area(name, dma)['level'] + 1 + + i = 1 + for nodes in calculate_district_metering_area_for_region(name, dma, part_count, part_type): + boundary = calculate_boundary(name, nodes) + boundary = inflate_boundary(name, boundary) + cs.add({ 'type': 'district_metering_area', 'id': f"DMA_{level}_{i}", 'boundary': boundary, 'parent': dma, 'nodes': nodes }) + i += 1 + + return execute_batch_command(name, cs) diff --git a/api/sections.py b/api/sections.py index 5ee8a01..d64ca0c 100644 --- a/api/sections.py +++ b/api/sections.py @@ -34,6 +34,7 @@ s29_scada_device = 'scada_device' s30_scada_device_data = 'scada_device_data' s31_scada_element = 'scada_element' s32_region = 'region' +s33_dma = 'district_metering_area' TITLE = 'TITLE' JUNCTIONS = 'JUNCTIONS' diff --git a/script/sql/create/33.dma.sql b/script/sql/create/33.dma.sql index 9dd7b51..07e53a6 100644 --- a/script/sql/create/33.dma.sql +++ b/script/sql/create/33.dma.sql @@ -2,5 +2,5 @@ create table region_dma ( id text primary key references region(id) , parent text --references region_dma(id) -, nodes text not null default '' +, nodes text not null ); diff --git a/test_tjnetwork.py b/test_tjnetwork.py index ee7d7ff..7360dba 100644 --- a/test_tjnetwork.py +++ b/test_tjnetwork.py @@ -5871,6 +5871,257 @@ class TestApi: self.leave(p) + def test_district_metering_area(self): + p = 'test_district_metering_area' + self.enter(p) + + dma = get_district_metering_area(p, 'dma') + assert dma == {} + + add_district_metering_area(p, ChangeSet({'id': 'dma', 'boundary': [(0.0, 0.0), (1.0, 0.0), (1.0, 1.0)]})) + dma = get_district_metering_area(p, 'dma') + assert dma == {} + + add_district_metering_area(p, ChangeSet({'id': 'dma', 'boundary': [(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 0.0)], 'parent': '1'})) + dma = get_district_metering_area(p, 'dma') + assert dma == {} + + add_district_metering_area(p, ChangeSet({'id': 'dma', 'boundary': [(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 0.0)]})) + dma = get_district_metering_area(p, 'dma') + assert dma['id'] == 'dma' + assert dma['boundary'] == [(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 0.0)] + assert dma['parent'] == None + assert dma['nodes'] == [] + assert dma['level'] == 1 + + set_district_metering_area(p, ChangeSet({'id': 'dma', 'boundary': [(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 0.0)], 'parent': '1'})) + dma = get_district_metering_area(p, 'dma') + assert dma['id'] == 'dma' + assert dma['boundary'] == [(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 0.0)] + assert dma['parent'] == None + assert dma['nodes'] == [] + assert dma['level'] == 1 + + add_district_metering_area(p, ChangeSet({'id': 'd0', 'boundary': [(0.0, 0.0), (1.0, 0.0), (2.0, 2.0), (0.0, 0.0)]})) + set_district_metering_area(p, ChangeSet({'id': 'dma', 'boundary': [(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 0.0)], 'parent': 'd0'})) + dma = get_district_metering_area(p, 'dma') + assert dma['id'] == 'dma' + assert dma['boundary'] == [(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 0.0)] + assert dma['parent'] == 'd0' + assert dma['nodes'] == [] + assert dma['level'] == 2 + + set_district_metering_area(p, ChangeSet({'id': 'dma', 'boundary': [(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 0.0)], 'nodes': ['1']})) + dma = get_district_metering_area(p, 'dma') + assert dma['id'] == 'dma' + assert dma['boundary'] == [(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 0.0)] + assert dma['parent'] == 'd0' + assert dma['nodes'] == [] + assert dma['level'] == 2 + + add_junction(p, ChangeSet({'id': 'j0', 'x': 0.0, 'y': 10.0, 'elevation': 20.0})) + set_district_metering_area(p, ChangeSet({'id': 'dma', 'boundary': [(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 0.0)], 'nodes': ['j0']})) + dma = get_district_metering_area(p, 'dma') + assert dma['id'] == 'dma' + assert dma['boundary'] == [(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 0.0)] + assert dma['parent'] == 'd0' + assert dma['nodes'] == ['j0'] + assert dma['level'] == 2 + + delete_district_metering_area(p, ChangeSet({'id': 'dma'})) + dma = get_district_metering_area(p, 'dma') + assert dma == {} + + add_district_metering_area(p, ChangeSet({'id': 'dma', 'boundary': [(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 0.0)], 'parent': 'd0', 'nodes': ['j0']})) + dma = get_district_metering_area(p, 'dma') + assert dma['id'] == 'dma' + assert dma['boundary'] == [(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 0.0)] + assert dma['parent'] == 'd0' + assert dma['nodes'] == ['j0'] + assert dma['level'] == 2 + + delete_district_metering_area(p, ChangeSet({'id': 'd0'})) + dma = get_district_metering_area(p, 'd0') + assert dma != {} + + delete_district_metering_area(p, ChangeSet({'id': 'dma'})) + dma = get_district_metering_area(p, 'dma') + assert dma == {} + + delete_district_metering_area(p, ChangeSet({'id': 'd0'})) + dma = get_district_metering_area(p, 'd0') + assert dma == {} + + self.leave(p) + + + def test_district_metering_area_op(self): + p = 'test_district_metering_area_op' + self.enter(p) + + cs = add_district_metering_area(p, ChangeSet({'id': 'dma', 'boundary': [(0.0, 0.0), (1.0, 0.0), (1.0, 1.0)]})).operations + assert len(cs) == 0 + + cs = add_district_metering_area(p, ChangeSet({'id': 'dma', 'boundary': [(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 0.0)], 'parent': '1'})).operations + assert len(cs) == 0 + + cs = add_district_metering_area(p, ChangeSet({'id': 'dma', 'boundary': [(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 0.0)]})).operations[0] + assert cs['operation'] == API_ADD + assert cs['type'] == 'district_metering_area' + assert cs['id'] == 'dma' + assert cs['boundary'] == [(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 0.0)] + assert cs['parent'] == None + assert cs['nodes'] == [] + + cs = execute_undo(p).operations[0] + assert cs['operation'] == API_DELETE + assert cs['type'] == 'district_metering_area' + assert cs['id'] == 'dma' + + cs = execute_redo(p).operations[0] + assert cs['operation'] == API_ADD + assert cs['type'] == 'district_metering_area' + assert cs['id'] == 'dma' + assert cs['boundary'] == [(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 0.0)] + assert cs['parent'] == None + assert cs['nodes'] == [] + + cs = set_district_metering_area(p, ChangeSet({'id': 'dma', 'boundary': [(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 0.0)], 'parent': '1'})).operations + assert len(cs) == 0 + + add_district_metering_area(p, ChangeSet({'id': 'd0', 'boundary': [(0.0, 0.0), (1.0, 0.0), (2.0, 2.0), (0.0, 0.0)]})) + cs = set_district_metering_area(p, ChangeSet({'id': 'dma', 'boundary': [(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 0.0)], 'parent': 'd0'})).operations[0] + assert cs['operation'] == API_UPDATE + assert cs['type'] == 'district_metering_area' + assert cs['id'] == 'dma' + assert cs['boundary'] == [(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 0.0)] + assert cs['parent'] == 'd0' + assert cs['nodes'] == [] + + cs = execute_undo(p).operations[0] + assert cs['operation'] == API_UPDATE + assert cs['type'] == 'district_metering_area' + assert cs['id'] == 'dma' + assert cs['boundary'] == [(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 0.0)] + assert cs['parent'] == None + assert cs['nodes'] == [] + + cs = execute_redo(p).operations[0] + assert cs['operation'] == API_UPDATE + assert cs['type'] == 'district_metering_area' + assert cs['id'] == 'dma' + assert cs['boundary'] == [(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 0.0)] + assert cs['parent'] == 'd0' + assert cs['nodes'] == [] + + cs = set_district_metering_area(p, ChangeSet({'id': 'dma', 'boundary': [(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 0.0)], 'nodes': ['1']})).operations + assert len(cs) == 0 + + add_junction(p, ChangeSet({'id': 'j0', 'x': 0.0, 'y': 10.0, 'elevation': 20.0})) + cs = set_district_metering_area(p, ChangeSet({'id': 'dma', 'boundary': [(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 0.0)], 'nodes': ['j0']})).operations[0] + assert cs['operation'] == API_UPDATE + assert cs['type'] == 'district_metering_area' + assert cs['id'] == 'dma' + assert cs['boundary'] == [(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 0.0)] + assert cs['parent'] == 'd0' + assert cs['nodes'] == ['j0'] + + cs = execute_undo(p).operations[0] + assert cs['operation'] == API_UPDATE + assert cs['type'] == 'district_metering_area' + assert cs['id'] == 'dma' + assert cs['boundary'] == [(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 0.0)] + assert cs['parent'] == 'd0' + assert cs['nodes'] == [] + + cs = execute_redo(p).operations[0] + assert cs['operation'] == API_UPDATE + assert cs['type'] == 'district_metering_area' + assert cs['id'] == 'dma' + assert cs['boundary'] == [(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 0.0)] + assert cs['parent'] == 'd0' + assert cs['nodes'] == ['j0'] + + cs = delete_district_metering_area(p, ChangeSet({'id': 'xxx'})).operations + assert len(cs) == 0 + + cs = delete_district_metering_area(p, ChangeSet({'id': 'dma'})).operations[0] + assert cs['operation'] == API_DELETE + assert cs['type'] == 'district_metering_area' + assert cs['id'] == 'dma' + + cs = execute_undo(p).operations[0] + assert cs['operation'] == API_ADD + assert cs['type'] == 'district_metering_area' + assert cs['id'] == 'dma' + assert cs['boundary'] == [(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 0.0)] + assert cs['parent'] == 'd0' + assert cs['nodes'] == ['j0'] + + cs = execute_redo(p).operations[0] + assert cs['operation'] == API_DELETE + assert cs['type'] == 'district_metering_area' + assert cs['id'] == 'dma' + + cs = add_district_metering_area(p, ChangeSet({'id': 'dma', 'boundary': [(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 0.0)], 'parent': 'd0', 'nodes': ['j0']})).operations[0] + assert cs['operation'] == API_ADD + assert cs['type'] == 'district_metering_area' + assert cs['id'] == 'dma' + assert cs['boundary'] == [(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 0.0)] + assert cs['parent'] == 'd0' + assert cs['nodes'] == ['j0'] + + cs = delete_district_metering_area(p, ChangeSet({'id': 'd0'})).operations + assert len(cs) == 0 + + cs = delete_district_metering_area(p, ChangeSet({'id': 'dma'})).operations[0] + assert cs['operation'] == API_DELETE + assert cs['type'] == 'district_metering_area' + assert cs['id'] == 'dma' + + cs = delete_district_metering_area(p, ChangeSet({'id': 'dma'})).operations + assert len(cs) == 0 + + cs = delete_district_metering_area(p, ChangeSet({'id': 'd0'})).operations[0] + assert cs['operation'] == API_DELETE + assert cs['type'] == 'district_metering_area' + assert cs['id'] == 'd0' + + self.leave(p) + + + def test_district_metering_area_gen(self): + p = 'test_district_metering_area_gen' + read_inp(p, f'./inp/net3.inp', '3') + open_project(p) + + cs = generate_district_metering_area(p, 3).operations + assert len(cs) == 3 + + dmas = get_all_district_metering_area_ids(p) + assert len(dmas) == 3 + + cs = generate_district_metering_area(p, 3).operations + assert len(cs) == 6 + + dmas = get_all_district_metering_area_ids(p) + assert len(dmas) == 3 + + cs = generate_sub_district_metering_area(p, 'DMA_1_1', 2).operations + assert len(cs) == 2 + + dmas = get_all_district_metering_area_ids(p) + assert len(dmas) == 5 + + cs = generate_district_metering_area(p, 3).operations + assert len(cs) == 8 + + dmas = get_all_district_metering_area_ids(p) + assert len(dmas) == 3 + + self.leave(p) + + # 34 service_area diff --git a/tjnetwork.py b/tjnetwork.py index 7c918f9..11b2d21 100644 --- a/tjnetwork.py +++ b/tjnetwork.py @@ -1022,9 +1022,6 @@ def add_region(name: str, cs: ChangeSet) -> ChangeSet: def delete_region(name: str, cs: ChangeSet) -> ChangeSet: return api.delete_region(name, cs) -def get_all_regions(name: str) -> list[dict[str, Any]]: - return [] - ############################################################ # district_metering_area 33 @@ -1040,29 +1037,31 @@ def calculate_district_metering_area_for_network(name: str, part_count: int = 1, return api.calculate_district_metering_area_for_network(name, part_count, part_type) def get_district_metering_area_schema(name: str) -> dict[str, dict[str, Any]]: - return {} + return api.get_district_metering_area_schema(name) def get_district_metering_area(name: str, id: str) -> dict[str, Any]: - return {} + return api.get_district_metering_area(name, id) def set_district_metering_area(name: str, cs: ChangeSet) -> ChangeSet: - return ChangeSet() + return api.set_district_metering_area(name, cs) -# example: add_district_metering_area(p, ChangeSet({'id': 'r', 'boundary': [(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 0.0)]})) def add_district_metering_area(name: str, cs: ChangeSet) -> ChangeSet: - return ChangeSet() + return api.add_district_metering_area(name, cs) def delete_district_metering_area(name: str, cs: ChangeSet) -> ChangeSet: - return ChangeSet() + return api.delete_district_metering_area(name, cs) + +def get_all_district_metering_area_ids(name: str) -> list[str]: + return api.get_all_district_metering_area_ids(name) def get_all_district_metering_areas(name: str) -> list[dict[str, Any]]: - return [] + return api.get_all_district_metering_areas(name) def generate_district_metering_area(name: str, part_count: int = 1, part_type: int = PARTITION_TYPE_RB) -> ChangeSet: - return ChangeSet() + return api.generate_district_metering_area(name, part_count, part_type) def generate_sub_district_metering_area(name: str, dma: str, part_count: int = 1, part_type: int = PARTITION_TYPE_RB) -> ChangeSet: - return ChangeSet() + return api.generate_sub_district_metering_area(name, dma, part_count, part_type) ############################################################