From 24e4d7b53dd7a20f77aaebe381b936c74f20b09e Mon Sep 17 00:00:00 2001 From: Majid Rezaei Date: Sun, 23 Jun 2024 19:34:18 -0400 Subject: [PATCH] feature: add district heating network creator --- main.py | 3 + .../geojson_graph_creator.py | 54 +++++++++++++++ .../simultinity_factor.py | 66 ++++++++++++------- 3 files changed, 98 insertions(+), 25 deletions(-) create mode 100644 scripts/district_heating_network/geojson_graph_creator.py diff --git a/main.py b/main.py index 7ddbbc38..31237bd2 100644 --- a/main.py +++ b/main.py @@ -7,6 +7,9 @@ from hub.helpers.dictionaries import Dictionaries from hub.imports.construction_factory import ConstructionFactory from hub.imports.usage_factory import UsageFactory from hub.imports.weather_factory import WeatherFactory +from scripts.district_heating_network.road_processor import road_processor +from scripts.district_heating_network.district_heating_network_creator import DistrictHeatingNetworkCreator +from scripts.district_heating_network.geojson_graph_creator import networkx_to_geojson import hub.helpers.constants as cte from hub.exports.exports_factory import ExportsFactory from scripts.pv_feasibility import pv_feasibility diff --git a/scripts/district_heating_network/geojson_graph_creator.py b/scripts/district_heating_network/geojson_graph_creator.py new file mode 100644 index 00000000..407be813 --- /dev/null +++ b/scripts/district_heating_network/geojson_graph_creator.py @@ -0,0 +1,54 @@ +import json +from shapely import LineString, Point +import networkx as nx +from pathlib import Path + + +def networkx_to_geojson(graph: nx.Graph) -> Path: + """ + Convert a NetworkX graph to GeoJSON format. + + :param graph: A NetworkX graph. + :return: GeoJSON formatted dictionary. + """ + features = [] + + for u, v, data in graph.edges(data=True): + line = LineString([u, v]) + feature = { + "type": "Feature", + "geometry": { + "type": "LineString", + "coordinates": list(line.coords) + }, + "properties": { + "weight": data.get("weight", 1.0) + } + } + features.append(feature) + + for node, data in graph.nodes(data=True): + point = Point(node) + feature = { + "type": "Feature", + "geometry": { + "type": "Point", + "coordinates": list(point.coords)[0] + }, + "properties": { + "type": data.get("type", "unknown"), + "id": data.get("id", "N/A") + } + } + features.append(feature) + + geojson = { + "type": "FeatureCollection", + "features": features + } + + output_geojson_file = Path('./out_files/network_graph.geojson').resolve() + with open(output_geojson_file, 'w') as file: + json.dump(geojson, file, indent=4) + + return output_geojson_file diff --git a/scripts/district_heating_network/simultinity_factor.py b/scripts/district_heating_network/simultinity_factor.py index e9e4d470..c97932b5 100644 --- a/scripts/district_heating_network/simultinity_factor.py +++ b/scripts/district_heating_network/simultinity_factor.py @@ -1,47 +1,60 @@ import pandas as pd import numpy as np - class DemandShiftProcessor: def __init__(self, city): self.city = city def random_shift(self, series): - shift_amount = np.random.randint(0, 2) + shift_amount = np.random.randint(0, round(0.005 * len(series))) return series.shift(shift_amount).fillna(series.shift(shift_amount - len(series))) def process_demands(self): - building_dfs = [] + heating_dfs = [] + cooling_dfs = [] for building in self.city.buildings: - df = self.convert_building_to_dataframe(building) - df.set_index('Date/Time', inplace=True) - shifted_demands = df.apply(self.random_shift, axis=0) - self.update_building_demands(building, shifted_demands) - building_dfs.append(shifted_demands) + heating_df = self.convert_building_to_dataframe(building, 'heating') + cooling_df = self.convert_building_to_dataframe(building, 'cooling') + heating_df.set_index('Date/Time', inplace=True) + cooling_df.set_index('Date/Time', inplace=True) + shifted_heating_demands = heating_df.apply(self.random_shift, axis=0) + shifted_cooling_demands = cooling_df.apply(self.random_shift, axis=0) + self.update_building_demands(building, shifted_heating_demands, 'heating') + self.update_building_demands(building, shifted_cooling_demands, 'cooling') + heating_dfs.append(shifted_heating_demands) + cooling_dfs.append(shifted_cooling_demands) - combined_df = pd.concat(building_dfs, axis=1) - self.calculate_and_set_simultaneity_factor(combined_df) + combined_heating_df = pd.concat(heating_dfs, axis=1) + combined_cooling_df = pd.concat(cooling_dfs, axis=1) + self.calculate_and_set_simultaneity_factor(combined_heating_df, 'heating') + self.calculate_and_set_simultaneity_factor(combined_cooling_df, 'cooling') - def convert_building_to_dataframe(self, building): - data = { - "Date/Time": self.generate_date_time_index(), - "Heating_Demand": building.heating_demand["hour"], - "Cooling_Demand": building.cooling_demand["hour"] - } + def convert_building_to_dataframe(self, building, demand_type): + if demand_type == 'heating': + data = { + "Date/Time": self.generate_date_time_index(), + "Heating_Demand": building.heating_demand["hour"] + } + else: # cooling + data = { + "Date/Time": self.generate_date_time_index(), + "Cooling_Demand": building.cooling_demand["hour"] + } return pd.DataFrame(data) def generate_date_time_index(self): - # Generate hourly date time index for a full year in 2013 + # Generate hourly date time index for a full year in 2024 date_range = pd.date_range(start="2013-01-01 00:00:00", end="2013-12-31 23:00:00", freq='H') return date_range.strftime('%m/%d %H:%M:%S').tolist() - def update_building_demands(self, building, shifted_demands): - heating_shifted = shifted_demands["Heating_Demand"] - cooling_shifted = shifted_demands["Cooling_Demand"] - - building.heating_demand = self.calculate_new_demands(heating_shifted) - building.cooling_demand = self.calculate_new_demands(cooling_shifted) + def update_building_demands(self, building, shifted_demands, demand_type): + if demand_type == 'heating': + shifted_series = shifted_demands["Heating_Demand"] + building.heating_demand = self.calculate_new_demands(shifted_series) + else: # cooling + shifted_series = shifted_demands["Cooling_Demand"] + building.cooling_demand = self.calculate_new_demands(shifted_series) def calculate_new_demands(self, shifted_series): new_demand = { @@ -56,9 +69,12 @@ class DemandShiftProcessor: monthly_demand = series.resample('M').sum() return monthly_demand.tolist() - def calculate_and_set_simultaneity_factor(self, combined_df): + def calculate_and_set_simultaneity_factor(self, combined_df, demand_type): total_demand_original = combined_df.sum(axis=1) peak_total_demand_original = total_demand_original.max() individual_peak_demands = combined_df.max(axis=0) sum_individual_peak_demands = individual_peak_demands.sum() - self.city.simultaneity_factor = peak_total_demand_original / sum_individual_peak_demands + if demand_type == 'heating': + self.city.simultaneity_factor_heating = peak_total_demand_original / sum_individual_peak_demands + else: # cooling + self.city.simultaneity_factor_cooling = peak_total_demand_original / sum_individual_peak_demands