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.
Files changed (47) hide show
  1. building_eload/__init__.py +38 -0
  2. building_eload/core/__init__.py +0 -0
  3. building_eload/core/building_loader.py +127 -0
  4. building_eload/core/dynamic_simulation.py +1201 -0
  5. building_eload/core/static_simulation.py +1011 -0
  6. building_eload/core/validation.py +1046 -0
  7. building_eload/models/__init__.py +0 -0
  8. building_eload/models/cooking.py +225 -0
  9. building_eload/models/dwelling.py +312 -0
  10. building_eload/models/heating.py +710 -0
  11. building_eload/models/hot_water.py +602 -0
  12. building_eload/models/non_residential.py +494 -0
  13. building_eload/models/occupancy.py +129 -0
  14. building_eload/models/specific.py +123 -0
  15. building_eload/models/vacations.py +372 -0
  16. building_eload/models/weather.py +121 -0
  17. building_eload/plots/__init__.py +0 -0
  18. building_eload/plots/clustering.py +464 -0
  19. building_eload/plots/dynamic_model.py +511 -0
  20. building_eload/plots/static_calibration.py +268 -0
  21. building_eload/plots/validation.py +1464 -0
  22. building_eload/scripts/__init__.py +0 -0
  23. building_eload/scripts/calibration/__init__.py +0 -0
  24. building_eload/scripts/calibration/run_plot_static_calibration_results.py +124 -0
  25. building_eload/scripts/climate/generate_climates.py +308 -0
  26. building_eload/scripts/clustering/__init__.py +0 -0
  27. building_eload/scripts/clustering/annual_energy_from_clustering.py +67 -0
  28. building_eload/scripts/clustering/district_dhu.py +347 -0
  29. building_eload/scripts/clustering/national_district_clustering.py +641 -0
  30. building_eload/scripts/clustering/regional_district_clustering.py +451 -0
  31. building_eload/scripts/clustering/run_multiple_clustering.py +212 -0
  32. building_eload/scripts/simulation/dynamic/run.py +77 -0
  33. building_eload/scripts/simulation/dynamic/run_parallel.py +205 -0
  34. building_eload/scripts/simulation/static/run.py +37 -0
  35. building_eload/scripts/simulation/static/run_parallel.py +182 -0
  36. building_eload/scripts/validation/run.py +67 -0
  37. building_eload/utils/__init__.py +0 -0
  38. building_eload/utils/data_download.py +97 -0
  39. building_eload/utils/district_list.py +105 -0
  40. building_eload/utils/epw_reader.py +135 -0
  41. building_eload/utils/logger.py +54 -0
  42. building_eload/utils/plot_utils.py +20 -0
  43. building_eload-0.4.0.dist-info/METADATA +249 -0
  44. building_eload-0.4.0.dist-info/RECORD +47 -0
  45. building_eload-0.4.0.dist-info/WHEEL +5 -0
  46. building_eload-0.4.0.dist-info/licenses/LICENCE.md +27 -0
  47. 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
+ )