diff --git a/cea/config.py b/cea/config.py index 5be62c2009..0d1cba7c05 100644 --- a/cea/config.py +++ b/cea/config.py @@ -561,6 +561,13 @@ def decode(self, value): weather_path = self.locator.get_weather(value) elif os.path.exists(value) and value.endswith('.epw'): weather_path = value + elif os.path.exists(value) and value.endswith('.dat'): + # if dat convert to epw + dat_weather_file = value + epw_weather_file = os.path.join(os.path.dirname(value), os.path.basename(value).replace('.dat', '.epw')) + from cea.datamanagement.weather_helper import parse_dat_weather_file + parse_dat_weather_file(dat_weather_file, epw_weather_file) + weather_path = epw_weather_file elif any(w.lower().startswith(value.lower()) for w in self.locator.get_weather_names()) and value.strip(): # allow using shortcuts weather_path = self.locator.get_weather( @@ -568,7 +575,7 @@ def decode(self, value): elif value in self.THIRD_PARTY_WEATHER_SOURCES: weather_path = value else: - raise cea.ConfigError(f"Invalid weather path: {value}") + raise cea.ConfigError(f"Invalid weather path: {value}. Supported formats: .epw, .dat") return weather_path @property diff --git a/cea/datamanagement/weather_helper/generate_weather_data_sources.py b/cea/datamanagement/weather_helper/generate_weather_data_sources.py index a6a82c2cc0..286231fb1e 100644 --- a/cea/datamanagement/weather_helper/generate_weather_data_sources.py +++ b/cea/datamanagement/weather_helper/generate_weather_data_sources.py @@ -22,7 +22,6 @@ WEATHER_DATA_LOCATION = os.path.join(os.path.realpath(os.path.dirname(__file__)), 'weather.geojson') - def main(): # Combine for global data excels = [pd.read_excel(url) for url in URL_LIST] diff --git a/cea/datamanagement/weather_helper/weather_helper.py b/cea/datamanagement/weather_helper/weather_helper.py index 00f3f4e658..4ef485f4d4 100644 --- a/cea/datamanagement/weather_helper/weather_helper.py +++ b/cea/datamanagement/weather_helper/weather_helper.py @@ -74,6 +74,47 @@ def copy_weather_file(source_weather_file, locator): source_weather_file=source_weather_file )) +def parse_dat_weather_file(source_dat_file, output_epw_file): + """ + convert a DWD .dat-file to a epw-file + + :param string source_dat_file: source .dat + :param string output_epw_file: output EPW + """ + import pandas as pd + + print(f"Parsing .dat file: {source_dat_file}") + try: + weather_data = pd.read_csv(source_dat_file, sep="\s+", skiprows=6, header=None) + weather_data.columns = ['RW', 'HW', 'MM', 'DD', 'HH', 't', 'p', 'WR', 'WG', 'N', 'x', 'RF', 'B', 'D', 'A', 'E', + 'IL'] + + # from dat to epw + with open(output_epw_file, 'w') as epw_file: + epw_file.write("LOCATION,Weimar,-,DEU,SRC-TMYx,105550,50.983,11.317,1,268\n") + epw_file.write("DESIGN CONDITIONS,0\n") + epw_file.write("TYPICAL/EXTREME PERIODS,0\n") + epw_file.write("GROUND TEMPERATURES,0\n") + epw_file.write("HOLIDAYS/DAYLIGHT SAVINGS,No,0,0,0\n") + epw_file.write("COMMENTS 1, converted from .dat to .epw by CEA\n") + epw_file.write("COMMENTS 2\n") + epw_file.write("DATA PERIODS,1,1,Data,Tuesday,1/1,12/31\n") + + for _, row in weather_data.iterrows(): + mm = int(row['MM']) + dd = int(row['DD']) + hh = int(row['HH']) + epw_line = f"1958,{mm},{dd},{hh},60,B8E7B8B8?9?0?0?0?0?0?0B8B8B8B8?0?0F8F8A7E7, " \ + f"{row['t']},99,99,{row['RF']}, {row['p']}, 9999, {row['A']}, {row['D']}, {row['B']}, {row['D']}," \ + f"-9999, -9999, -9999, -9999,{row['WR']}, {row['WG']}, 99, 9999, 9999, 9999,0, 999999999,0," \ + f" -999,-999,-999\n" + epw_file.write(epw_line) ++ except pd.errors.EmptyDataError as e: ++ raise ValueError("The .dat file is empty or has incorrect format") from e ++ except pd.errors.ParserError as e: ++ raise ValueError("Failed to parse the .dat file - invalid format") from e ++ except Exception as e: ++ raise ValueError(f"Unexpected error while parsing .dat file: {str(e)}") from e def main(config): """ @@ -85,7 +126,13 @@ def main(config): locator = cea.inputlocator.InputLocator(config.scenario) weather = config.weather_helper.weather - if config.weather_helper.weather == 'climate.onebuilding.org': + if weather.endswith('.dat'): + print(f"Detected .dat file: {weather}") + # convert .dat in .epw + dat_weather_file = weather + epw_weather_file = locator.get_weather_file() + parse_dat_weather_file(dat_weather_file, epw_weather_file) + elif config.weather_helper.weather == 'climate.onebuilding.org': print("No weather provided, fetching from online sources.") fetch_weather_data(locator.get_weather_file(), locator.get_zone_geometry()) else: diff --git a/cea/default.config b/cea/default.config index 785add0b2d..9790d8c0f5 100644 --- a/cea/default.config +++ b/cea/default.config @@ -132,7 +132,7 @@ trees.help = Path to shapefile containing the geometries of tree canopies in pol [weather-helper] weather = weather.type = WeatherPathParameter -weather.help = Either a full path to a weather file (.epw) or the name of one of the weather files contained within the CEA. Leave blank to fetch from third-party sources. +weather.help = Either a full path to a weather file (.epw or .dat) or the name of one of the weather files contained within the CEA. Leave blank to fetch from third-party sources. [radiation] buildings =