Coverage for bim2sim/tasks/common/weather.py: 28%
46 statements
« prev ^ index » next coverage.py v7.6.12, created at 2025-03-12 17:09 +0000
« prev ^ index » next coverage.py v7.6.12, created at 2025-03-12 17:09 +0000
1from bim2sim.tasks.base import ITask
2from bim2sim.utilities.common_functions import filter_elements
3from typing import Any
4from pathlib import WindowsPath, Path
5from typing import Optional
6class Weather(ITask):
7 """Task to get the weather file for later simulation"""
8 reads = ('elements',)
9 touches = ('weather_file',)
11 def run(self, elements: dict):
12 self.logger.info("Setting weather file.")
13 weather_file: Optional[WindowsPath] = None
14 # try to get weather file from settings
15 if self.playground.sim_settings.weather_file_path:
16 weather_file = self.playground.sim_settings.weather_file_path
17 # try to get TRY weather file for location of IFC
18 if not weather_file:
19 raise NotImplementedError("Waiting for response from DWD if we can"
20 "implement this")
21 # lat, long = self.get_location_lat_long_from_ifc(elements)
22 # weather_file = self.get_weatherfile_from_dwd(lat, long)
23 self.check_file_ending(weather_file)
24 if not weather_file:
25 raise ValueError("No weather file provided for the simulation, "
26 "can't continue model generation.")
27 return weather_file,
29 def check_file_ending(self, weather_file: WindowsPath):
30 """Check if the file ending fits the simulation model type."""
31 plugin_name = self.playground.project.plugin_cls.name
32 if plugin_name in ['EnergyPlus', 'Comfort']:
33 if not weather_file.suffix == '.epw':
34 raise ValueError(
35 f"EnergyPlus simulation model should be created, but "
36 f"instead .epw a {weather_file.suffix} file was provided.")
37 # all other plugins currently use .mos files
38 else:
39 if not weather_file.suffix == '.mos':
40 raise ValueError(
41 f"Modelica simulation model should be created, but "
42 f"instead .mos a {weather_file.suffix} file was provided.")
44 def get_location_lat_long_from_ifc(self, elements: dict) -> [float]:
45 """
46 Returns the location in form of latitude and longitude based on IfcSite.
48 The location of the site and therefore the building are taken from the
49 IfcSite in form of latitude and longitude. Latitude and Longitude each
50 are a tuple of (degrees, minutes, seconds) and, optionally,
51 millionths of seconds. See IfcSite Documentation for further
52 information.
53 Args:
54 elements: dict with bim2sim elements
56 Returns:
57 latitude, longitude: two float values for latitude and longitude
58 """
59 site = filter_elements(elements, 'Site')
60 if len(site) > 1:
61 self.logger.warning(
62 "More than one IfcSite in the provided IFC file(s). We are"
63 "using the location of the first IfcSite found for weather "
64 "file definition.")
65 latitude = site[0].location_latitude
66 longitude = site[0].location_longitude
67 return latitude, longitude
69 def get_weatherfile_from_dwd(self, lat: tuple, long: tuple):
70 # TODO implement scraper, if DWD allows it
71 raise NotImplementedError("Waiting for response from DWD if we can"
72 "implement this")
74 def get_location_name(self, latitude: tuple, longitude: tuple) -> str:
75 """Returns the name of the location based on latitude and longitude.
77 Args:
78 latitude: tuple of degrees, minutes and seconds
79 longitude: tuple of degrees, minutes and seconds
81 Returns:
82 location_name: str of the location name
83 """
84 # TODO: this might be obsolete, because if we use DWD method, we don't
85 # need a name anymore, just the coordinates
87 import requests
88 # URL for Nominatim API
89 nominatim_url = "https://nominatim.openstreetmap.org/reverse"
91 # Parameters for the API request
92 params = {
93 "format": "json",
94 "lat": latitude[0] + (latitude[1] / 60) + (latitude[2] / 3600),
95 "lon": longitude[0] + (longitude[1] / 60) + (longitude[2] / 3600),
96 }
98 # Send the HTTP request to the Nominatim API
99 response = requests.get(nominatim_url, params=params)
101 # Check if the request was successful
102 if response.status_code == 200:
103 data = response.json()
104 location_name = data.get("display_name", "Location not found")
106 else:
107 self.logger.warning(
108 'No location could be retrieved. Maybe due to limited API usage'
109 'or faulty location data. Setting Aachen as default location.')
110 location_name = 'Aachen'
112 return location_name