ladybugdynamo.ladybug.core module
import re
import copy
import euclid
class DateTimeLib:
"""Ladybug DateTime Libray
This class includes useful data and methods for date and time
"""
monthList = ['JAN', 'FEB', 'MAR', 'APR', 'MAY', 'JUN', 'JUL', 'AUG', 'SEP', 'OCT', 'NOV', 'DEC']
numOfDaysEachMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
numOfDaysUntilMonth = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365]
numOfHoursUntilMonth = [24 * numOfDay for numOfDay in numOfDaysUntilMonth]
@classmethod
def checkDateTime(cls, month, day, hour, minute = None):
"""Checks if time combination is a valid time."""
# check month
if not 1 <= month <= 12:
raise ValueError("month should be between 1-12")
if day < 1 or day > cls.numOfDaysEachMonth[month-1]:
raise ValueError("Number of days for %s should be \
between 1-%d"%(cls.monthList[month-1], cls.numOfDaysEachMonth[month-1]))
if not 0 <= hour <= 24:
raise ValueError("hour should be between 0.0-24.0")
if not minute or minute == 0:
if hour == 0 and day == 1 and month == 1:
# last hour of the year
month = 12
day = 31
hour = 24
elif hour == 0 and day == 1:
# The last hour of the last day of the month before
month -= 1
day = cls.numOfDaysEachMonth[month-1]
hour = 24
elif hour==0:
# the last hour of the day before
hour = 24
day -= 1
hour, minute = cls.__getHourAndMinute(hour, minute)
return month, day, hour, minute
@classmethod
def getHourOfYear(cls, month, day, hour, minute = None):
"""Return hour of the year between 1 and 8760."""
# make sure input values are correct
month, day, hour, minute = cls.checkDateTime(month, day, hour, minute)
# fix the end day
JD = cls.numOfDaysUntilMonth[month-1] + int(day)
return (JD - 1) * 24 + hour
@classmethod
def getDayOfYear(cls, month, day):
"""Retuen day of the year between 1 and 365"""
# make sure input values are correct
month, day, hour, minute = cls.checkDateTime(month, day, hour = 1)
# fix the end day
return cls.numOfDaysUntilMonth[month-1] + int(day)
@classmethod
def getMonthDayHourAndMinute(cls, hourOfYear):
"""Return month, day and hour for an hour of the year"""
if hourOfYear == 8760: return 12, 31, 24, 0
# find month
for monthCount in range(12):
if hourOfYear <= cls.numOfHoursUntilMonth[monthCount + 1]:
month = monthCount + 1
break
# find day and hour
if hourOfYear%24 == 0:
# last hour of the day
day = int((hourOfYear - cls.numOfHoursUntilMonth[month - 1])/24)
hour = 24
minute = 0
else:
day = int((hourOfYear - cls.numOfHoursUntilMonth[month - 1])/24) + 1
hour = int(hourOfYear%24)
minute = cls.__getHourAndMinute(hourOfYear)[1]
return month, day, hour, minute
@staticmethod
def __getHourAndMinute(hour, minute = None):
"""Calculate and return hour and minute
This method is mainly usefule for calculating minutes from float hours
if minute is missing. otherwise it will only check the inputs append
returns the checked values
Args:
hour: A float value between 0.0-24.0
minutes: An integer between 0-59. Default in None
Returns:
hour: An interger between 0-24
minute: An integer between 0-59
"""
if not minute:
minute = (hour - int(hour)) * 60
# cast values to integer
hour = int(hour + int(minute/60))
minute = minute%60
return hour, minute
# TODO: add comparison methods (largerthan, smallerthan, ...)
class LBDateTime:
"""Ladybug Date time"""
def __init__(self, month = 1, day = 1, hour = 1, minute = None):
self.month, self.day, self.hour, \
self.minute = DateTimeLib.checkDateTime(month, day, hour, minute)
self.floatHour = self.hour + self.minute/60.0
self.DOY = DateTimeLib.getDayOfYear(self.month, self.day)
self.HOY = DateTimeLib.getHourOfYear(self.month, self.day, self.hour, self.minute)
self.MOY = self.HOY * 60 + self.minute # minute of the year
self.floatHOY = self.HOY + self.minute/60.0
@classmethod
def fromHOY(cls, HOY):
"""Create Ladybug Datetime from an hour of the year
Args:
HOY: A float value between 0.0 to 8760.0
"""
month, day, hour, minute = DateTimeLib.getMonthDayHourAndMinute(HOY)
return cls(month, day, hour, minute)
@classmethod
def fromMOY(cls, MOY):
"""create Ladybug DateTime from Minute of the year"""
HOY = MOY/60.0
return cls.fromHOY(HOY)
@classmethod
def fromDateTimeString(cls, datetimeString):
day, month, hour, minute = datetimeString \
.replace(" at ", " ") \
.replace(":", " ") \
.split(" ")
month = DateTimeLib.monthList.index(month.upper()) + 1
return cls(month, int(day), int(hour), int(minute))
@property
def humanReadableHour(self):
"""Return hours and minutes in a human readable way"""
minute = str(self.minute)
if len(minute) == 1: minute = "0" + minute
return "%d:%s"%(self.hour, minute)
def __repr__(self):
return "%d %s at %s"%( self.day, DateTimeLib.monthList[self.month-1], self.humanReadableHour)
# TODO: Add NA analysis period
class AnalysisPeriod:
"""Ladybug Analysis Period.
A continuous analysis period between two days of the year between certain hours
Attributes:
stMonth: An integer between 1-12 for starting month (default = 1)
stDay: An integer between 1-31 for starting day (default = 1).
Note that some months are shorter than 31 days.
stHour: An integer between 1-24 for starting hour (default = 1)
endMonth: An integer between 1-12 for ending month (default = 12)
endDay: An integer between 1-31 for ending day (default = 31)
Note that some months are shorter than 31 days.
endHour: An integer between 1-24 for ending hour (default = 24)
timestep: An integer number from 1, 2, 3, 4, 5, 6, 10, 12, 15, 20, 30, 60
"""
__validTimesteps = {1 : 60, 2 : 30, 3 : 20, 4 : 15, 5 : 12, \
6 : 10, 10 : 6, 12 : 5, 15 : 4, 20 : 3, 30 : 2, 60 : 1}
#TODO: handle timestep between 1-60
def __init__(self, stMonth = 1, stDay = 1, stHour = 1, endMonth = 12,
endDay = 31, endHour = 24, timestep = 1):
"""Init an analysis period"""
# calculate start time and end time
self.stTime = LBDateTime(int(stMonth), int(stDay), float(stHour))
self.endTime = LBDateTime(int(endMonth), int(endDay), float(endHour))
if self.stTime.hour <= self.endTime.hour:
self.overnight = False # each segments of hours will be in a single day
else:
self.overnight = True
# A reversed analysis period defines a period that starting month is after ending month
# (e.g DEC to JUN)
if self.stTime.HOY > self.endTime.HOY:
self.reversed = True
else:
self.reversed = False
# check time step
if timestep not in self.__validTimesteps:
raise ValueError("Invalid timestep. Valid values are %s"%str(self.__validTimesteps))
# calculate time stamp
self.timestep = timestep
self.minuteIntervals = self.__validTimesteps[timestep]
# calculate timestamps and hoursOfYear
self.__timestampsData = {} # A dictionary for timedates. Key values will be minute of year
self.__calculateTimestamps()
@classmethod
def fromAnalysisPeriod(cls, analysisPeriod):
"""Create and Analysis Period from an analysis period
This method is useful to be called from inside Grasshopper or Dynamo
"""
if not analysisPeriod:
print "Analysis period is set to annual"
return AnalysisPeriod()
elif isinstance(analysisPeriod, AnalysisPeriod):
return analysisPeriod
elif isinstance(analysisPeriod, str):
return cls.__fromAnalysisPeriodString(analysisPeriod)
@classmethod
def __fromAnalysisPeriodString(cls, analysisPeriodString):
# %s/%s to %s/%s between %s to %s @%s
ap = analysisPeriodString.lower() \
.replace(' ', '') \
.replace('to', ' ') \
.replace('/', ' ') \
.replace('between', ' ') \
.replace('@', ' ')
try:
stMonth, stDay, \
endMonth, endDay, \
stHour, endHour, timestep = ap.split(' ')
return cls(stMonth, stDay, stHour, endMonth, endDay, endHour, int(timestep))
except:
raise ValueError(analysisPeriodString + " is not a valid analysis period!")
def __calculateTimestamps(self):
"""Return a list of Ladybug DateTime in this analysis period."""
# calculate based on minutes
curr = self.stTime.MOY - 60 + self.minuteIntervals
if not self.reversed:
while curr <= self.endTime.MOY:
time = LBDateTime.fromMOY(curr)
if not self.isTimeIncluded(time):
self.__timestampsData[time.MOY] = time
curr += self.minuteIntervals
else:
while (0 <= curr <= self.endTime.MOY or self.stTime.MOY <= curr < 8760 * 60):
time = LBDateTime.fromMOY(curr)
if not self.isTimeIncluded(time):
self.__timestampsData[time.MOY] = time
curr = (curr + self.minuteIntervals)%(8760 * 60)
@property
def dates(self):
"""A sorted list of dates in this analysis period"""
# sort dictionary based on key values (minute of the year)
sortedTimestamps = sorted(self.__timestampsData.items(), key= lambda x: x[0])
return [timestamp[1] for timestamp in sortedTimestamps]
@property
def HOYs(self):
"""A sorted list of hours of year in this analysis period"""
return [timestamp.HOY for timestamp in self.dates]
@property
def floatHOYs(self):
"""A sorted list of hours of year as float values in this analysis period"""
return [timestamp.floatHOY for timestamp in self.dates]
@property
def totalNumOfHours(self):
"""Total number of hours during this analysis period"""
return len(self.__timestampsData)/self.timestep
@property
def isAnnual(self):
"""Check if an analysis period is annual"""
return True if self.totalNumOfHours == 8760 else False
def isTimeIncluded(self, time):
"""Check if time is included in analysis period.
Return True if time is inside this analysis period,
otherwise return False
Args:
time: A LBDateTime to be tested
Returns:
A boolean. True if time is included in analysis period
"""
# time filtering in Ladybug and honeybee is slightly different since start hour and end hour will be
# applied for every day. For instance 2/20 9am to 2/22 5pm means hour between 9-17 during 20, 21 and 22 of Feb
return time.MOY in self.__timestampsData
def __repr__(self):
return "%s/%s to %s/%s between %s to %s @%d"%\
(self.stTime.month, self.stTime.day, \
self.endTime.month, self.endTime.day, \
self.stTime.hour, self.endTime.hour, \
self.timestep)
class LBHeader:
"""Standard Ladybug header for lists.
The header carries data for city,
data type, unit, and analysis period
Attributes:
city: A string for the city name
dataType: A valid Ladybug data type. Try DataType.dataTypes to see list of data types
unit: dataType unit. If empty string it will be set based on dataType
timestep: Data timestep "Hourly", "Daily", "Monthly", "Annual", "N/A"
analysisPeriod: A Ladybug analysis period. (defualt: 1 Jan 1 to 31 Dec 24)
"""
def __init__(self, city = 'unknown', dataType = 'unknown', unit = 'unknown', frequency = 'unknown', analysisPeriod = None):
"""Initiate Ladybug header for lists."""
self.city = city
self.dataType = dataType
self.unit = unit
self.frequency = frequency
self.analysisPeriod = 'unknown' if not analysisPeriod \
else AnalysisPeriod.fromAnalysisPeriod(analysisPeriod)
def duplicate(self):
return copy.deepcopy(self)
@property
def __key(self):
return 'location|dataType|units|frequency|dataPeriod'
def toList(self):
"""Return Ladybug header as a list"""
return [
self.__key,
self.city,
self.dataType,
self.unit,
self.frequency,
self.analysisPeriod
]
def __repr__(self):
return "%s for %s during %s"%(self.dataType, self.city, self.analysisPeriod)
# TODO: write classes for latitude, longitude, etc
class Location:
def __init__(self, city = '', country = '', latitude = '0.00', \
longitude = '0.00', timeZone = '0.00', elevation ='0.00', \
source = '', stationId = ''):
self.city = str(city)
self.country = str(country)
self.latitude = float(latitude)
self.longitude = float(longitude)
self.timeZone = float(timeZone)
self.elevation = float(elevation)
self.source = str(source)
self.stationId = str(stationId)
def createFromEPString(self, EPString):
"""Create a Ladybug location from an EnergyPlus location string
Parameters:
EPString: Standard EP location string
Usage:
l = Location() #initiate location
l.createFromEPString(EPString)
print "LAT:%s, LON:%s"%(l.latitude, l.longitude)
"""
try:
self.city, self.latitude, \
self.longitude, self.timeZone, \
self.elevation = re.findall(r'\r*\n*([a-zA-Z0-9.:_-]*)[,|;]', \
EPString, re.DOTALL)[1:]
self.latitude = float(self.latitude)
self.longitude = float(self.longitude)
self.timeZone = float(self.timeZone)
self.elevation = float(self.elevation)
except Exception, e:
raise Exception ("Failed to import EP string! %s"%str(e))
def duplicate(self):
return copy.deepcopy(self)
@property
def EPStyleLocationString(self):
"""Return EnergyPlus's location string"""
return "Site:Location,\n" + \
self.city + ',\n' + \
str(self.latitude) +', !Latitude\n' + \
str(self.longitude) +', !Longitude\n' + \
str(self.timeZone) +', !Time Zone\n' + \
str(self.elevation) + '; !Elevation'
def __repr__(self):
return "%s"%(self.EPStyleLocationString)
class LBData:
"""Ladybug data point"""
# TODO: Change value to be an object from it's data type
# Check datatype.py for available datatypes
def __init__(self, value, dateTime):
self.datetime = dateTime
self.value = value
@classmethod
def fromLBData(cls, data):
assert isinstance(data, LBData), "Input is not a LBData."
return data
def updateValue(self, newValue):
self.value = newValue
def __int__(self):
return int(self.value)
def __float__(self):
return float(self.value)
def __str__(self):
return str(self.value)
def __eq__(self, other):
return self.value == float(other)
def __ne__(self, other):
return self.value != float(other)
def __lt__(self, other):
return self.value < other
def __gt__(self, other):
return self.value > other
def __le__(self, other):
return self.value <= other
def __ge__(self, other):
return self.value >= other
def __add__(self, other):
return self.value + other
def __sub__(self, other):
return self.value - other
def __mul__(self, other):
return self.value * other
def __floordiv__(self, other):
return self.value // other
def __div__(self, other):
return self.value / other
def __mod__(self, other):
return self.value%other
def __pow__(self, other):
return self.value**other
def __radd__(self, other):
return self.__add__(other)
def __rsub__(self, other):
return other - self.value
def __rmul__(self, other):
return self.__mul__(other)
def __rfloordiv__(self, other):
return other//self.value
def __rdiv__(self, other):
return other/self.value
def __rmod__(self, other):
return other%self.value
def __rpow__(self, other):
return other**self.value
def __repr__(self):
return self.__str__()
class LBPatchData(LBData):
"""Ladybug sky patch data type"""
def __init__(self, value, vector):
# sky data doesn't have time
datetime = LBDateTime()
LBData.__init__(self, value, datetime)
self.vector = euclid.Vector3(*vector)
class DataList:
"""Ladybug data list
A list of ladybug data with a LBHeader
"""
def __init__(self, data = None, header = None):
self.__data = self.checkInputData(data)
self.header = LBHeader() if not header else header
def values(self, header = False):
"""Return the list of values
Args:
header: A boolean that indicates if values should include the headers
Return:
A list of values
"""
if not header:
return self.__data
else:
return self.header.toList() + self.__data
@property
def timeStamps(self):
"List of time stamps for current data"
return [value.datetime for value in self.__data]
def checkInputData(self, data):
"""Check input data"""
if not data: return []
return [LBData.fromLBData(d) for d in data]
def append(self, data):
"""Append LBData to current list"""
self.extend([data])
def extend(self, dataList):
"""Extend a list of LBData to the end of current list"""
self.__data.extend(self.checkInputData(dataList))
def duplicate(self):
"""Duplicate current data list"""
return copy.deepcopy(self)
@staticmethod
def average(data):
values = [value.value for value in data]
return sum(values)/len(data)
def groupDataByMonth(self, monthRange = range(1,13), userDataList = None):
"""Return a dictionary of values where values are grouped for each month
key values are between 1-12
Parameters:
monthRange: A list of numbers for months. Default is 1-12
userDataList: An optional data list of LBData to be processed
Usage:
epwfile = EPW("epw file address")
monthlyValues = epwfile.dryBulbTemperature.groupValuesByMonth()
print monthlyValues[2] # returns values for the month of March
"""
hourlyDataByMonth = {}
if userDataList:
data = [LBData.fromLBData(d) for d in userDataList]
else:
data = self.__data
for d in data:
if not d.datetime.month in monthRange: continue
if not hourlyDataByMonth.has_key(d.datetime.month): hourlyDataByMonth[d.datetime.month] = [] #create an empty list for month
hourlyDataByMonth[d.datetime.month].append(d)
print "Found data for months " + str(hourlyDataByMonth.keys())
return hourlyDataByMonth
def groupDataByDay(self, dayRange = range(1, 366), userDataList = None):
"""Return a dictionary of values where values are grouped by each day of year
key values are between 1-365
Parameters:
dayRange: A list of numbers for days. Default is 1-365
userDataList: An optional data list of LBData to be processed
Usage:
epwfile = EPW("epw file address")
dailyValues = epwfile.dryBulbTemperature.groupDataByDay(range(1, 30))
print dailyValues[2] # returns values for the second day of year
"""
hourlyDataByDay = {}
if userDataList:
data = [LBData.fromLBData(d) for d in userDataList]
else:
data = self.__data
for d in data:
DOY = DateTimeLib.getDayOfYear(d.datetime.month, d.datetime.day)
if not DOY in dayRange: continue
if not hourlyDataByDay.has_key(DOY): hourlyDataByDay[DOY] = [] #create an empty list for month
hourlyDataByDay[DOY].append(d)
print "Found data for " + str(len(hourlyDataByDay.keys())) + " days."
return hourlyDataByDay
def groupDataByHour(self, hourRange = range(1, 25), userDataList = None):
"""Return a dictionary of values where values are grouped by each hour of day
key values are between 1-24
Parameters:
hourRange: A list of numbers for hours. Default is 1-24
userDataList: An optional data list of LBData to be processed
Usage:
epwfile = EPW("epw file address")
monthlyValues = epwfile.dryBulbTemperature.groupDataByMonth([1])
groupedHourlyData = epwfile.dryBulbTemperature.groupDataByHour(userDataList = monthlyValues[2])
for hour, data in groupedHourlyData.items():
print "average temperature values for hour " + str(hour) + " during JAN is " + str(core.DataList.average(data)) + " " + DBT.header.unit
"""
hourlyDataByHour = {}
if userDataList:
data = [LBData.fromLBData(d) for d in userDataList]
else:
data = self.__data
for d in data:
if not d.datetime.hour in hourRange: continue
if not hourlyDataByHour.has_key(d.datetime.hour): hourlyDataByHour[d.datetime.hour] = [] #create an empty list for month
hourlyDataByHour[d.datetime.hour].append(d)
print "Found data for hours " + str(hourlyDataByHour.keys())
return hourlyDataByHour
# TODO: Add validity check for input values
def updateDataForAnAnalysisPeriod(self, values, analysisPeriod = None):
"""Replace current values in data list with new set of values
for a specific analysis period.
Length of values should be equal to number of hours in analysis period
Parameters:
values: A list of values to be replaced in the file
analysisPeriod: An analysis period for input the input values.
Default is set to the whole year.
"""
if not analysisPeriod:
analysisPeriod = AnalysisPeriod()
# check length of data vs length of analysis period
if len(values) != analysisPeriod.totalNumOfHours:
raise ValueError("Length of values %d is not equal to " + \
"number of hours in analysis period %d"%(len(values), \
analysisPeriod.totalNumOfHours))
# get all time stamps
timeStamps = analysisPeriod.dates
# map timeStamps and values
newValues = {}
for count, value in enumerate(values):
HOY = timeStamps[count].HOY
newValues[HOY] = value
# update values
updatedCount = 0
for counter, data in enumerate(self.__data):
try:
value = newValues[data.datetime.HOY]
data.updateValue(value)
updatedCount+=1
except KeyError:
pass
# return self for chaining methods
print "%s data are updated for %d hours."%(self.header.dataType, updatedCount)
# return self for chaining methods
return self
def updateDataForHoursOfYear(self, values, hoursOfYear):
"""Replace current values in data list with new set of values
for a list of hours of year
Length of values should be equal to number of hours in hours of year
Parameters:
values: A list of values to be replaced in the file
hoursOfYear: A list of HOY between 1 and 8760
"""
# check length of data vs length of analysis period
if len(values) != len(hoursOfYear):
raise ValueError("Length of values %d is not equal to " + \
"number of hours in analysis period %d"%(len(values), \
len(hoursOfYear)))
# map hours and values
newValues = {}
for count, value in enumerate(values):
HOY = hoursOfYear[count]
newValues[HOY] = value
# update values
updatedCount = 0
for counter, data in enumerate(self.__data):
try:
value = newValues[data.datetime.HOY]
data.updateValue(value)
updatedCount+=1
except KeyError:
pass
print "%s data %s updated for %d hour%s."%(self.header.dataType, \
'are' if len(values)>1 else 'is', updatedCount,\
's' if len(values)>1 else '')
# return self for chaining methods
return self
def updateDataForAnHour(self, value, hourOfYear):
"""Replace current value in data list with a new value
for a specific hour of the year
Parameters:
value: A single value
hoursOfYear: The hour of the year
"""
return self.updateDataForHoursOfYear([value], [hourOfYear])
def filterByAnalysisPeriod(self, analysisPeriod):
"""Filter the list based on an analysis period
Parameters:
analysis period: A Ladybug analysis period
Return:
A new DataList with filtered data
Usage:
analysisPeriod = AnalysisPeriod(2,1,1,3,31,24) #start of Feb to end of Mar
epw = EPW("c:\ladybug\weatherdata.epw")
DBT = epw.dryBulbTemperature
filteredDBT = DBT.filterByAnalysisPeriod(analysisPeriod)
"""
if not analysisPeriod or analysisPeriod.isAnnual:
print "You need a valid analysis period to filter data."
return self
# There is no guarantee that data is continuous so I iterate through the
# each data point one by one
filteredData = [ d for d in self.__data if analysisPeriod.isTimeIncluded(d.datetime)]
# create a new filteredData
filteredHeader = self.header.duplicate()
filteredHeader.analysisPeriod = analysisPeriod
filteredDataList = DataList(filteredData, filteredHeader)
return filteredDataList
def filterByHOYs(self, HOYs):
"""Filter the list based on an analysis period
Parameters:
HOYs: A List of hours of the year [1-8760]
Return:
A new DataList with filtered data
Usage:
HOYs = range(1,48) # The first two days of the year
epw = EPW("c:\ladybug\weatherdata.epw")
DBT = epw.dryBulbTemperature
filteredDBT = DBT.filterByHOYs(HOYs)
"""
# There is no guarantee that data is continuous so I iterate through the
# each data point one by one
filteredData = [ d for d in self.__data if d.datetime.HOY in HOYs]
# create a new filteredData
filteredHeader = self.header.duplicate()
filteredHeader.analysisPeriod = "unknown"
filteredDataList = DataList(filteredData, filteredHeader)
return filteredDataList
def filterByConditionalStatement(self, statement):
"""Filter the list based on an analysis period
Parameters:
statement: A conditional statement as a string (e.g. x>25 and x%5==0).
The variable should always be named as x
Return:
A new DataList with filtered data
Usage:
epw = EPW("c:\ladybug\weatherdata.epw")
DBT = epw.dryBulbTemperature
# filter data for when dry bulb temperature is more then 25
filteredDBT = DBT.filterByConditionalStatement('x > 25')
# get the list of time stamps that meet the conditional statement
print filteredDBT.timeStamps
"""
def checkInputStatement(statement):
stStatement = statement.lower().replace("and", "").replace("or", "")\
.replace("not", "").replace("in", "").replace("is", "")
l = [s for s in stStatement if s.isalpha()]
if list(set(l)) != ['x']:
statementErrorMsg = 'Invalid input statement. Statement should be a valid Python statement' + \
' and the variable should be named as x'
raise ValueError(statementErrorMsg)
checkInputStatement(statement)
statement = statement.replace('x', 'd.value')
filteredData = [d for d in self.__data if eval(statement)]
# create a new filteredData
filteredHeader = self.header.duplicate()
filteredHeader.analysisPeriod = 'N/A'
filteredDataList = DataList(filteredData, filteredHeader)
return filteredDataList
def filterByPattern(self, patternList):
"""Filter the list based on a list of Boolean
Length of Boolean should be equal to length of values in DataList
Parameters:
patternList: A list of True, False values
Return:
A new DataList with filtered data
"""
# check length of data vs length of analysis period
if len(self.values) != len(patternList):
print len(self.values), len(patternList)
errMsg = "Length of values %d is not equal to number of patterns %d" \
%(len(self.values), len(patternList))
raise ValueError(errMsg)
filteredData = [d for count, d in enumerate(self.__data) if patternList[count]]
# create a new filteredData
filteredHeader = self.header.duplicate()
filteredHeader.analysisPeriod = 'N/A'
filteredDataList = DataList(filteredData, filteredHeader)
return filteredDataList
def averageMonthly(self, userDataList = None):
"""Return a dictionary of values for average values for available months"""
# group data for each month
monthlyValues = self.groupDataByMonth(userDataList= userDataList)
averageValues = dict()
# average values for each month
for month, values in monthlyValues.items():
averageValues[month] = self.average(values)
return averageValues
def averageMonthlyForEachHour(self, userDataList = None):
"""Calculate average value for each hour during each month
This method returns a dictionary with nested dictionaries for each hour
"""
# get monthy values
monthlyHourlyValues = self.groupDataByMonth(userDataList= userDataList)
# group data for each hour in each month and collect them in a dictionary
averagedMonthlyValuesPerHour = {}
for month, monthlyValues in monthlyHourlyValues.items():
if month not in averagedMonthlyValuesPerHour: averagedMonthlyValuesPerHour[month] = {}
# group data for each hour
groupedHourlyData = self.groupDataByHour(userDataList = monthlyValues)
for hour, data in groupedHourlyData.items():
averagedMonthlyValuesPerHour[month][hour] = self.average(data)
return averagedMonthlyValuesPerHour
def __len__(self):
return len(self.__data)
def __getitem__(self, key):
return self.__data[key]
def __setitem__(self, key, value):
self.__data[key] = value
def __delitem__(self, key):
del self.__data[key]
def __iter__(self):
return iter(self.__data)
# TODO: Reverse analysis period in header
def __reversed__(self):
return FunctionalList(reversed(self.__data))
def __repr__(self):
return "Ladybug.DataList#%s"%self.header.dataType
Classes
class AnalysisPeriod
Ladybug Analysis Period.
A continuous analysis period between two days of the year between certain hours
Attributes: stMonth: An integer between 1-12 for starting month (default = 1) stDay: An integer between 1-31 for starting day (default = 1). Note that some months are shorter than 31 days. stHour: An integer between 1-24 for starting hour (default = 1) endMonth: An integer between 1-12 for ending month (default = 12) endDay: An integer between 1-31 for ending day (default = 31) Note that some months are shorter than 31 days. endHour: An integer between 1-24 for ending hour (default = 24) timestep: An integer number from 1, 2, 3, 4, 5, 6, 10, 12, 15, 20, 30, 60
class AnalysisPeriod:
"""Ladybug Analysis Period.
A continuous analysis period between two days of the year between certain hours
Attributes:
stMonth: An integer between 1-12 for starting month (default = 1)
stDay: An integer between 1-31 for starting day (default = 1).
Note that some months are shorter than 31 days.
stHour: An integer between 1-24 for starting hour (default = 1)
endMonth: An integer between 1-12 for ending month (default = 12)
endDay: An integer between 1-31 for ending day (default = 31)
Note that some months are shorter than 31 days.
endHour: An integer between 1-24 for ending hour (default = 24)
timestep: An integer number from 1, 2, 3, 4, 5, 6, 10, 12, 15, 20, 30, 60
"""
__validTimesteps = {1 : 60, 2 : 30, 3 : 20, 4 : 15, 5 : 12, \
6 : 10, 10 : 6, 12 : 5, 15 : 4, 20 : 3, 30 : 2, 60 : 1}
#TODO: handle timestep between 1-60
def __init__(self, stMonth = 1, stDay = 1, stHour = 1, endMonth = 12,
endDay = 31, endHour = 24, timestep = 1):
"""Init an analysis period"""
# calculate start time and end time
self.stTime = LBDateTime(int(stMonth), int(stDay), float(stHour))
self.endTime = LBDateTime(int(endMonth), int(endDay), float(endHour))
if self.stTime.hour <= self.endTime.hour:
self.overnight = False # each segments of hours will be in a single day
else:
self.overnight = True
# A reversed analysis period defines a period that starting month is after ending month
# (e.g DEC to JUN)
if self.stTime.HOY > self.endTime.HOY:
self.reversed = True
else:
self.reversed = False
# check time step
if timestep not in self.__validTimesteps:
raise ValueError("Invalid timestep. Valid values are %s"%str(self.__validTimesteps))
# calculate time stamp
self.timestep = timestep
self.minuteIntervals = self.__validTimesteps[timestep]
# calculate timestamps and hoursOfYear
self.__timestampsData = {} # A dictionary for timedates. Key values will be minute of year
self.__calculateTimestamps()
@classmethod
def fromAnalysisPeriod(cls, analysisPeriod):
"""Create and Analysis Period from an analysis period
This method is useful to be called from inside Grasshopper or Dynamo
"""
if not analysisPeriod:
print "Analysis period is set to annual"
return AnalysisPeriod()
elif isinstance(analysisPeriod, AnalysisPeriod):
return analysisPeriod
elif isinstance(analysisPeriod, str):
return cls.__fromAnalysisPeriodString(analysisPeriod)
@classmethod
def __fromAnalysisPeriodString(cls, analysisPeriodString):
# %s/%s to %s/%s between %s to %s @%s
ap = analysisPeriodString.lower() \
.replace(' ', '') \
.replace('to', ' ') \
.replace('/', ' ') \
.replace('between', ' ') \
.replace('@', ' ')
try:
stMonth, stDay, \
endMonth, endDay, \
stHour, endHour, timestep = ap.split(' ')
return cls(stMonth, stDay, stHour, endMonth, endDay, endHour, int(timestep))
except:
raise ValueError(analysisPeriodString + " is not a valid analysis period!")
def __calculateTimestamps(self):
"""Return a list of Ladybug DateTime in this analysis period."""
# calculate based on minutes
curr = self.stTime.MOY - 60 + self.minuteIntervals
if not self.reversed:
while curr <= self.endTime.MOY:
time = LBDateTime.fromMOY(curr)
if not self.isTimeIncluded(time):
self.__timestampsData[time.MOY] = time
curr += self.minuteIntervals
else:
while (0 <= curr <= self.endTime.MOY or self.stTime.MOY <= curr < 8760 * 60):
time = LBDateTime.fromMOY(curr)
if not self.isTimeIncluded(time):
self.__timestampsData[time.MOY] = time
curr = (curr + self.minuteIntervals)%(8760 * 60)
@property
def dates(self):
"""A sorted list of dates in this analysis period"""
# sort dictionary based on key values (minute of the year)
sortedTimestamps = sorted(self.__timestampsData.items(), key= lambda x: x[0])
return [timestamp[1] for timestamp in sortedTimestamps]
@property
def HOYs(self):
"""A sorted list of hours of year in this analysis period"""
return [timestamp.HOY for timestamp in self.dates]
@property
def floatHOYs(self):
"""A sorted list of hours of year as float values in this analysis period"""
return [timestamp.floatHOY for timestamp in self.dates]
@property
def totalNumOfHours(self):
"""Total number of hours during this analysis period"""
return len(self.__timestampsData)/self.timestep
@property
def isAnnual(self):
"""Check if an analysis period is annual"""
return True if self.totalNumOfHours == 8760 else False
def isTimeIncluded(self, time):
"""Check if time is included in analysis period.
Return True if time is inside this analysis period,
otherwise return False
Args:
time: A LBDateTime to be tested
Returns:
A boolean. True if time is included in analysis period
"""
# time filtering in Ladybug and honeybee is slightly different since start hour and end hour will be
# applied for every day. For instance 2/20 9am to 2/22 5pm means hour between 9-17 during 20, 21 and 22 of Feb
return time.MOY in self.__timestampsData
def __repr__(self):
return "%s/%s to %s/%s between %s to %s @%d"%\
(self.stTime.month, self.stTime.day, \
self.endTime.month, self.endTime.day, \
self.stTime.hour, self.endTime.hour, \
self.timestep)
Ancestors (in MRO)
Instance variables
var HOYs
A sorted list of hours of year in this analysis period
var dates
A sorted list of dates in this analysis period
var endTime
var floatHOYs
A sorted list of hours of year as float values in this analysis period
var isAnnual
Check if an analysis period is annual
var minuteIntervals
var stTime
var timestep
var totalNumOfHours
Total number of hours during this analysis period
Methods
def __init__(
self, stMonth=1, stDay=1, stHour=1, endMonth=12, endDay=31, endHour=24, timestep=1)
Init an analysis period
def __init__(self, stMonth = 1, stDay = 1, stHour = 1, endMonth = 12,
endDay = 31, endHour = 24, timestep = 1):
"""Init an analysis period"""
# calculate start time and end time
self.stTime = LBDateTime(int(stMonth), int(stDay), float(stHour))
self.endTime = LBDateTime(int(endMonth), int(endDay), float(endHour))
if self.stTime.hour <= self.endTime.hour:
self.overnight = False # each segments of hours will be in a single day
else:
self.overnight = True
# A reversed analysis period defines a period that starting month is after ending month
# (e.g DEC to JUN)
if self.stTime.HOY > self.endTime.HOY:
self.reversed = True
else:
self.reversed = False
# check time step
if timestep not in self.__validTimesteps:
raise ValueError("Invalid timestep. Valid values are %s"%str(self.__validTimesteps))
# calculate time stamp
self.timestep = timestep
self.minuteIntervals = self.__validTimesteps[timestep]
# calculate timestamps and hoursOfYear
self.__timestampsData = {} # A dictionary for timedates. Key values will be minute of year
self.__calculateTimestamps()
def fromAnalysisPeriod(
cls, analysisPeriod)
Create and Analysis Period from an analysis period
This method is useful to be called from inside Grasshopper or Dynamo
@classmethod
def fromAnalysisPeriod(cls, analysisPeriod):
"""Create and Analysis Period from an analysis period
This method is useful to be called from inside Grasshopper or Dynamo
"""
if not analysisPeriod:
print "Analysis period is set to annual"
return AnalysisPeriod()
elif isinstance(analysisPeriod, AnalysisPeriod):
return analysisPeriod
elif isinstance(analysisPeriod, str):
return cls.__fromAnalysisPeriodString(analysisPeriod)
def isTimeIncluded(
self, time)
Check if time is included in analysis period.
Return True if time is inside this analysis period, otherwise return False
Args: time: A LBDateTime to be tested
Returns: A boolean. True if time is included in analysis period
def isTimeIncluded(self, time):
"""Check if time is included in analysis period.
Return True if time is inside this analysis period,
otherwise return False
Args:
time: A LBDateTime to be tested
Returns:
A boolean. True if time is included in analysis period
"""
# time filtering in Ladybug and honeybee is slightly different since start hour and end hour will be
# applied for every day. For instance 2/20 9am to 2/22 5pm means hour between 9-17 during 20, 21 and 22 of Feb
return time.MOY in self.__timestampsData
class DataList
Ladybug data list
A list of ladybug data with a LBHeader
class DataList:
"""Ladybug data list
A list of ladybug data with a LBHeader
"""
def __init__(self, data = None, header = None):
self.__data = self.checkInputData(data)
self.header = LBHeader() if not header else header
def values(self, header = False):
"""Return the list of values
Args:
header: A boolean that indicates if values should include the headers
Return:
A list of values
"""
if not header:
return self.__data
else:
return self.header.toList() + self.__data
@property
def timeStamps(self):
"List of time stamps for current data"
return [value.datetime for value in self.__data]
def checkInputData(self, data):
"""Check input data"""
if not data: return []
return [LBData.fromLBData(d) for d in data]
def append(self, data):
"""Append LBData to current list"""
self.extend([data])
def extend(self, dataList):
"""Extend a list of LBData to the end of current list"""
self.__data.extend(self.checkInputData(dataList))
def duplicate(self):
"""Duplicate current data list"""
return copy.deepcopy(self)
@staticmethod
def average(data):
values = [value.value for value in data]
return sum(values)/len(data)
def groupDataByMonth(self, monthRange = range(1,13), userDataList = None):
"""Return a dictionary of values where values are grouped for each month
key values are between 1-12
Parameters:
monthRange: A list of numbers for months. Default is 1-12
userDataList: An optional data list of LBData to be processed
Usage:
epwfile = EPW("epw file address")
monthlyValues = epwfile.dryBulbTemperature.groupValuesByMonth()
print monthlyValues[2] # returns values for the month of March
"""
hourlyDataByMonth = {}
if userDataList:
data = [LBData.fromLBData(d) for d in userDataList]
else:
data = self.__data
for d in data:
if not d.datetime.month in monthRange: continue
if not hourlyDataByMonth.has_key(d.datetime.month): hourlyDataByMonth[d.datetime.month] = [] #create an empty list for month
hourlyDataByMonth[d.datetime.month].append(d)
print "Found data for months " + str(hourlyDataByMonth.keys())
return hourlyDataByMonth
def groupDataByDay(self, dayRange = range(1, 366), userDataList = None):
"""Return a dictionary of values where values are grouped by each day of year
key values are between 1-365
Parameters:
dayRange: A list of numbers for days. Default is 1-365
userDataList: An optional data list of LBData to be processed
Usage:
epwfile = EPW("epw file address")
dailyValues = epwfile.dryBulbTemperature.groupDataByDay(range(1, 30))
print dailyValues[2] # returns values for the second day of year
"""
hourlyDataByDay = {}
if userDataList:
data = [LBData.fromLBData(d) for d in userDataList]
else:
data = self.__data
for d in data:
DOY = DateTimeLib.getDayOfYear(d.datetime.month, d.datetime.day)
if not DOY in dayRange: continue
if not hourlyDataByDay.has_key(DOY): hourlyDataByDay[DOY] = [] #create an empty list for month
hourlyDataByDay[DOY].append(d)
print "Found data for " + str(len(hourlyDataByDay.keys())) + " days."
return hourlyDataByDay
def groupDataByHour(self, hourRange = range(1, 25), userDataList = None):
"""Return a dictionary of values where values are grouped by each hour of day
key values are between 1-24
Parameters:
hourRange: A list of numbers for hours. Default is 1-24
userDataList: An optional data list of LBData to be processed
Usage:
epwfile = EPW("epw file address")
monthlyValues = epwfile.dryBulbTemperature.groupDataByMonth([1])
groupedHourlyData = epwfile.dryBulbTemperature.groupDataByHour(userDataList = monthlyValues[2])
for hour, data in groupedHourlyData.items():
print "average temperature values for hour " + str(hour) + " during JAN is " + str(core.DataList.average(data)) + " " + DBT.header.unit
"""
hourlyDataByHour = {}
if userDataList:
data = [LBData.fromLBData(d) for d in userDataList]
else:
data = self.__data
for d in data:
if not d.datetime.hour in hourRange: continue
if not hourlyDataByHour.has_key(d.datetime.hour): hourlyDataByHour[d.datetime.hour] = [] #create an empty list for month
hourlyDataByHour[d.datetime.hour].append(d)
print "Found data for hours " + str(hourlyDataByHour.keys())
return hourlyDataByHour
# TODO: Add validity check for input values
def updateDataForAnAnalysisPeriod(self, values, analysisPeriod = None):
"""Replace current values in data list with new set of values
for a specific analysis period.
Length of values should be equal to number of hours in analysis period
Parameters:
values: A list of values to be replaced in the file
analysisPeriod: An analysis period for input the input values.
Default is set to the whole year.
"""
if not analysisPeriod:
analysisPeriod = AnalysisPeriod()
# check length of data vs length of analysis period
if len(values) != analysisPeriod.totalNumOfHours:
raise ValueError("Length of values %d is not equal to " + \
"number of hours in analysis period %d"%(len(values), \
analysisPeriod.totalNumOfHours))
# get all time stamps
timeStamps = analysisPeriod.dates
# map timeStamps and values
newValues = {}
for count, value in enumerate(values):
HOY = timeStamps[count].HOY
newValues[HOY] = value
# update values
updatedCount = 0
for counter, data in enumerate(self.__data):
try:
value = newValues[data.datetime.HOY]
data.updateValue(value)
updatedCount+=1
except KeyError:
pass
# return self for chaining methods
print "%s data are updated for %d hours."%(self.header.dataType, updatedCount)
# return self for chaining methods
return self
def updateDataForHoursOfYear(self, values, hoursOfYear):
"""Replace current values in data list with new set of values
for a list of hours of year
Length of values should be equal to number of hours in hours of year
Parameters:
values: A list of values to be replaced in the file
hoursOfYear: A list of HOY between 1 and 8760
"""
# check length of data vs length of analysis period
if len(values) != len(hoursOfYear):
raise ValueError("Length of values %d is not equal to " + \
"number of hours in analysis period %d"%(len(values), \
len(hoursOfYear)))
# map hours and values
newValues = {}
for count, value in enumerate(values):
HOY = hoursOfYear[count]
newValues[HOY] = value
# update values
updatedCount = 0
for counter, data in enumerate(self.__data):
try:
value = newValues[data.datetime.HOY]
data.updateValue(value)
updatedCount+=1
except KeyError:
pass
print "%s data %s updated for %d hour%s."%(self.header.dataType, \
'are' if len(values)>1 else 'is', updatedCount,\
's' if len(values)>1 else '')
# return self for chaining methods
return self
def updateDataForAnHour(self, value, hourOfYear):
"""Replace current value in data list with a new value
for a specific hour of the year
Parameters:
value: A single value
hoursOfYear: The hour of the year
"""
return self.updateDataForHoursOfYear([value], [hourOfYear])
def filterByAnalysisPeriod(self, analysisPeriod):
"""Filter the list based on an analysis period
Parameters:
analysis period: A Ladybug analysis period
Return:
A new DataList with filtered data
Usage:
analysisPeriod = AnalysisPeriod(2,1,1,3,31,24) #start of Feb to end of Mar
epw = EPW("c:\ladybug\weatherdata.epw")
DBT = epw.dryBulbTemperature
filteredDBT = DBT.filterByAnalysisPeriod(analysisPeriod)
"""
if not analysisPeriod or analysisPeriod.isAnnual:
print "You need a valid analysis period to filter data."
return self
# There is no guarantee that data is continuous so I iterate through the
# each data point one by one
filteredData = [ d for d in self.__data if analysisPeriod.isTimeIncluded(d.datetime)]
# create a new filteredData
filteredHeader = self.header.duplicate()
filteredHeader.analysisPeriod = analysisPeriod
filteredDataList = DataList(filteredData, filteredHeader)
return filteredDataList
def filterByHOYs(self, HOYs):
"""Filter the list based on an analysis period
Parameters:
HOYs: A List of hours of the year [1-8760]
Return:
A new DataList with filtered data
Usage:
HOYs = range(1,48) # The first two days of the year
epw = EPW("c:\ladybug\weatherdata.epw")
DBT = epw.dryBulbTemperature
filteredDBT = DBT.filterByHOYs(HOYs)
"""
# There is no guarantee that data is continuous so I iterate through the
# each data point one by one
filteredData = [ d for d in self.__data if d.datetime.HOY in HOYs]
# create a new filteredData
filteredHeader = self.header.duplicate()
filteredHeader.analysisPeriod = "unknown"
filteredDataList = DataList(filteredData, filteredHeader)
return filteredDataList
def filterByConditionalStatement(self, statement):
"""Filter the list based on an analysis period
Parameters:
statement: A conditional statement as a string (e.g. x>25 and x%5==0).
The variable should always be named as x
Return:
A new DataList with filtered data
Usage:
epw = EPW("c:\ladybug\weatherdata.epw")
DBT = epw.dryBulbTemperature
# filter data for when dry bulb temperature is more then 25
filteredDBT = DBT.filterByConditionalStatement('x > 25')
# get the list of time stamps that meet the conditional statement
print filteredDBT.timeStamps
"""
def checkInputStatement(statement):
stStatement = statement.lower().replace("and", "").replace("or", "")\
.replace("not", "").replace("in", "").replace("is", "")
l = [s for s in stStatement if s.isalpha()]
if list(set(l)) != ['x']:
statementErrorMsg = 'Invalid input statement. Statement should be a valid Python statement' + \
' and the variable should be named as x'
raise ValueError(statementErrorMsg)
checkInputStatement(statement)
statement = statement.replace('x', 'd.value')
filteredData = [d for d in self.__data if eval(statement)]
# create a new filteredData
filteredHeader = self.header.duplicate()
filteredHeader.analysisPeriod = 'N/A'
filteredDataList = DataList(filteredData, filteredHeader)
return filteredDataList
def filterByPattern(self, patternList):
"""Filter the list based on a list of Boolean
Length of Boolean should be equal to length of values in DataList
Parameters:
patternList: A list of True, False values
Return:
A new DataList with filtered data
"""
# check length of data vs length of analysis period
if len(self.values) != len(patternList):
print len(self.values), len(patternList)
errMsg = "Length of values %d is not equal to number of patterns %d" \
%(len(self.values), len(patternList))
raise ValueError(errMsg)
filteredData = [d for count, d in enumerate(self.__data) if patternList[count]]
# create a new filteredData
filteredHeader = self.header.duplicate()
filteredHeader.analysisPeriod = 'N/A'
filteredDataList = DataList(filteredData, filteredHeader)
return filteredDataList
def averageMonthly(self, userDataList = None):
"""Return a dictionary of values for average values for available months"""
# group data for each month
monthlyValues = self.groupDataByMonth(userDataList= userDataList)
averageValues = dict()
# average values for each month
for month, values in monthlyValues.items():
averageValues[month] = self.average(values)
return averageValues
def averageMonthlyForEachHour(self, userDataList = None):
"""Calculate average value for each hour during each month
This method returns a dictionary with nested dictionaries for each hour
"""
# get monthy values
monthlyHourlyValues = self.groupDataByMonth(userDataList= userDataList)
# group data for each hour in each month and collect them in a dictionary
averagedMonthlyValuesPerHour = {}
for month, monthlyValues in monthlyHourlyValues.items():
if month not in averagedMonthlyValuesPerHour: averagedMonthlyValuesPerHour[month] = {}
# group data for each hour
groupedHourlyData = self.groupDataByHour(userDataList = monthlyValues)
for hour, data in groupedHourlyData.items():
averagedMonthlyValuesPerHour[month][hour] = self.average(data)
return averagedMonthlyValuesPerHour
def __len__(self):
return len(self.__data)
def __getitem__(self, key):
return self.__data[key]
def __setitem__(self, key, value):
self.__data[key] = value
def __delitem__(self, key):
del self.__data[key]
def __iter__(self):
return iter(self.__data)
# TODO: Reverse analysis period in header
def __reversed__(self):
return FunctionalList(reversed(self.__data))
def __repr__(self):
return "Ladybug.DataList#%s"%self.header.dataType
Ancestors (in MRO)
Static methods
def average(
data)
@staticmethod
def average(data):
values = [value.value for value in data]
return sum(values)/len(data)
Instance variables
var header
var timeStamps
List of time stamps for current data
Methods
def __init__(
self, data=None, header=None)
def __init__(self, data = None, header = None):
self.__data = self.checkInputData(data)
self.header = LBHeader() if not header else header
def append(
self, data)
Append LBData to current list
def append(self, data):
"""Append LBData to current list"""
self.extend([data])
def averageMonthly(
self, userDataList=None)
Return a dictionary of values for average values for available months
def averageMonthly(self, userDataList = None):
"""Return a dictionary of values for average values for available months"""
# group data for each month
monthlyValues = self.groupDataByMonth(userDataList= userDataList)
averageValues = dict()
# average values for each month
for month, values in monthlyValues.items():
averageValues[month] = self.average(values)
return averageValues
def averageMonthlyForEachHour(
self, userDataList=None)
Calculate average value for each hour during each month
This method returns a dictionary with nested dictionaries for each hour
def averageMonthlyForEachHour(self, userDataList = None):
"""Calculate average value for each hour during each month
This method returns a dictionary with nested dictionaries for each hour
"""
# get monthy values
monthlyHourlyValues = self.groupDataByMonth(userDataList= userDataList)
# group data for each hour in each month and collect them in a dictionary
averagedMonthlyValuesPerHour = {}
for month, monthlyValues in monthlyHourlyValues.items():
if month not in averagedMonthlyValuesPerHour: averagedMonthlyValuesPerHour[month] = {}
# group data for each hour
groupedHourlyData = self.groupDataByHour(userDataList = monthlyValues)
for hour, data in groupedHourlyData.items():
averagedMonthlyValuesPerHour[month][hour] = self.average(data)
return averagedMonthlyValuesPerHour
def checkInputData(
self, data)
Check input data
def checkInputData(self, data):
"""Check input data"""
if not data: return []
return [LBData.fromLBData(d) for d in data]
def duplicate(
self)
Duplicate current data list
def duplicate(self):
"""Duplicate current data list"""
return copy.deepcopy(self)
def extend(
self, dataList)
Extend a list of LBData to the end of current list
def extend(self, dataList):
"""Extend a list of LBData to the end of current list"""
self.__data.extend(self.checkInputData(dataList))
def filterByAnalysisPeriod(
self, analysisPeriod)
Filter the list based on an analysis period Parameters: analysis period: A Ladybug analysis period
Return: A new DataList with filtered data
Usage: analysisPeriod = AnalysisPeriod(2,1,1,3,31,24) #start of Feb to end of Mar epw = EPW("c:\ladybug\weatherdata.epw") DBT = epw.dryBulbTemperature filteredDBT = DBT.filterByAnalysisPeriod(analysisPeriod)
def filterByAnalysisPeriod(self, analysisPeriod):
"""Filter the list based on an analysis period
Parameters:
analysis period: A Ladybug analysis period
Return:
A new DataList with filtered data
Usage:
analysisPeriod = AnalysisPeriod(2,1,1,3,31,24) #start of Feb to end of Mar
epw = EPW("c:\ladybug\weatherdata.epw")
DBT = epw.dryBulbTemperature
filteredDBT = DBT.filterByAnalysisPeriod(analysisPeriod)
"""
if not analysisPeriod or analysisPeriod.isAnnual:
print "You need a valid analysis period to filter data."
return self
# There is no guarantee that data is continuous so I iterate through the
# each data point one by one
filteredData = [ d for d in self.__data if analysisPeriod.isTimeIncluded(d.datetime)]
# create a new filteredData
filteredHeader = self.header.duplicate()
filteredHeader.analysisPeriod = analysisPeriod
filteredDataList = DataList(filteredData, filteredHeader)
return filteredDataList
def filterByConditionalStatement(
self, statement)
Filter the list based on an analysis period Parameters: statement: A conditional statement as a string (e.g. x>25 and x%5==0). The variable should always be named as x
Return: A new DataList with filtered data
Usage: epw = EPW("c:\ladybug\weatherdata.epw") DBT = epw.dryBulbTemperature # filter data for when dry bulb temperature is more then 25 filteredDBT = DBT.filterByConditionalStatement('x > 25') # get the list of time stamps that meet the conditional statement print filteredDBT.timeStamps
def filterByConditionalStatement(self, statement):
"""Filter the list based on an analysis period
Parameters:
statement: A conditional statement as a string (e.g. x>25 and x%5==0).
The variable should always be named as x
Return:
A new DataList with filtered data
Usage:
epw = EPW("c:\ladybug\weatherdata.epw")
DBT = epw.dryBulbTemperature
# filter data for when dry bulb temperature is more then 25
filteredDBT = DBT.filterByConditionalStatement('x > 25')
# get the list of time stamps that meet the conditional statement
print filteredDBT.timeStamps
"""
def checkInputStatement(statement):
stStatement = statement.lower().replace("and", "").replace("or", "")\
.replace("not", "").replace("in", "").replace("is", "")
l = [s for s in stStatement if s.isalpha()]
if list(set(l)) != ['x']:
statementErrorMsg = 'Invalid input statement. Statement should be a valid Python statement' + \
' and the variable should be named as x'
raise ValueError(statementErrorMsg)
checkInputStatement(statement)
statement = statement.replace('x', 'd.value')
filteredData = [d for d in self.__data if eval(statement)]
# create a new filteredData
filteredHeader = self.header.duplicate()
filteredHeader.analysisPeriod = 'N/A'
filteredDataList = DataList(filteredData, filteredHeader)
return filteredDataList
def filterByHOYs(
self, HOYs)
Filter the list based on an analysis period Parameters: HOYs: A List of hours of the year [1-8760]
Return: A new DataList with filtered data
Usage: HOYs = range(1,48) # The first two days of the year epw = EPW("c:\ladybug\weatherdata.epw") DBT = epw.dryBulbTemperature filteredDBT = DBT.filterByHOYs(HOYs)
def filterByHOYs(self, HOYs):
"""Filter the list based on an analysis period
Parameters:
HOYs: A List of hours of the year [1-8760]
Return:
A new DataList with filtered data
Usage:
HOYs = range(1,48) # The first two days of the year
epw = EPW("c:\ladybug\weatherdata.epw")
DBT = epw.dryBulbTemperature
filteredDBT = DBT.filterByHOYs(HOYs)
"""
# There is no guarantee that data is continuous so I iterate through the
# each data point one by one
filteredData = [ d for d in self.__data if d.datetime.HOY in HOYs]
# create a new filteredData
filteredHeader = self.header.duplicate()
filteredHeader.analysisPeriod = "unknown"
filteredDataList = DataList(filteredData, filteredHeader)
return filteredDataList
def filterByPattern(
self, patternList)
Filter the list based on a list of Boolean
Length of Boolean should be equal to length of values in DataList
Parameters: patternList: A list of True, False values
Return: A new DataList with filtered data
def filterByPattern(self, patternList):
"""Filter the list based on a list of Boolean
Length of Boolean should be equal to length of values in DataList
Parameters:
patternList: A list of True, False values
Return:
A new DataList with filtered data
"""
# check length of data vs length of analysis period
if len(self.values) != len(patternList):
print len(self.values), len(patternList)
errMsg = "Length of values %d is not equal to number of patterns %d" \
%(len(self.values), len(patternList))
raise ValueError(errMsg)
filteredData = [d for count, d in enumerate(self.__data) if patternList[count]]
# create a new filteredData
filteredHeader = self.header.duplicate()
filteredHeader.analysisPeriod = 'N/A'
filteredDataList = DataList(filteredData, filteredHeader)
return filteredDataList
def groupDataByDay(
self, dayRange=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, 363, 364, 365], userDataList=None)
Return a dictionary of values where values are grouped by each day of year
key values are between 1-365
Parameters: dayRange: A list of numbers for days. Default is 1-365 userDataList: An optional data list of LBData to be processed Usage: epwfile = EPW("epw file address") dailyValues = epwfile.dryBulbTemperature.groupDataByDay(range(1, 30)) print dailyValues[2] # returns values for the second day of year
def groupDataByDay(self, dayRange = range(1, 366), userDataList = None):
"""Return a dictionary of values where values are grouped by each day of year
key values are between 1-365
Parameters:
dayRange: A list of numbers for days. Default is 1-365
userDataList: An optional data list of LBData to be processed
Usage:
epwfile = EPW("epw file address")
dailyValues = epwfile.dryBulbTemperature.groupDataByDay(range(1, 30))
print dailyValues[2] # returns values for the second day of year
"""
hourlyDataByDay = {}
if userDataList:
data = [LBData.fromLBData(d) for d in userDataList]
else:
data = self.__data
for d in data:
DOY = DateTimeLib.getDayOfYear(d.datetime.month, d.datetime.day)
if not DOY in dayRange: continue
if not hourlyDataByDay.has_key(DOY): hourlyDataByDay[DOY] = [] #create an empty list for month
hourlyDataByDay[DOY].append(d)
print "Found data for " + str(len(hourlyDataByDay.keys())) + " days."
return hourlyDataByDay
def groupDataByHour(
self, hourRange=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24], userDataList=None)
Return a dictionary of values where values are grouped by each hour of day
key values are between 1-24
Parameters: hourRange: A list of numbers for hours. Default is 1-24 userDataList: An optional data list of LBData to be processed
Usage: epwfile = EPW("epw file address") monthlyValues = epwfile.dryBulbTemperature.groupDataByMonth([1]) groupedHourlyData = epwfile.dryBulbTemperature.groupDataByHour(userDataList = monthlyValues[2]) for hour, data in groupedHourlyData.items(): print "average temperature values for hour " + str(hour) + " during JAN is " + str(core.DataList.average(data)) + " " + DBT.header.unit
def groupDataByHour(self, hourRange = range(1, 25), userDataList = None):
"""Return a dictionary of values where values are grouped by each hour of day
key values are between 1-24
Parameters:
hourRange: A list of numbers for hours. Default is 1-24
userDataList: An optional data list of LBData to be processed
Usage:
epwfile = EPW("epw file address")
monthlyValues = epwfile.dryBulbTemperature.groupDataByMonth([1])
groupedHourlyData = epwfile.dryBulbTemperature.groupDataByHour(userDataList = monthlyValues[2])
for hour, data in groupedHourlyData.items():
print "average temperature values for hour " + str(hour) + " during JAN is " + str(core.DataList.average(data)) + " " + DBT.header.unit
"""
hourlyDataByHour = {}
if userDataList:
data = [LBData.fromLBData(d) for d in userDataList]
else:
data = self.__data
for d in data:
if not d.datetime.hour in hourRange: continue
if not hourlyDataByHour.has_key(d.datetime.hour): hourlyDataByHour[d.datetime.hour] = [] #create an empty list for month
hourlyDataByHour[d.datetime.hour].append(d)
print "Found data for hours " + str(hourlyDataByHour.keys())
return hourlyDataByHour
def groupDataByMonth(
self, monthRange=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], userDataList=None)
Return a dictionary of values where values are grouped for each month
key values are between 1-12
Parameters: monthRange: A list of numbers for months. Default is 1-12 userDataList: An optional data list of LBData to be processed
Usage: epwfile = EPW("epw file address") monthlyValues = epwfile.dryBulbTemperature.groupValuesByMonth() print monthlyValues[2] # returns values for the month of March
def groupDataByMonth(self, monthRange = range(1,13), userDataList = None):
"""Return a dictionary of values where values are grouped for each month
key values are between 1-12
Parameters:
monthRange: A list of numbers for months. Default is 1-12
userDataList: An optional data list of LBData to be processed
Usage:
epwfile = EPW("epw file address")
monthlyValues = epwfile.dryBulbTemperature.groupValuesByMonth()
print monthlyValues[2] # returns values for the month of March
"""
hourlyDataByMonth = {}
if userDataList:
data = [LBData.fromLBData(d) for d in userDataList]
else:
data = self.__data
for d in data:
if not d.datetime.month in monthRange: continue
if not hourlyDataByMonth.has_key(d.datetime.month): hourlyDataByMonth[d.datetime.month] = [] #create an empty list for month
hourlyDataByMonth[d.datetime.month].append(d)
print "Found data for months " + str(hourlyDataByMonth.keys())
return hourlyDataByMonth
def updateDataForAnAnalysisPeriod(
self, values, analysisPeriod=None)
Replace current values in data list with new set of values for a specific analysis period.
Length of values should be equal to number of hours in analysis period
Parameters: values: A list of values to be replaced in the file analysisPeriod: An analysis period for input the input values. Default is set to the whole year.
def updateDataForAnAnalysisPeriod(self, values, analysisPeriod = None):
"""Replace current values in data list with new set of values
for a specific analysis period.
Length of values should be equal to number of hours in analysis period
Parameters:
values: A list of values to be replaced in the file
analysisPeriod: An analysis period for input the input values.
Default is set to the whole year.
"""
if not analysisPeriod:
analysisPeriod = AnalysisPeriod()
# check length of data vs length of analysis period
if len(values) != analysisPeriod.totalNumOfHours:
raise ValueError("Length of values %d is not equal to " + \
"number of hours in analysis period %d"%(len(values), \
analysisPeriod.totalNumOfHours))
# get all time stamps
timeStamps = analysisPeriod.dates
# map timeStamps and values
newValues = {}
for count, value in enumerate(values):
HOY = timeStamps[count].HOY
newValues[HOY] = value
# update values
updatedCount = 0
for counter, data in enumerate(self.__data):
try:
value = newValues[data.datetime.HOY]
data.updateValue(value)
updatedCount+=1
except KeyError:
pass
# return self for chaining methods
print "%s data are updated for %d hours."%(self.header.dataType, updatedCount)
# return self for chaining methods
return self
def updateDataForAnHour(
self, value, hourOfYear)
Replace current value in data list with a new value for a specific hour of the year
Parameters: value: A single value hoursOfYear: The hour of the year
def updateDataForAnHour(self, value, hourOfYear):
"""Replace current value in data list with a new value
for a specific hour of the year
Parameters:
value: A single value
hoursOfYear: The hour of the year
"""
return self.updateDataForHoursOfYear([value], [hourOfYear])
def updateDataForHoursOfYear(
self, values, hoursOfYear)
Replace current values in data list with new set of values for a list of hours of year
Length of values should be equal to number of hours in hours of year
Parameters: values: A list of values to be replaced in the file hoursOfYear: A list of HOY between 1 and 8760
def updateDataForHoursOfYear(self, values, hoursOfYear):
"""Replace current values in data list with new set of values
for a list of hours of year
Length of values should be equal to number of hours in hours of year
Parameters:
values: A list of values to be replaced in the file
hoursOfYear: A list of HOY between 1 and 8760
"""
# check length of data vs length of analysis period
if len(values) != len(hoursOfYear):
raise ValueError("Length of values %d is not equal to " + \
"number of hours in analysis period %d"%(len(values), \
len(hoursOfYear)))
# map hours and values
newValues = {}
for count, value in enumerate(values):
HOY = hoursOfYear[count]
newValues[HOY] = value
# update values
updatedCount = 0
for counter, data in enumerate(self.__data):
try:
value = newValues[data.datetime.HOY]
data.updateValue(value)
updatedCount+=1
except KeyError:
pass
print "%s data %s updated for %d hour%s."%(self.header.dataType, \
'are' if len(values)>1 else 'is', updatedCount,\
's' if len(values)>1 else '')
# return self for chaining methods
return self
def values(
self, header=False)
Return the list of values
Args: header: A boolean that indicates if values should include the headers
Return: A list of values
def values(self, header = False):
"""Return the list of values
Args:
header: A boolean that indicates if values should include the headers
Return:
A list of values
"""
if not header:
return self.__data
else:
return self.header.toList() + self.__data
class DateTimeLib
Ladybug DateTime Libray This class includes useful data and methods for date and time
class DateTimeLib:
"""Ladybug DateTime Libray
This class includes useful data and methods for date and time
"""
monthList = ['JAN', 'FEB', 'MAR', 'APR', 'MAY', 'JUN', 'JUL', 'AUG', 'SEP', 'OCT', 'NOV', 'DEC']
numOfDaysEachMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
numOfDaysUntilMonth = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365]
numOfHoursUntilMonth = [24 * numOfDay for numOfDay in numOfDaysUntilMonth]
@classmethod
def checkDateTime(cls, month, day, hour, minute = None):
"""Checks if time combination is a valid time."""
# check month
if not 1 <= month <= 12:
raise ValueError("month should be between 1-12")
if day < 1 or day > cls.numOfDaysEachMonth[month-1]:
raise ValueError("Number of days for %s should be \
between 1-%d"%(cls.monthList[month-1], cls.numOfDaysEachMonth[month-1]))
if not 0 <= hour <= 24:
raise ValueError("hour should be between 0.0-24.0")
if not minute or minute == 0:
if hour == 0 and day == 1 and month == 1:
# last hour of the year
month = 12
day = 31
hour = 24
elif hour == 0 and day == 1:
# The last hour of the last day of the month before
month -= 1
day = cls.numOfDaysEachMonth[month-1]
hour = 24
elif hour==0:
# the last hour of the day before
hour = 24
day -= 1
hour, minute = cls.__getHourAndMinute(hour, minute)
return month, day, hour, minute
@classmethod
def getHourOfYear(cls, month, day, hour, minute = None):
"""Return hour of the year between 1 and 8760."""
# make sure input values are correct
month, day, hour, minute = cls.checkDateTime(month, day, hour, minute)
# fix the end day
JD = cls.numOfDaysUntilMonth[month-1] + int(day)
return (JD - 1) * 24 + hour
@classmethod
def getDayOfYear(cls, month, day):
"""Retuen day of the year between 1 and 365"""
# make sure input values are correct
month, day, hour, minute = cls.checkDateTime(month, day, hour = 1)
# fix the end day
return cls.numOfDaysUntilMonth[month-1] + int(day)
@classmethod
def getMonthDayHourAndMinute(cls, hourOfYear):
"""Return month, day and hour for an hour of the year"""
if hourOfYear == 8760: return 12, 31, 24, 0
# find month
for monthCount in range(12):
if hourOfYear <= cls.numOfHoursUntilMonth[monthCount + 1]:
month = monthCount + 1
break
# find day and hour
if hourOfYear%24 == 0:
# last hour of the day
day = int((hourOfYear - cls.numOfHoursUntilMonth[month - 1])/24)
hour = 24
minute = 0
else:
day = int((hourOfYear - cls.numOfHoursUntilMonth[month - 1])/24) + 1
hour = int(hourOfYear%24)
minute = cls.__getHourAndMinute(hourOfYear)[1]
return month, day, hour, minute
@staticmethod
def __getHourAndMinute(hour, minute = None):
"""Calculate and return hour and minute
This method is mainly usefule for calculating minutes from float hours
if minute is missing. otherwise it will only check the inputs append
returns the checked values
Args:
hour: A float value between 0.0-24.0
minutes: An integer between 0-59. Default in None
Returns:
hour: An interger between 0-24
minute: An integer between 0-59
"""
if not minute:
minute = (hour - int(hour)) * 60
# cast values to integer
hour = int(hour + int(minute/60))
minute = minute%60
return hour, minute
Ancestors (in MRO)
Class variables
var monthList
var numOfDay
var numOfDaysEachMonth
var numOfDaysUntilMonth
var numOfHoursUntilMonth
Methods
def checkDateTime(
cls, month, day, hour, minute=None)
Checks if time combination is a valid time.
@classmethod
def checkDateTime(cls, month, day, hour, minute = None):
"""Checks if time combination is a valid time."""
# check month
if not 1 <= month <= 12:
raise ValueError("month should be between 1-12")
if day < 1 or day > cls.numOfDaysEachMonth[month-1]:
raise ValueError("Number of days for %s should be \
between 1-%d"%(cls.monthList[month-1], cls.numOfDaysEachMonth[month-1]))
if not 0 <= hour <= 24:
raise ValueError("hour should be between 0.0-24.0")
if not minute or minute == 0:
if hour == 0 and day == 1 and month == 1:
# last hour of the year
month = 12
day = 31
hour = 24
elif hour == 0 and day == 1:
# The last hour of the last day of the month before
month -= 1
day = cls.numOfDaysEachMonth[month-1]
hour = 24
elif hour==0:
# the last hour of the day before
hour = 24
day -= 1
hour, minute = cls.__getHourAndMinute(hour, minute)
return month, day, hour, minute
def getDayOfYear(
cls, month, day)
Retuen day of the year between 1 and 365
@classmethod
def getDayOfYear(cls, month, day):
"""Retuen day of the year between 1 and 365"""
# make sure input values are correct
month, day, hour, minute = cls.checkDateTime(month, day, hour = 1)
# fix the end day
return cls.numOfDaysUntilMonth[month-1] + int(day)
def getHourOfYear(
cls, month, day, hour, minute=None)
Return hour of the year between 1 and 8760.
@classmethod
def getHourOfYear(cls, month, day, hour, minute = None):
"""Return hour of the year between 1 and 8760."""
# make sure input values are correct
month, day, hour, minute = cls.checkDateTime(month, day, hour, minute)
# fix the end day
JD = cls.numOfDaysUntilMonth[month-1] + int(day)
return (JD - 1) * 24 + hour
def getMonthDayHourAndMinute(
cls, hourOfYear)
Return month, day and hour for an hour of the year
@classmethod
def getMonthDayHourAndMinute(cls, hourOfYear):
"""Return month, day and hour for an hour of the year"""
if hourOfYear == 8760: return 12, 31, 24, 0
# find month
for monthCount in range(12):
if hourOfYear <= cls.numOfHoursUntilMonth[monthCount + 1]:
month = monthCount + 1
break
# find day and hour
if hourOfYear%24 == 0:
# last hour of the day
day = int((hourOfYear - cls.numOfHoursUntilMonth[month - 1])/24)
hour = 24
minute = 0
else:
day = int((hourOfYear - cls.numOfHoursUntilMonth[month - 1])/24) + 1
hour = int(hourOfYear%24)
minute = cls.__getHourAndMinute(hourOfYear)[1]
return month, day, hour, minute
class LBData
Ladybug data point
class LBData:
"""Ladybug data point"""
# TODO: Change value to be an object from it's data type
# Check datatype.py for available datatypes
def __init__(self, value, dateTime):
self.datetime = dateTime
self.value = value
@classmethod
def fromLBData(cls, data):
assert isinstance(data, LBData), "Input is not a LBData."
return data
def updateValue(self, newValue):
self.value = newValue
def __int__(self):
return int(self.value)
def __float__(self):
return float(self.value)
def __str__(self):
return str(self.value)
def __eq__(self, other):
return self.value == float(other)
def __ne__(self, other):
return self.value != float(other)
def __lt__(self, other):
return self.value < other
def __gt__(self, other):
return self.value > other
def __le__(self, other):
return self.value <= other
def __ge__(self, other):
return self.value >= other
def __add__(self, other):
return self.value + other
def __sub__(self, other):
return self.value - other
def __mul__(self, other):
return self.value * other
def __floordiv__(self, other):
return self.value // other
def __div__(self, other):
return self.value / other
def __mod__(self, other):
return self.value%other
def __pow__(self, other):
return self.value**other
def __radd__(self, other):
return self.__add__(other)
def __rsub__(self, other):
return other - self.value
def __rmul__(self, other):
return self.__mul__(other)
def __rfloordiv__(self, other):
return other//self.value
def __rdiv__(self, other):
return other/self.value
def __rmod__(self, other):
return other%self.value
def __rpow__(self, other):
return other**self.value
def __repr__(self):
return self.__str__()
Ancestors (in MRO)
Instance variables
var datetime
var value
Methods
def __init__(
self, value, dateTime)
def __init__(self, value, dateTime):
self.datetime = dateTime
self.value = value
def fromLBData(
cls, data)
@classmethod
def fromLBData(cls, data):
assert isinstance(data, LBData), "Input is not a LBData."
return data
def updateValue(
self, newValue)
def updateValue(self, newValue):
self.value = newValue
class LBDateTime
Ladybug Date time
class LBDateTime:
"""Ladybug Date time"""
def __init__(self, month = 1, day = 1, hour = 1, minute = None):
self.month, self.day, self.hour, \
self.minute = DateTimeLib.checkDateTime(month, day, hour, minute)
self.floatHour = self.hour + self.minute/60.0
self.DOY = DateTimeLib.getDayOfYear(self.month, self.day)
self.HOY = DateTimeLib.getHourOfYear(self.month, self.day, self.hour, self.minute)
self.MOY = self.HOY * 60 + self.minute # minute of the year
self.floatHOY = self.HOY + self.minute/60.0
@classmethod
def fromHOY(cls, HOY):
"""Create Ladybug Datetime from an hour of the year
Args:
HOY: A float value between 0.0 to 8760.0
"""
month, day, hour, minute = DateTimeLib.getMonthDayHourAndMinute(HOY)
return cls(month, day, hour, minute)
@classmethod
def fromMOY(cls, MOY):
"""create Ladybug DateTime from Minute of the year"""
HOY = MOY/60.0
return cls.fromHOY(HOY)
@classmethod
def fromDateTimeString(cls, datetimeString):
day, month, hour, minute = datetimeString \
.replace(" at ", " ") \
.replace(":", " ") \
.split(" ")
month = DateTimeLib.monthList.index(month.upper()) + 1
return cls(month, int(day), int(hour), int(minute))
@property
def humanReadableHour(self):
"""Return hours and minutes in a human readable way"""
minute = str(self.minute)
if len(minute) == 1: minute = "0" + minute
return "%d:%s"%(self.hour, minute)
def __repr__(self):
return "%d %s at %s"%( self.day, DateTimeLib.monthList[self.month-1], self.humanReadableHour)
Ancestors (in MRO)
Instance variables
var DOY
var HOY
var MOY
var floatHOY
var floatHour
var humanReadableHour
Return hours and minutes in a human readable way
Methods
def __init__(
self, month=1, day=1, hour=1, minute=None)
def __init__(self, month = 1, day = 1, hour = 1, minute = None):
self.month, self.day, self.hour, \
self.minute = DateTimeLib.checkDateTime(month, day, hour, minute)
self.floatHour = self.hour + self.minute/60.0
self.DOY = DateTimeLib.getDayOfYear(self.month, self.day)
self.HOY = DateTimeLib.getHourOfYear(self.month, self.day, self.hour, self.minute)
self.MOY = self.HOY * 60 + self.minute # minute of the year
self.floatHOY = self.HOY + self.minute/60.0
def fromDateTimeString(
cls, datetimeString)
@classmethod
def fromDateTimeString(cls, datetimeString):
day, month, hour, minute = datetimeString \
.replace(" at ", " ") \
.replace(":", " ") \
.split(" ")
month = DateTimeLib.monthList.index(month.upper()) + 1
return cls(month, int(day), int(hour), int(minute))
def fromHOY(
cls, HOY)
Create Ladybug Datetime from an hour of the year
Args: HOY: A float value between 0.0 to 8760.0
@classmethod
def fromHOY(cls, HOY):
"""Create Ladybug Datetime from an hour of the year
Args:
HOY: A float value between 0.0 to 8760.0
"""
month, day, hour, minute = DateTimeLib.getMonthDayHourAndMinute(HOY)
return cls(month, day, hour, minute)
def fromMOY(
cls, MOY)
create Ladybug DateTime from Minute of the year
@classmethod
def fromMOY(cls, MOY):
"""create Ladybug DateTime from Minute of the year"""
HOY = MOY/60.0
return cls.fromHOY(HOY)
class LBHeader
Standard Ladybug header for lists.
The header carries data for city, data type, unit, and analysis period
Attributes: city: A string for the city name dataType: A valid Ladybug data type. Try DataType.dataTypes to see list of data types unit: dataType unit. If empty string it will be set based on dataType timestep: Data timestep "Hourly", "Daily", "Monthly", "Annual", "N/A" analysisPeriod: A Ladybug analysis period. (defualt: 1 Jan 1 to 31 Dec 24)
class LBHeader:
"""Standard Ladybug header for lists.
The header carries data for city,
data type, unit, and analysis period
Attributes:
city: A string for the city name
dataType: A valid Ladybug data type. Try DataType.dataTypes to see list of data types
unit: dataType unit. If empty string it will be set based on dataType
timestep: Data timestep "Hourly", "Daily", "Monthly", "Annual", "N/A"
analysisPeriod: A Ladybug analysis period. (defualt: 1 Jan 1 to 31 Dec 24)
"""
def __init__(self, city = 'unknown', dataType = 'unknown', unit = 'unknown', frequency = 'unknown', analysisPeriod = None):
"""Initiate Ladybug header for lists."""
self.city = city
self.dataType = dataType
self.unit = unit
self.frequency = frequency
self.analysisPeriod = 'unknown' if not analysisPeriod \
else AnalysisPeriod.fromAnalysisPeriod(analysisPeriod)
def duplicate(self):
return copy.deepcopy(self)
@property
def __key(self):
return 'location|dataType|units|frequency|dataPeriod'
def toList(self):
"""Return Ladybug header as a list"""
return [
self.__key,
self.city,
self.dataType,
self.unit,
self.frequency,
self.analysisPeriod
]
def __repr__(self):
return "%s for %s during %s"%(self.dataType, self.city, self.analysisPeriod)
Ancestors (in MRO)
Instance variables
var analysisPeriod
var city
var dataType
var frequency
var unit
Methods
def __init__(
self, city='unknown', dataType='unknown', unit='unknown', frequency='unknown', analysisPeriod=None)
Initiate Ladybug header for lists.
def __init__(self, city = 'unknown', dataType = 'unknown', unit = 'unknown', frequency = 'unknown', analysisPeriod = None):
"""Initiate Ladybug header for lists."""
self.city = city
self.dataType = dataType
self.unit = unit
self.frequency = frequency
self.analysisPeriod = 'unknown' if not analysisPeriod \
else AnalysisPeriod.fromAnalysisPeriod(analysisPeriod)
def duplicate(
self)
def duplicate(self):
return copy.deepcopy(self)
def toList(
self)
Return Ladybug header as a list
def toList(self):
"""Return Ladybug header as a list"""
return [
self.__key,
self.city,
self.dataType,
self.unit,
self.frequency,
self.analysisPeriod
]
class LBPatchData
Ladybug sky patch data type
class LBPatchData(LBData):
"""Ladybug sky patch data type"""
def __init__(self, value, vector):
# sky data doesn't have time
datetime = LBDateTime()
LBData.__init__(self, value, datetime)
self.vector = euclid.Vector3(*vector)
Ancestors (in MRO)
Instance variables
var vector
Methods
def __init__(
self, value, vector)
def __init__(self, value, vector):
# sky data doesn't have time
datetime = LBDateTime()
LBData.__init__(self, value, datetime)
self.vector = euclid.Vector3(*vector)
def fromLBData(
cls, data)
Inheritance:
LBData
.fromLBData
@classmethod
def fromLBData(cls, data):
assert isinstance(data, LBData), "Input is not a LBData."
return data
def updateValue(
self, newValue)
Inheritance:
LBData
.updateValue
def updateValue(self, newValue):
self.value = newValue
class Location
class Location:
def __init__(self, city = '', country = '', latitude = '0.00', \
longitude = '0.00', timeZone = '0.00', elevation ='0.00', \
source = '', stationId = ''):
self.city = str(city)
self.country = str(country)
self.latitude = float(latitude)
self.longitude = float(longitude)
self.timeZone = float(timeZone)
self.elevation = float(elevation)
self.source = str(source)
self.stationId = str(stationId)
def createFromEPString(self, EPString):
"""Create a Ladybug location from an EnergyPlus location string
Parameters:
EPString: Standard EP location string
Usage:
l = Location() #initiate location
l.createFromEPString(EPString)
print "LAT:%s, LON:%s"%(l.latitude, l.longitude)
"""
try:
self.city, self.latitude, \
self.longitude, self.timeZone, \
self.elevation = re.findall(r'\r*\n*([a-zA-Z0-9.:_-]*)[,|;]', \
EPString, re.DOTALL)[1:]
self.latitude = float(self.latitude)
self.longitude = float(self.longitude)
self.timeZone = float(self.timeZone)
self.elevation = float(self.elevation)
except Exception, e:
raise Exception ("Failed to import EP string! %s"%str(e))
def duplicate(self):
return copy.deepcopy(self)
@property
def EPStyleLocationString(self):
"""Return EnergyPlus's location string"""
return "Site:Location,\n" + \
self.city + ',\n' + \
str(self.latitude) +', !Latitude\n' + \
str(self.longitude) +', !Longitude\n' + \
str(self.timeZone) +', !Time Zone\n' + \
str(self.elevation) + '; !Elevation'
def __repr__(self):
return "%s"%(self.EPStyleLocationString)
Ancestors (in MRO)
Instance variables
var EPStyleLocationString
Return EnergyPlus's location string
var city
var country
var elevation
var latitude
var longitude
var source
var stationId
var timeZone
Methods
def __init__(
self, city='', country='', latitude='0.00', longitude='0.00', timeZone='0.00', elevation='0.00', source='', stationId='')
def __init__(self, city = '', country = '', latitude = '0.00', \
longitude = '0.00', timeZone = '0.00', elevation ='0.00', \
source = '', stationId = ''):
self.city = str(city)
self.country = str(country)
self.latitude = float(latitude)
self.longitude = float(longitude)
self.timeZone = float(timeZone)
self.elevation = float(elevation)
self.source = str(source)
self.stationId = str(stationId)
def createFromEPString(
self, EPString)
Create a Ladybug location from an EnergyPlus location string Parameters: EPString: Standard EP location string
Usage: l = Location() #initiate location l.createFromEPString(EPString) print "LAT:%s, LON:%s"%(l.latitude, l.longitude)
def createFromEPString(self, EPString):
"""Create a Ladybug location from an EnergyPlus location string
Parameters:
EPString: Standard EP location string
Usage:
l = Location() #initiate location
l.createFromEPString(EPString)
print "LAT:%s, LON:%s"%(l.latitude, l.longitude)
"""
try:
self.city, self.latitude, \
self.longitude, self.timeZone, \
self.elevation = re.findall(r'\r*\n*([a-zA-Z0-9.:_-]*)[,|;]', \
EPString, re.DOTALL)[1:]
self.latitude = float(self.latitude)
self.longitude = float(self.longitude)
self.timeZone = float(self.timeZone)
self.elevation = float(self.elevation)
except Exception, e:
raise Exception ("Failed to import EP string! %s"%str(e))
def duplicate(
self)
def duplicate(self):
return copy.deepcopy(self)