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

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',) 

10 

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, 

28 

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.") 

43 

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. 

47 

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 

55 

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 

68 

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") 

73 

74 def get_location_name(self, latitude: tuple, longitude: tuple) -> str: 

75 """Returns the name of the location based on latitude and longitude. 

76 

77 Args: 

78 latitude: tuple of degrees, minutes and seconds 

79 longitude: tuple of degrees, minutes and seconds 

80 

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 

86 

87 import requests 

88 # URL for Nominatim API 

89 nominatim_url = "https://nominatim.openstreetmap.org/reverse" 

90 

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 } 

97 

98 # Send the HTTP request to the Nominatim API 

99 response = requests.get(nominatim_url, params=params) 

100 

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") 

105 

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' 

111 

112 return location_name