Source code for ladybug.epw

# coding=utf-8
from .location import Location
from .analysisperiod import AnalysisPeriod
from .datatype import DataPoint  # Temperature, RelativeHumidity, Radiation, Illuminance
from .header import Header
from .datacollection import DataCollection
from .dt import DateTime

import os
import copy
import sys
if (sys.version_info > (3, 0)):
    # https://docs.python.org/3/tutorial/inputoutput.html#reading-and-writing-files
    # python 3
    readmode = 'r'
    writemode = 'w'
    xrange = range
else:
    readmode = 'rb'
    writemode = 'wb'


[docs]class EPW(object): """Import epw data from a local epw file. args: file_path: Local file address to an epw file. properties: years dry_bulb_temperature dew_point_temperature relative_humidity atmospheric_station_pressure extraterrestrial_horizontal_radiation extraterrestrial_direct_normal_radiation horizontal_infrared_radiation_intensity global_horizontal_radiation direct_normal_radiation diffuse_horizontal_radiation global_horizontal_illuminance direct_normal_illuminance diffuse_horizontal_illuminance zenith_luminance wind_direction wind_speed total_sky_cover opaque_sky_cover visibility ceiling_height present_weather_observation present_weather_codes precipitable_water aerosol_optical_depth snow_depth days_since_last_snowfall albedo liquid_precipitation_depth liquid_precipitation_quantity sky_temperature """ def __init__(self, file_path=None): """Init class.""" self.file_path = file_path self._is_data_loaded = False self._is_location_loaded = False self._data = [] # place holder for data as ladybug data collection self._header = None # epw header self._num_of_fields = 35 # it is 35 for TMY3 files @property def file_path(self): """Get path to epw file.""" return self._file_path @property def folder(self): """Get epw file folder.""" return self._folder @property def file_name(self): """Get epw file name.""" return self._file_name @file_path.setter def file_path(self, epw_file_path): """Get path to epw file.""" self._file_path = os.path.normpath(epw_file_path) if not os.path.isfile(self._file_path): raise ValueError( 'Cannot find an epw file at {}'.format(self._file_path)) if not epw_file_path.lower().endswith('epw'): raise TypeError(epw_file_path + ' is not an .epw file.') self._folder, self._file_name = os.path.split(self.file_path) @property def is_data_loaded(self): """Return True if weather data is loaded.""" return self._is_data_loaded @property def is_location_loaded(self): """Return True if location data is loaded.""" return self._is_location_loaded @property def header(self): """Return epw file header.""" if not self.is_location_loaded: self._import_data(import_location_only=True) return self._header @property def location(self): """Return location data.""" if not self.is_location_loaded: self._import_data(import_location_only=True) return self._location # TODO: import EPW header. Currently I just ignore header data def _import_data(self, import_location_only=False): """Import data from an epw file. Hourly data will be saved in self.data and location data will be saved in self.location """ with open(self.file_path, readmode) as epwin: line = epwin.readline() # import location data # first line has location data - Here is an example # LOCATION,Denver Centennial Golden Nr,CO,USA,TMY3,724666,39.74, # -105.18,-7.0,1829.0 if not self._is_location_loaded: location_data = line.strip().split(',') self._location = Location() self._location.city = location_data[1].replace('\\', ' ') \ .replace('/', ' ') self._location.country = location_data[3] self._location.source = location_data[4] self._location.station_id = location_data[5] self._location.latitude = location_data[6] self._location.longitude = location_data[7] self._location.time_zone = location_data[8] self._location.elevation = location_data[9] self._is_location_loaded = True # TODO: add parsing for header self._header = [line] + [epwin.readline() for i in xrange(7)] if import_location_only: return # read first line of data to overwrite the number of fields line = epwin.readline() self._num_of_fields = min(len(line.strip().split(',')), 35) # create an annual analysis period analysis_period = AnalysisPeriod() # create an empty collection for each field in epw file for field_number in range(self._num_of_fields): # create header field = EPWFields.field_by_number(field_number) # the header of data collection header = Header(location=self.location, analysis_period=analysis_period, data_type=field.name, unit=field.unit, middle_hour=field.middle_hour) # create an empty data list with the header self._data.append(DataCollection(header=header)) # collect hourly data while line: data = line.strip().split(',') year, month, day, hour = map(int, data[:4]) # in an epw file year can be different for each month # since I'm using this timestamp as the key and will be using it for # sorting. I'm setting it up to 2015 - the real year will be collected # under modelYear timestamp = DateTime(month, day, hour - 1) for field_number in xrange(self._num_of_fields): value_type = EPWFields.field_by_number(field_number).value_type try: value = value_type(data[field_number]) except ValueError as e: # failed to convert the value for the specific TypeError if value_type != int: raise ValueError(e) value = int(round(float(data[field_number]))) self._data[field_number].append(DataPoint(value, timestamp)) line = epwin.readline() # move last item to start position for fields on the hour for field_number in xrange(self._num_of_fields): middle_hour = EPWFields.field_by_number(field_number).middle_hour if middle_hour is False: # shift datetimes for an hour for data in self._data[field_number]: try: data.datetime = data.datetime.add_hour(1) except ValueError: # this is the last hour data.datetime = DateTime(1, 1, 0) # now move the last hour to first last_hour = self._data[field_number].pop() self._data[field_number].insert(0, last_hour) self._is_data_loaded = True def _get_data_by_field(self, field_number): """Return a data field by field number. This is a useful method to get the values for fields that Ladybug currently doesn't import by default. You can find list of fields by typing EPWFields.fields Args: field_number: a value between 0 to 34 for different available epw fields. Returns: An annual Ladybug list """ if not self.is_data_loaded: self._import_data() # check input data if not 0 <= field_number < self._num_of_fields: raise ValueError("Field number should be between 0-%d" % self._num_of_fields) return self._data[field_number] # TODO: Add utility library to check file path, filename, etc
[docs] def save(self, folder=None, file_name=None): """Save epw object as an epw file.""" # check file_path if not folder: folder = self.folder if not file_name: file_name = os.path.splitext(self.file_name)[0] + '_modified.epw' full_path = os.path.join(folder, file_name) # load data if it's not loaded if not self.is_data_loaded: self._import_data() # write the file with open(full_path, writemode) as modEpwFile: modEpwFile.writelines(self._header) lines = [] try: # move first item to end position for fields on the hour for field in range(0, self._num_of_fields): middle_hour = EPWFields.field_by_number(field).middle_hour if middle_hour is False: first_hour = self._data[field].pop(0) self._data[field].append(first_hour) for hour in xrange(0, 8760): line = [] for field in range(0, self._num_of_fields): line.append(str(self._data[field].values[hour].value)) lines.append(",".join(line) + "\n") except IndexError: # cleaning up modEpwFile.close() length_error_msg = 'Data length is not 8760 hours and cannot be ' + \ 'saved as an EPW file.' raise ValueError(length_error_msg) else: modEpwFile.writelines(lines) finally: del(lines) # move last item to start position for fields on the hour for field in range(0, self._num_of_fields): middle_hour = EPWFields.field_by_number(field).middle_hour if middle_hour is False: last_hour = self._data[field].pop() self._data[field].insert(0, last_hour) return full_path
[docs] def import_data_by_field(self, field_number): """Return annual values for any field_number in epw file. This is a useful method to get the values for fields that Ladybug currently doesn't import by default. You can find list of fields by typing EPWFields.fields Args: field_number: a value between 0 to 34 for different available epw fields. 0 Year 1 Month 2 Day 3 Hour 4 Minute - 6 Dry Bulb Temperature 7 Dew Point Temperature 8 Relative Humidity 9 Atmospheric Station Pressure 10 Extraterrestrial Horizontal Radiation 11 Extraterrestrial Direct Normal Radiation 12 Horizontal Infrared Radiation Intensity 13 Global Horizontal Radiation 14 Direct Normal Radiation 15 Diffuse Horizontal Radiation 16 Global Horizontal Illuminance 17 Direct Normal Illuminance 18 Diffuse Horizontal Illuminance 19 Zenith Luminance 20 Wind Direction 21 Wind Speed 22 Total Sky Cover 23 Opaque Sky Cover 24 Visibility 25 Ceiling Height 26 Present Weather Observation 27 Present Weather Codes 28 Precipitable Water 29 Aerosol Optical Depth 30 Snow Depth 31 Days Since Last Snowfall 32 Albedo 33 Liquid Precipitation Depth 34 Liquid Precipitation Quantity Returns: An annual Ladybug list """ return self._get_data_by_field(field_number)
@property def years(self): """Return years as a Ladybug Data List.""" return self._get_data_by_field(0) @property def dry_bulb_temperature(self): """Return annual Dry Bulb Temperature as a Ladybug Data List. This is the dry bulb temperature in C at the time indicated. Note that this is a full numeric field (i.e. 23.6) and not an integer representation with tenths. Valid values range from -70C to 70 C. Missing value for this field is 99.9 Read more at: https://energyplus.net/sites/all/modules/custom/nrel_custom/pdfs /pdfs_v8.4.0/AuxiliaryPrograms.pdf (Chapter 2.9.1) """ return self._get_data_by_field(6) @property def dew_point_temperature(self): u"""Return annual Dew Point Temperature as a Ladybug Data List. This is the dew point temperature in C at the time indicated. Note that this is a full numeric field (i.e. 23.6) and not an integer representation with tenths. Valid values range from -70 C to 70 C. Missing value for this field is 99.9 Read more at: https://energyplus.net/sites/all/modules/custom/nrel_custom/pdfs /pdfs_v8.4.0/AuxiliaryPrograms.pdf (Chapter 2.9.1) """ return self._get_data_by_field(7) @property def relative_humidity(self): u"""Return annual Relative Humidity as a Ladybug Data List. This is the Relative Humidity in percent at the time indicated. Valid values range from 0% to 110%. Missing value for this field is 999. Read more at: https://energyplus.net/sites/all/modules/custom/nrel_custom/pdfs /pdfs_v8.4.0/AuxiliaryPrograms.pdf (Chapter 2.9.1) """ return self._get_data_by_field(8) @property def atmospheric_station_pressure(self): """Return annual Atmospheric Station Pressure as a Ladybug Data List. This is the station pressure in Pa at the time indicated. Valid values range from 31,000 to 120,000. (These values were chosen from the standard barometric pressure for all elevations of the World). Missing value for this field is 999999 Read more at: https://energyplus.net/sites/all/modules/custom/nrel_custom/pdfs /pdfs_v8.4.0/AuxiliaryPrograms.pdf (Chapter 2.9.1) """ return self._get_data_by_field(9) @property def extraterrestrial_horizontal_radiation(self): """Return annual Extraterrestrial Horizontal Radiation as a Ladybug Data List. This is the Extraterrestrial Horizontal Radiation in Wh/m2. It is not currently used in EnergyPlus calculations. It should have a minimum value of 0; missing value for this field is 9999. Read more at: https://energyplus.net/sites/all/modules/custom/nrel_custom/pdfs /pdfs_v8.4.0/AuxiliaryPrograms.pdf (Chapter 2.9.1) """ return self._get_data_by_field(10) @property def extraterrestrial_direct_normal_radiation(self): """Return annual Extraterrestrial Direct Normal Radiation as a Ladybug Data List. This is the Extraterrestrial Direct Normal Radiation in Wh/m2. (Amount of solar radiation in Wh/m2 received on a surface normal to the rays of the sun at the top of the atmosphere during the number of minutes preceding the time indicated). It is not currently used in EnergyPlus calculations. It should have a minimum value of 0; missing value for this field is 9999. Read more at: https://energyplus.net/sites/all/modules/custom/nrel_custom/pdfs /pdfs_v8.4.0/AuxiliaryPrograms.pdf (Chapter 2.9.1) """ return self._get_data_by_field(11) @property def horizontal_infrared_radiation_intensity(self): """Return annual Horizontal Infrared Radiation Intensity as a Ladybug Data List. This is the Horizontal Infrared Radiation Intensity in Wh/m2. If it is missing, it is calculated from the Opaque Sky Cover field as shown in the following explanation. It should have a minimum value of 0; missing value for this field is 9999. Read more at: https://energyplus.net/sites/all/modules/custom/nrel_custom/pdfs /pdfs_v8.4.0/AuxiliaryPrograms.pdf (Chapter 2.9.1) """ return self._get_data_by_field(12) @property def global_horizontal_radiation(self): """Return annual Global Horizontal Radiation as a Ladybug Data List. This is the Global Horizontal Radiation in Wh/m2. (Total amount of direct and diffuse solar radiation in Wh/m2 received on a horizontal surface during the number of minutes preceding the time indicated.) It is not currently used in EnergyPlus calculations. It should have a minimum value of 0; missing value for this field is 9999. Read more at: https://energyplus.net/sites/all/modules/custom/nrel_custom/pdfs /pdfs_v8.4.0/AuxiliaryPrograms.pdf (Chapter 2.9.1) """ return self._get_data_by_field(13) @property def direct_normal_radiation(self): """Return annual Direct Normal Radiation as a Ladybug Data List. This is the Direct Normal Radiation in Wh/m2. (Amount of solar radiation in Wh/m2 received directly from the solar disk on a surface perpendicular to the sun's rays, during the number of minutes preceding the time indicated.) If the field is missing ( >= 9999) or invalid ( < 0), it is set to 0. Counts of such missing values are totaled and presented at the end of the runperiod. Read more at: https://energyplus.net/sites/all/modules/custom/nrel_custom/pdfs /pdfs_v8.4.0/AuxiliaryPrograms.pdf (Chapter 2.9.1) """ return self._get_data_by_field(14) @property def diffuse_horizontal_radiation(self): """Return annual Diffuse Horizontal Radiation as a Ladybug Data List. This is the Diffuse Horizontal Radiation in Wh/m2. (Amount of solar radiation in Wh/m2 received from the sky (excluding the solar disk) on a horizontal surface during the number of minutes preceding the time indicated.) If the field is missing ( >= 9999) or invalid ( < 0), it is set to 0. Counts of such missing values are totaled and presented at the end of the runperiod Read more at: https://energyplus.net/sites/all/modules/custom/nrel_custom/pdfs /pdfs_v8.4.0/AuxiliaryPrograms.pdf (Chapter 2.9.1) """ return self._get_data_by_field(15) @property def global_horizontal_illuminance(self): """Return annual Global Horizontal Illuminance as a Ladybug Data List. This is the Global Horizontal Illuminance in lux. (Average total amount of direct and diffuse illuminance in hundreds of lux received on a horizontal surface during the number of minutes preceding the time indicated.) It is not currently used in EnergyPlus calculations. It should have a minimum value of 0; missing value for this field is 999999 and will be considered missing if greater than or equal to 999900. Read more at: https://energyplus.net/sites/all/modules/custom/nrel_custom/pdfs /pdfs_v8.4.0/AuxiliaryPrograms.pdf (Chapter 2.9.1) """ return self._get_data_by_field(16) @property def direct_normal_illuminance(self): """Return annual Direct Normal Illuminance as a Ladybug Data List. This is the Direct Normal Illuminance in lux. (Average amount of illuminance in hundreds of lux received directly from the solar disk on a surface perpendicular to the sun's rays, during the number of minutes preceding the time indicated.) It is not currently used in EnergyPlus calculations. It should have a minimum value of 0; missing value for this field is 999999 and will be considered missing if greater than or equal to 999900. Read more at: https://energyplus.net/sites/all/modules/custom/nrel_custom/pdfs /pdfs_v8.4.0/AuxiliaryPrograms.pdf (Chapter 2.9.1) """ return self._get_data_by_field(17) @property def diffuse_horizontal_illuminance(self): """Return annual Diffuse Horizontal Illuminance as a Ladybug Data List. This is the Diffuse Horizontal Illuminance in lux. (Average amount of illuminance in hundreds of lux received from the sky (excluding the solar disk) on a horizontal surface during the number of minutes preceding the time indicated.) It is not currently used in EnergyPlus calculations. It should have a minimum value of 0; missing value for this field is 999999 and will be considered missing if greater than or equal to 999900. Read more at: https://energyplus.net/sites/all/modules/custom/nrel_custom/pdfs /pdfs_v8.4.0/AuxiliaryPrograms.pdf (Chapter 2.9.1) """ return self._get_data_by_field(18) @property def zenith_luminance(self): """Return annual Zenith Luminance as a Ladybug Data List. This is the Zenith Illuminance in Cd/m2. (Average amount of luminance at the sky's zenith in tens of Cd/m2 during the number of minutes preceding the time indicated.) It is not currently used in EnergyPlus calculations. It should have a minimum value of 0; missing value for this field is 9999. Read more at: https://energyplus.net/sites/all/modules/custom/nrel_custom/pdfs /pdfs_v8.4.0/AuxiliaryPrograms.pdf (Chapter 2.9.1) """ return self._get_data_by_field(19) @property def wind_direction(self): """Return annual Wind Direction as a Ladybug Data List. This is the Wind Direction in degrees where the convention is that North=0.0, East=90.0, South=180.0, West=270.0. (Wind direction in degrees at the time indicated. If calm, direction equals zero.) Values can range from 0 to 360. Missing value is 999. Read more at: https://energyplus.net/sites/all/modules/custom/nrel_custom/pdfs /pdfs_v8.4.0/AuxiliaryPrograms.pdf (Chapter 2.9.1) """ return self._get_data_by_field(20) @property def wind_speed(self): """Return annual Wind Speed as a Ladybug Data List. This is the wind speed in m/sec. (Wind speed at time indicated.) Values can range from 0 to 40. Missing value is 999. Read more at: https://energyplus.net/sites/all/modules/custom/nrel_custom/pdfs /pdfs_v8.4.0/AuxiliaryPrograms.pdf (Chapter 2.9.1) """ return self._get_data_by_field(21) @property def total_sky_cover(self): """Return annual Total Sky Cover as a Ladybug Data List. This is the value for total sky cover (tenths of coverage). (i.e. 1 is 1/10 covered. 10 is total coverage). (Amount of sky dome in tenths covered by clouds or obscuring phenomena at the hour indicated at the time indicated.) Minimum value is 0; maximum value is 10; missing value is 99. Read more at: https://energyplus.net/sites/all/modules/custom/nrel_custom/pdfs /pdfs_v8.4.0/AuxiliaryPrograms.pdf (Chapter 2.9.1) """ return self._get_data_by_field(22) @property def opaque_sky_cover(self): """Return annual Opaque Sky Cover as a Ladybug Data List. This is the value for opaque sky cover (tenths of coverage). (i.e. 1 is 1/10 covered. 10 is total coverage). (Amount of sky dome in tenths covered by clouds or obscuring phenomena that prevent observing the sky or higher cloud layers at the time indicated.) This is not used unless the field for Horizontal Infrared Radiation Intensity is missing and then it is used to calculate Horizontal Infrared Radiation Intensity. Minimum value is 0; maximum value is 10; missing value is 99. Read more at: https://energyplus.net/sites/all/modules/custom/nrel_custom/pdfs /pdfs_v8.4.0/AuxiliaryPrograms.pdf (Chapter 2.9.1) """ return self._get_data_by_field(23) @property def visibility(self): """Return annual Visibility as a Ladybug Data List. This is the value for visibility in km. (Horizontal visibility at the time indicated.) It is not currently used in EnergyPlus calculations. Missing value is 9999. Read more at: https://energyplus.net/sites/all/modules/custom/nrel_custom/pdfs /pdfs_v8.4.0/AuxiliaryPrograms.pdf (Chapter 2.9.1) """ return self._get_data_by_field(24) @property def ceiling_height(self): """Return annual Ceiling Height as a Ladybug Data List. This is the value for ceiling height in m. (77777 is unlimited ceiling height. 88888 is cirroform ceiling.) It is not currently used in EnergyPlus calculations. Missing value is 99999 Read more at: https://energyplus.net/sites/all/modules/custom/nrel_custom/pdfs /pdfs_v8.4.0/AuxiliaryPrograms.pdf (Chapter 2.9.1) """ return self._get_data_by_field(25) @property def present_weather_observation(self): """Return annual Present Weather Observation as a Ladybug Data List. If the value of the field is 0, then the observed weather codes are taken from the following field. If the value of the field is 9, then "missing" weather is assumed. Since the primary use of these fields (Present Weather Observation and Present Weather Codes) is for rain/wet surfaces, a missing observation field or a missing weather code implies no rain. Read more at: https://energyplus.net/sites/all/modules/custom/nrel_custom/pdfs /pdfs_v8.4.0/AuxiliaryPrograms.pdf (Chapter 2.9.1) """ return self._get_data_by_field(26) @property def present_weather_codes(self): """Return annual Present Weather Codes as a Ladybug Data List. The present weather codes field is assumed to follow the TMY2 conventions for this field. Note that though this field may be represented as numeric (e.g. in the CSV format), it is really a text field of 9 single digits. This convention along with values for each "column" (left to right) is presented in Table 16. Note that some formats (e.g. TMY) does not follow this convention - as much as possible, the present weather codes are converted to this convention during WeatherConverter processing. Also note that the most important fields are those representing liquid precipitation - where the surfaces of the building would be wet. EnergyPlus uses "Snow Depth" to determine if snow is on the ground. Read more at: https://energyplus.net/sites/all/modules/custom/nrel_custom/pdfs /pdfs_v8.4.0/AuxiliaryPrograms.pdf (Chapter 2.9.1) """ return self._get_data_by_field(27) @property def precipitable_water(self): """Return annual Precipitable Water as a Ladybug Data List. This is the value for Precipitable Water in mm. (This is not rain - rain is inferred from the PresWeathObs field but a better result is from the Liquid Precipitation Depth field). It is not currently used in EnergyPlus calculations (primarily due to the unreliability of the reporting of this value). Missing value is 999. Read more at: https://energyplus.net/sites/all/modules/custom/nrel_custom/pdfs /pdfs_v8.4.0/AuxiliaryPrograms.pdf (Chapter 2.9.1) """ return self._get_data_by_field(28) @property def aerosol_optical_depth(self): """Return annual Aerosol Optical Depth as a Ladybug Data List. This is the value for Aerosol Optical Depth in thousandths. It is not currently used in EnergyPlus calculations. Missing value is .999. Read more at: https://energyplus.net/sites/all/modules/custom/nrel_custom/pdfs /pdfs_v8.4.0/AuxiliaryPrograms.pdf (Chapter 2.9.1) """ return self._get_data_by_field(29) @property def snow_depth(self): """Return annual Snow Depth as a Ladybug Data List. This is the value for Snow Depth in cm. This field is used to tell when snow is on the ground and, thus, the ground reflectance may change. Missing value is 999. Read more at: https://energyplus.net/sites/all/modules/custom/nrel_custom/pdfs /pdfs_v8.4.0/AuxiliaryPrograms.pdf (Chapter 2.9.1) """ return self._get_data_by_field(30) @property def days_since_last_snowfall(self): """Return annual Days Since Last Snow Fall as a Ladybug Data List. This is the value for Days Since Last Snowfall. It is not currently used in EnergyPlus calculations. Missing value is 99. Read more at: https://energyplus.net/sites/all/modules/custom/nrel_custom/pdfs /pdfs_v8.4.0/AuxiliaryPrograms.pdf (Chapter 2.9.1) """ return self._get_data_by_field(31) @property def albedo(self): """Return annual Albedo values as a Ladybug Data List. The ratio (unitless) of reflected solar irradiance to global horizontal irradiance. It is not currently used in EnergyPlus. Read more at: https://energyplus.net/sites/all/modules/custom/nrel_custom/pdfs /pdfs_v8.4.0/AuxiliaryPrograms.pdf (Chapter 2.9.1) """ return self._get_data_by_field(32) @property def liquid_precipitation_depth(self): """Return annual liquid precipitation depth as a Ladybug Data List. The amount of liquid precipitation (mm) observed at the indicated time for the period indicated in the liquid precipitation quantity field. If this value is not missing, then it is used and overrides the "precipitation" flag as rainfall. Conversely, if the precipitation flag shows rain and this field is missing or zero, it is set to 1.5 (mm). Read more at: https://energyplus.net/sites/all/modules/custom/nrel_custom/pdfs /pdfs_v8.4.0/AuxiliaryPrograms.pdf (Chapter 2.9.1) """ return self._get_data_by_field(33) @property def liquid_precipitation_quantity(self): """Return annual Liquid Precipitation Quantity as a Ladybug Data List. The period of accumulation (hr) for the liquid precipitation depth field. It is not currently used in EnergyPlus. Read more at: https://energyplus.net/sites/all/modules/custom/nrel_custom/ pdfs/pdfs_v8.4.0/AuxiliaryPrograms.pdf (Chapter 2.9.1) """ return self._get_data_by_field(34) @property def sky_temperature(self): """Return annual Sky Temperature as a Ladybug Data List. This value in degrees Celcius is derived from the Horizontal Infrared Radiation Intensity in Wh/m2. It represents the long wave radiant temperature of the sky Read more at: https://bigladdersoftware.com/epx/docs/8-9/engineering-reference /climate-calculations.html#energyplus-sky-temperature-calculation """ # create sky temperature data collection from horizontal infrared horiz_ir = self._get_data_by_field(12) sky_temp_header = copy.copy(horiz_ir.header) sky_temp_header.data_type = 'Sky Temperature' sky_temp_header.unit = 'C' # calculate sy temperature for each hour sky_temp_data = [] for hor_ir in horiz_ir.values: dat = hor_ir.datetime temp = ((float(hor_ir) / (5.6697 * (10**(-8))))**(0.25)) - 273.15 sky_temp_data.append(DataPoint(temp, dat)) sky_temp = DataCollection(sky_temp_data, sky_temp_header) return sky_temp def _get_wea_header(self): return "place %s\n" % self.location.city + \ "latitude %.2f\n" % self.location.latitude + \ "longitude %.2f\n" % -self.location.longitude + \ "time_zone %d\n" % (-self.location.time_zone * 15) + \ "site_elevation %.1f\n" % self.location.elevation + \ "weather_data_file_unit 1\n"
[docs] def to_wea(self, file_path=None, hoys=None): """Write an wea file from the epw file. WEA carries radiation values from epw. Gendaymtx uses these values to generate the sky. For an annual analysis it is identical to using epw2wea. args: file_path: Full file path for output file. hoys: List of hours of the year. Default is 0-8759. """ hoys = hoys or xrange(8760) if not file_path: file_path = self.file_path[:-4] + '.wea' if not file_path.lower().endswith('.wea'): file_path += '.wea' with open(file_path, "wb") as weaFile: # write header weaFile.write(self._get_wea_header()) # write values for hoy in hoys: dir_rad = self.direct_normal_radiation[hoy] dif_rad = self.diffuse_horizontal_radiation[hoy] line = "%d %d %.3f %d %d\n" \ % (dir_rad.datetime.month, dir_rad.datetime.day, dir_rad.datetime.hour + 0.5, dir_rad, dif_rad) weaFile.write(line) return file_path
[docs] def ToString(self): """Overwrite .NET ToString.""" return self.__repr__()
def __repr__(self): """epw file representation.""" return "EPW file Data for [%s]" % self.location.city
[docs]class EPWFields(object): """EPW weather file fields. Read more at: https://energyplus.net/sites/all/modules/custom/nrel_custom/pdfs/ pdfs_v8.4.0/AuxiliaryPrograms.pdf (Chapter 2.9.1) """ FIELDS = { 0: {'name': 'Year', 'type': int, 'middle_hour': False }, 1: {'name': 'Month', 'type': int, 'middle_hour': False }, 2: {'name': 'Day', 'type': int, 'middle_hour': False }, 3: {'name': 'Hour', 'type': int, 'middle_hour': False }, 4: {'name': 'Minute', 'type': int, 'middle_hour': False }, 5: {'name': 'Uncertainty Flags', 'type': str, 'middle_hour': False }, 6: {'name': 'Dry Bulb Temperature', 'type': float, 'unit': 'C', 'min': -70, 'max': 70, 'missing': 99.9, 'middle_hour': False }, 7: {'name': 'Dew Point Temperature', 'type': float, 'unit': 'C', 'min': -70, 'max': 70, 'missing': 99.9, 'middle_hour': False }, 8: {'name': 'Relative Humidity', 'type': int, 'unit': '%', 'missing': 999, 'min': 0, 'max': 110, 'middle_hour': False }, 9: {'name': 'Atmospheric Station Pressure', 'type': int, 'unit': 'Pa', 'missing': 999999, 'min': 31000, 'max': 120000, 'middle_hour': False }, 10: {'name': 'Extraterrestrial Horizontal Radiation', 'type': int, 'unit': 'Wh/m2', 'missing': 9999, 'min': 0, 'middle_hour': True }, 11: {'name': 'Extraterrestrial Direct Normal Radiation', 'type': int, 'unit': 'Wh/m2', 'missing': 9999, 'min': 0, 'middle_hour': True }, 12: {'name': 'Horizontal Infrared Radiation Intensity', 'type': int, 'unit': 'Wh/m2', 'missing': 9999, 'min': 0, 'middle_hour': False }, 13: {'name': 'Global Horizontal Radiation', 'type': int, 'unit': 'Wh/m2', 'missing': 9999, 'min': 0, 'middle_hour': True }, 14: {'name': 'Direct Normal Radiation', 'type': int, 'unit': 'Wh/m2', 'missing': 9999, 'min': 0, 'middle_hour': True }, 15: {'name': 'Diffuse Horizontal Radiation', 'type': int, 'unit': 'Wh/m2', 'missing': 9999, 'min': 0, 'middle_hour': True }, 16: {'name': 'Global Horizontal Illuminance', 'type': int, 'unit': 'lux', 'missing': 999999, # note will be missing if >= 999900 'min': 0, 'middle_hour': True }, 17: {'name': 'Direct Normal Illuminance', 'type': int, 'unit': 'lux', 'missing': 999999, # note will be missing if >= 999900 'min': 0, 'middle_hour': True }, 18: {'name': 'Diffuse Horizontal Illuminance', 'type': int, 'unit': 'lux', 'missing': 999999, # note will be missing if >= 999900 'min': 0, 'middle_hour': True }, 19: {'name': 'Zenith Luminance', 'type': int, 'unit': 'Cd/m2', 'missing': 9999, # note will be missing if >= 9999 'min': 0, 'middle_hour': True }, 20: {'name': 'Wind Direction', 'type': int, 'unit': 'degrees', 'missing': 999, 'min': 0, 'max': 360, 'middle_hour': False }, 21: {'name': 'Wind Speed', 'type': float, 'unit': 'm/s', 'missing': 999, 'min': 0, 'max': 40, 'middle_hour': False }, 22: {'name': 'Total Sky Cover', # (used if Horizontal IR Intensity missing) 'type': int, 'missing': 99, 'min': 0, 'max': 10, 'middle_hour': False }, 23: {'name': 'Opaque Sky Cover', # (used if Horizontal IR Intensity missing) 'type': int, 'missing': 99, 'middle_hour': False }, 24: {'name': 'Visibility', 'type': float, 'unit': 'km', 'missing': 9999, 'middle_hour': False }, 25: {'name': 'Ceiling Height', 'type': int, 'unit': 'm', 'missing': 99999, 'middle_hour': False }, 26: {'name': 'Present Weather Observation', 'type': int, 'middle_hour': False }, 27: {'name': 'Present Weather Codes', 'type': int, 'middle_hour': False }, 28: {'name': 'Precipitable Water', 'type': int, 'unit': 'mm', 'missing': 999, 'middle_hour': False }, 29: {'name': 'Aerosol Optical Depth', 'type': float, 'unit': 'thousandths', 'missing': 999, 'middle_hour': False }, 30: {'name': 'Snow Depth', 'type': int, 'unit': 'cm', 'missing': 999, 'middle_hour': False }, 31: {'name': 'Days Since Last Snowfall', 'type': int, 'missing': 99, 'middle_hour': False }, 32: {'name': 'Albedo', 'type': float, 'missing': 999, 'middle_hour': False }, 33: {'name': 'Liquid Precipitation Depth', 'type': float, 'unit': 'mm', 'missing': 999, 'middle_hour': False }, 34: {'name': 'Liquid Precipitation Quantity', 'type': float, 'unit': 'hr', 'missing': 99, 'middle_hour': False } }
[docs] @classmethod def field_by_number(cls, field_number): """Return an EPWField based on field number. 0 Year 1 Month 2 Day 3 Hour 4 Minute - 6 Dry Bulb Temperature 7 Dew Point Temperature 8 Relative Humidity 9 Atmospheric Station Pressure 10 Extraterrestrial Horizontal Radiation 11 Extraterrestrial Direct Normal Radiation 12 Horizontal Infrared Radiation Intensity 13 Global Horizontal Radiation 14 Direct Normal Radiation 15 Diffuse Horizontal Radiation 16 Global Horizontal Illuminance 17 Direct Normal Illuminance 18 Diffuse Horizontal Illuminance 19 Zenith Luminance 20 Wind Direction 21 Wind Speed 22 Total Sky Cover 23 Opaque Sky Cover 24 Visibility 25 Ceiling Height 26 Present Weather Observation 27 Present Weather Codes 28 Precipitable Water 29 Aerosol Optical Depth 30 Snow Depth 31 Days Since Last Snowfall 32 Albedo 33 Liquid Precipitation Depth 34 Liquid Precipitation Quantity """ return EPWField(cls.FIELDS[field_number])
def __repr__(self): """EPW fields representation.""" fields = ( '{}: {}'.format(key, value['name']) for key, value in self.FIELDS.items() ) return '\n'.join(fields)
[docs]class EPWField(object): """An EPW field. Attributes: name: Name of the field. type: field value type (e.g. int, float, str) unit: Field unit. """ def __init__(self, field_dict): self.name = field_dict['name'] self.value_type = field_dict['type'] self.middle_hour = field_dict['middle_hour'] if 'unit' in field_dict: self.unit = field_dict['unit'] else: self.unit = None