building-eload 0.4.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- building_eload/__init__.py +38 -0
- building_eload/core/__init__.py +0 -0
- building_eload/core/building_loader.py +127 -0
- building_eload/core/dynamic_simulation.py +1201 -0
- building_eload/core/static_simulation.py +1011 -0
- building_eload/core/validation.py +1046 -0
- building_eload/models/__init__.py +0 -0
- building_eload/models/cooking.py +225 -0
- building_eload/models/dwelling.py +312 -0
- building_eload/models/heating.py +710 -0
- building_eload/models/hot_water.py +602 -0
- building_eload/models/non_residential.py +494 -0
- building_eload/models/occupancy.py +129 -0
- building_eload/models/specific.py +123 -0
- building_eload/models/vacations.py +372 -0
- building_eload/models/weather.py +121 -0
- building_eload/plots/__init__.py +0 -0
- building_eload/plots/clustering.py +464 -0
- building_eload/plots/dynamic_model.py +511 -0
- building_eload/plots/static_calibration.py +268 -0
- building_eload/plots/validation.py +1464 -0
- building_eload/scripts/__init__.py +0 -0
- building_eload/scripts/calibration/__init__.py +0 -0
- building_eload/scripts/calibration/run_plot_static_calibration_results.py +124 -0
- building_eload/scripts/climate/generate_climates.py +308 -0
- building_eload/scripts/clustering/__init__.py +0 -0
- building_eload/scripts/clustering/annual_energy_from_clustering.py +67 -0
- building_eload/scripts/clustering/district_dhu.py +347 -0
- building_eload/scripts/clustering/national_district_clustering.py +641 -0
- building_eload/scripts/clustering/regional_district_clustering.py +451 -0
- building_eload/scripts/clustering/run_multiple_clustering.py +212 -0
- building_eload/scripts/simulation/dynamic/run.py +77 -0
- building_eload/scripts/simulation/dynamic/run_parallel.py +205 -0
- building_eload/scripts/simulation/static/run.py +37 -0
- building_eload/scripts/simulation/static/run_parallel.py +182 -0
- building_eload/scripts/validation/run.py +67 -0
- building_eload/utils/__init__.py +0 -0
- building_eload/utils/data_download.py +97 -0
- building_eload/utils/district_list.py +105 -0
- building_eload/utils/epw_reader.py +135 -0
- building_eload/utils/logger.py +54 -0
- building_eload/utils/plot_utils.py +20 -0
- building_eload-0.4.0.dist-info/METADATA +249 -0
- building_eload-0.4.0.dist-info/RECORD +47 -0
- building_eload-0.4.0.dist-info/WHEEL +5 -0
- building_eload-0.4.0.dist-info/licenses/LICENCE.md +27 -0
- building_eload-0.4.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
# Utilisation de Path(__file__).parents[1] pour naviguer vers le bon répertoire parent
|
|
4
|
+
data_path = {
|
|
5
|
+
"data": Path(__file__).parents[1] / "data",
|
|
6
|
+
"bdtopo": Path(__file__).parents[1] / "data" / "bdtopo",
|
|
7
|
+
"elmas": Path(__file__).parents[1] / "data" / "elmas",
|
|
8
|
+
"simulation": Path(__file__).parents[1] / "data" / "simulation",
|
|
9
|
+
"activity_calendar": Path(__file__).parents[1] / "data" / "activity_calendar",
|
|
10
|
+
"validation": Path(__file__).parents[1] / "data" / "validation",
|
|
11
|
+
"climate": Path(__file__).parents[1] / "data" / "climate",
|
|
12
|
+
"representative_districts": Path(__file__).parents[1] / "data" / "representative_districts",
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
plot_path = Path(__file__).parents[1] / "plot"
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
download_link = {
|
|
19
|
+
"bd_topo_11": "https://data.geopf.fr/telechargement/download/BDTOPO/BDTOPO_3-4_TOUSTHEMES_GPKG_LAMB93_R11_2024-09-15/BDTOPO_3-4_TOUSTHEMES_GPKG_LAMB93_R11_2024-09-15.7z",
|
|
20
|
+
"bd_topo_24": "https://data.geopf.fr/telechargement/download/BDTOPO/BDTOPO_3-4_TOUSTHEMES_GPKG_LAMB93_R24_2024-12-15/BDTOPO_3-4_TOUSTHEMES_GPKG_LAMB93_R24_2024-12-15.7z",
|
|
21
|
+
"bd_topo_27": "https://data.geopf.fr/telechargement/download/BDTOPO/BDTOPO_3-4_TOUSTHEMES_GPKG_LAMB93_R27_2025-03-15/BDTOPO_3-4_TOUSTHEMES_GPKG_LAMB93_R27_2025-03-15.7z",
|
|
22
|
+
"bd_topo_28": "https://data.geopf.fr/telechargement/download/BDTOPO/BDTOPO_3-4_TOUSTHEMES_GPKG_LAMB93_R28_2025-03-15/BDTOPO_3-4_TOUSTHEMES_GPKG_LAMB93_R28_2025-03-15.7z",
|
|
23
|
+
"bd_topo_32": "https://data.geopf.fr/telechargement/download/BDTOPO/BDTOPO_3-4_TOUSTHEMES_GPKG_LAMB93_R32_2025-03-15/BDTOPO_3-4_TOUSTHEMES_GPKG_LAMB93_R32_2025-03-15.7z",
|
|
24
|
+
"bd_topo_44": "https://data.geopf.fr/telechargement/download/BDTOPO/BDTOPO_3-4_TOUSTHEMES_GPKG_LAMB93_R44_2025-03-15/BDTOPO_3-4_TOUSTHEMES_GPKG_LAMB93_R44_2025-03-15.7z",
|
|
25
|
+
"bd_topo_52": "https://data.geopf.fr/telechargement/download/BDTOPO/BDTOPO_3-4_TOUSTHEMES_GPKG_LAMB93_R52_2025-03-15/BDTOPO_3-4_TOUSTHEMES_GPKG_LAMB93_R52_2025-03-15.7z",
|
|
26
|
+
"bd_topo_53": "https://data.geopf.fr/telechargement/download/BDTOPO/BDTOPO_3-4_TOUSTHEMES_GPKG_LAMB93_R53_2025-03-15/BDTOPO_3-4_TOUSTHEMES_GPKG_LAMB93_R53_2025-03-15.7z",
|
|
27
|
+
"bd_topo_75": "https://data.geopf.fr/telechargement/download/BDTOPO/BDTOPO_3-4_TOUSTHEMES_GPKG_LAMB93_R75_2025-03-15/BDTOPO_3-4_TOUSTHEMES_GPKG_LAMB93_R75_2025-03-15.7z",
|
|
28
|
+
"bd_topo_76": "https://data.geopf.fr/telechargement/download/BDTOPO/BDTOPO_3-4_TOUSTHEMES_GPKG_LAMB93_R76_2025-03-15/BDTOPO_3-4_TOUSTHEMES_GPKG_LAMB93_R76_2025-03-15.7z",
|
|
29
|
+
"bd_topo_84": "https://data.geopf.fr/telechargement/download/BDTOPO/BDTOPO_3-4_TOUSTHEMES_GPKG_LAMB93_R84_2025-03-15/BDTOPO_3-4_TOUSTHEMES_GPKG_LAMB93_R84_2025-03-15.7z",
|
|
30
|
+
"bd_topo_93": "https://data.geopf.fr/telechargement/download/BDTOPO/BDTOPO_3-4_TOUSTHEMES_GPKG_LAMB93_R93_2025-03-15/BDTOPO_3-4_TOUSTHEMES_GPKG_LAMB93_R93_2025-03-15.7z",
|
|
31
|
+
"bd_topo_94": "https://data.geopf.fr/telechargement/download/BDTOPO/BDTOPO_3-4_TOUSTHEMES_GPKG_LAMB93_R94_2025-03-15/BDTOPO_3-4_TOUSTHEMES_GPKG_LAMB93_R94_2025-03-15.7z",
|
|
32
|
+
"district_list": "https://drive.google.com/uc?export=download&id=1YbTrxEMXHyTfcblaDWeBTbZMilL4QZP7",
|
|
33
|
+
"elmas": "https://figshare.com/ndownloader/files/41895786",
|
|
34
|
+
"occupant_diaries": "https://drive.google.com/uc?export=download&id=1Q_QEOoAvtA0T_70EpA4T2JpDh-VCFh1B",
|
|
35
|
+
"ORE": "https://drive.google.com/uc?export=download&id=1IAmwRaE_HxVmkik49CStng0vlXMigjmg",
|
|
36
|
+
"ENEDIS_REGIONAL": "TODO",
|
|
37
|
+
"ENEDIS_NATIONAL": "TODO",
|
|
38
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# building_loader.py
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
import warnings
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Optional
|
|
7
|
+
|
|
8
|
+
import buildingdata
|
|
9
|
+
import geopandas as gpd
|
|
10
|
+
from shapely import geometry
|
|
11
|
+
|
|
12
|
+
from building_eload import data_path
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class BuildingLoader:
|
|
19
|
+
"""Load buildings from a district and filter them based on intersection with the district geometry."""
|
|
20
|
+
|
|
21
|
+
INTERSECTION_THRESHOLD = 0.5
|
|
22
|
+
BUILDING_LAYER_NAME = "batiment"
|
|
23
|
+
GIS_ENGINE = "pyogrio"
|
|
24
|
+
BUILDING_FILE_PREFIX = "BDTOPO_R"
|
|
25
|
+
BUILDING_FILE_SUFFIX = ".gpkg"
|
|
26
|
+
DEFAULT_DISTRICT_DATA_FILE = None
|
|
27
|
+
DEFAULT_BUILDING_FOOTPRINT_FOLDER = Path(data_path["bdtopo"])
|
|
28
|
+
|
|
29
|
+
def __init__(self, district_id: str, district_data_file: str = None, building_footprint_folder: str = None):
|
|
30
|
+
|
|
31
|
+
self.district_id = str(district_id)
|
|
32
|
+
self.district_data_file = (
|
|
33
|
+
Path(district_data_file) if district_data_file is not None else self.DEFAULT_DISTRICT_DATA_FILE
|
|
34
|
+
)
|
|
35
|
+
self.building_footprint_folder = Path(
|
|
36
|
+
building_footprint_folder or self.DEFAULT_BUILDING_FOOTPRINT_FOLDER
|
|
37
|
+
)
|
|
38
|
+
logger.debug(f"{self.logger_header} District data file: {self.district_data_file}.")
|
|
39
|
+
logger.debug(f"{self.logger_header} Building footprint folder: {self.building_footprint_folder}.")
|
|
40
|
+
|
|
41
|
+
@property
|
|
42
|
+
def logger_header(self):
|
|
43
|
+
if self.district_id is None:
|
|
44
|
+
return "[BuildingLoader]"
|
|
45
|
+
return f"[District {self.district_id}]"
|
|
46
|
+
|
|
47
|
+
# -------------------------------------------------------
|
|
48
|
+
# Main function
|
|
49
|
+
# -------------------------------------------------------
|
|
50
|
+
|
|
51
|
+
def load_buildings(self) -> Optional[gpd.GeoDataFrame]:
|
|
52
|
+
"""Load buildings for a specific district."""
|
|
53
|
+
logger.info(f"{self.logger_header} Loading buildings.")
|
|
54
|
+
|
|
55
|
+
district_data = self._load_district(self.district_id)
|
|
56
|
+
if district_data.empty:
|
|
57
|
+
logger.warning(f"{self.logger_header} District {self.district_id} not found in district file.")
|
|
58
|
+
return None
|
|
59
|
+
district_geometry = self._get_district_geometry(district_data)
|
|
60
|
+
buildings = self._load_buildings_in_district_bounds(district_geometry, district_data)
|
|
61
|
+
filtered_buildings = self._filter_buildings_on_bounds(buildings, district_geometry)
|
|
62
|
+
filtered_buildings["district"] = self.district_id
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
logger.info(f"{self.logger_header} Loaded {len(filtered_buildings)} buildings.")
|
|
66
|
+
|
|
67
|
+
return filtered_buildings
|
|
68
|
+
|
|
69
|
+
# -------------------------------------------------------
|
|
70
|
+
# Utilis
|
|
71
|
+
# -------------------------------------------------------
|
|
72
|
+
|
|
73
|
+
def _load_district(self, district_id: str) -> gpd.GeoDataFrame:
|
|
74
|
+
district_id = str(district_id)
|
|
75
|
+
if self.district_data_file is not None:
|
|
76
|
+
districts = gpd.read_parquet(self.district_data_file)
|
|
77
|
+
else:
|
|
78
|
+
districts = buildingdata.get_districts()
|
|
79
|
+
|
|
80
|
+
district_codes = districts["district"].astype(str).str.zfill(len(district_id))
|
|
81
|
+
return districts[district_codes == district_id]
|
|
82
|
+
|
|
83
|
+
@staticmethod
|
|
84
|
+
def _get_district_geometry(district_data: gpd.GeoDataFrame) -> geometry.Polygon:
|
|
85
|
+
return district_data["geometry"].iloc[0]
|
|
86
|
+
|
|
87
|
+
def _load_buildings_in_district_bounds(
|
|
88
|
+
self, district_geometry: geometry.Polygon, district_data: gpd.GeoDataFrame
|
|
89
|
+
) -> gpd.GeoDataFrame:
|
|
90
|
+
bounds = district_geometry.convex_hull.bounds
|
|
91
|
+
building_path = self._get_building_path(district_data)
|
|
92
|
+
|
|
93
|
+
if not building_path.exists():
|
|
94
|
+
logger.debug(
|
|
95
|
+
f"{self.logger_header} Local BDTOPO file {building_path} not found; "
|
|
96
|
+
"loading buildings through buildingdata."
|
|
97
|
+
)
|
|
98
|
+
return buildingdata.get_bdtopo(self.district_id)
|
|
99
|
+
|
|
100
|
+
with warnings.catch_warnings():
|
|
101
|
+
warnings.filterwarnings("ignore")
|
|
102
|
+
logger.debug(f"{self.logger_header} Loading buildings from {building_path}.")
|
|
103
|
+
return gpd.read_file(
|
|
104
|
+
building_path,
|
|
105
|
+
layer=self.BUILDING_LAYER_NAME,
|
|
106
|
+
engine=self.GIS_ENGINE,
|
|
107
|
+
bbox=bounds,
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
def _filter_buildings_on_bounds(
|
|
111
|
+
self, buildings: gpd.GeoDataFrame, district_geometry: geometry.Polygon
|
|
112
|
+
) -> gpd.GeoDataFrame:
|
|
113
|
+
buildings["intersection_share"] = (
|
|
114
|
+
buildings["geometry"].intersection(district_geometry).area / buildings["geometry"].area
|
|
115
|
+
)
|
|
116
|
+
filtered_buildings = buildings.loc[
|
|
117
|
+
buildings.intersection_share >= self.INTERSECTION_THRESHOLD
|
|
118
|
+
].copy()
|
|
119
|
+
filtered_buildings.drop(columns=["intersection_share"], inplace=True)
|
|
120
|
+
return filtered_buildings
|
|
121
|
+
|
|
122
|
+
def _get_building_path(self, district_data: gpd.GeoDataFrame) -> Path:
|
|
123
|
+
region_id = int(district_data["region"].iloc[0])
|
|
124
|
+
return (
|
|
125
|
+
self.building_footprint_folder
|
|
126
|
+
/ f"{self.BUILDING_FILE_PREFIX}{region_id}{self.BUILDING_FILE_SUFFIX}"
|
|
127
|
+
)
|