ladybugdynamo.ladybug.sky module
import epw
import core
import skyvector
import os
import json
import subprocess
import sys
class CumulativeSkyMtx(object):
"""Cumulative Sky
Attributes:
epwFileAddress: Path to EPW file.
skyDensity: Density of the sky. 0 generates a Tregenza sky, which will
divide up the sky dome with a coarse density of 145 sky patches.
Set to 1 to generate a Reinhart sky, which will divide up the sky dome
using a density of 580 sky patches.
workingDir: A local directory to run the study and write the results
Usage:
epwfile = r"C:\EnergyPlusV8-3-0\WeatherData\USA_CO_Golden-NREL.724666_TMY3.epw"
cumSky = CumulativeSkyMtx(epwfile) #calculate the sky
# annual results
cumSky.annualResults()
# results for an anlysis period
ap = AnalysisPeriod(startMonth = 2, endMonth = 12)
results = cumSky.filterByAnalysisPeriod(ap)
print results.diffuseValues
"""
def __init__(self, epwFileAddress, skyDensity = 0, workingDir = None):
self.__epw = epw.EPW(epwFileAddress)
self.__data = {"diffuse": {}, "direct": {}}
self.__results = {}
self.skyDensity = skyDensity
self.__isCalculated = False
self.__isLoaded = False
self.workingDir = workingDir
@property
def skyDensity(self):
"Return sky density. 0: Tregenza, 1: Reinhart"
return self.__skyDensity
@skyDensity.setter
def skyDensity(self, density):
assert int(density) <= 1, "Sky density is should be 0: Tregenza sky, 1: Reinhart sky"
self.__skyDensity = int(density)
#prepare place holder for patches
for patch in range(self.numberOfPatches):
self.__data['diffuse'] = {}
self.__data['direct'] = {}
@property
def workingDir(self):
"A local directory to run the study and write the results"
return self.__workingDir
@workingDir.setter
def workingDir(self, workingDir):
# update addresses
if not hasattr(self, "__workingDir"):
# user has initiated the class
self.__workingDir = workingDir
if not self.__workingDir:
self.__workingDir = self.__epw.filePath
# add name of city to path
if not self.__workingDir.endswith(self.__epw.location.city):
self.__workingDir = os.path.join(self.__workingDir, self.__epw.location.city.replace(" ", "_"))
# create the folder is it's not created
if not os.path.isdir(self.__workingDir):
os.mkdir(self.__workingDir)
# update path for other files if it's a new workingDir
# naming convention is weatherFileName_[diffuse/direct]_[skyDensity].mtx
__name = self.__epw.fileName[:-4] + "_%s_%d.mtx"
self.__diffuseMtxFileAddress = os.path.join(self.__workingDir, __name%("dif", self.__skyDensity))
self.__directMtxFileAddress = os.path.join(self.__workingDir, __name%("dir", self.__skyDensity))
self.__jsonFileAddress= os.path.join(self.__workingDir, self.__epw.fileName[:-4] + \
"_" + str(self.__skyDensity) + ".json")
@property
def numberOfPatches(self):
return self.__patchData("numberOfPatches")
@property
def skyDiffuseRadiation(self):
"""Diffuse values for sky patches as a LBDataList"""
assert self.__isCalculated, "You need to calculate the materix first before" + \
" loading the results. Use calculateMtx method.\nIf you see this error from inside " + \
"Dynamo reconnect one of the inputs and re-run the file!\nFiles are created under %s"%self.workingDir
assert self.__isLoaded, "The values are not loaded. Use skyMtx method."
return self.__results["diffuse"]
@property
def skyDirectRadiation(self):
"""Direct values for sky patches as a LBDataList"""
assert self.__isCalculated, "You need to calculate the materix first before" + \
" loading the results. Use calculateMtx method.\nIf you see this error from inside " + \
"Dynamo reconnect one of the inputs and re-run the file!\nFiles are created under %s"%self.workingDir
assert self.__isLoaded, "The values are not loaded. Use skyMtx method."
return self.__results["direct"]
@property
def skyTotalRadiation(self):
"""Total values for sky patches as a LBDataList"""
assert self.__isCalculated, "You need to calculate the materix first before" + \
" loading the results. Use calculateMtx method.\nIf you see this error from inside " + \
"Dynamo reconnect one of the inputs and re-run the file!\nFiles are created under %s"%self.workingDir
assert self.__isLoaded, "The values are not loaded. Use skyMtx method."
return self.__results["total"]
def steradianConversionFactor(self, patchNumber):
"Steradian Conversion Factor"
rowNumber = self.__calculateRowNumber(patchNumber)
strConv = self.__patchData("steradianConversionFactor")[rowNumber]
return strConv
def __patchData(self, key):
"""
Return data for sky patches based on key
Args:
key: valid keys are numberOfPatches, numberOfPatchesInEachRow and steradianConversionFactor.
Return:
Data for this sky based on the sky density. Depending on the key it can be a number or a list of numbers
Usage:
self.__patchesData("numberOfPatches")
>> 146
"""
# first row is horizon and last row is values for the zenith
# first patch is the ground. I put 0 on conversion
__data = {
"numberOfPatches": {0 : 146, 1 : 578},
"numOfPatchesInEachRow": {0: [1, 30, 30, 24, 24, 18, 12, 6, 1], \
1: [1, 60, 60, 60, 60, 48, 48, 48, 48, 36, 36, 24, 24, 12, 12, 1]},
"steradianConversionFactor": {0 : [0, 0.0435449227, 0.0416418006, 0.0473984151, \
0.0406730411, 0.0428934136, 0.0445221864, 0.0455168385, 0.0344199465],
1: [0, 0.0113221971, 0.0111894547, 0.0109255262, 0.0105335058, 0.0125224872, \
0.0117312774, 0.0108025291, 0.00974713106, 0.011436609, 0.00974295956, \
0.0119026242, 0.00905126163, 0.0121875626, 0.00612971396, 0.00921483254]}
}
assert key in __data, "Invalid key: %s"%key
return __data[key][self.__skyDensity]
def __calculateRowNumber(self, patchNumber):
"""Calculate number of row for sky patch"""
if patchNumber ==0 : return 0
__numOfPatchesInEachRow = self.__patchData(key = "numOfPatchesInEachRow")
for rowCount, patchCountInRow in enumerate(__numOfPatchesInEachRow):
if patchNumber < sum(__numOfPatchesInEachRow[:rowCount+1]):
return rowCount
def epw2wea(self, filePath = None):
if not filePath:
filePath = os.path.join(self.__workingDir, self.__epw.fileName[:-4] + ".wea")
self.__weaFileAddress = self.__epw.epw2wea(filePath)
def calculateMtx(self, pathToRadianceBinaries = r"c:\radiance\bin", recalculate = False):
"""use Radiance gendaymtx to generate the sky
Args:
pathToRadianceBinaries: Path to Radiance libraries. Default is C:\radiance\bin.
recalculate: Set to True if you want the sky to be recalculated even it has been calculated already
"""
#check if the result is already calculated
if not recalculate:
if os.path.isfile(self.__diffuseMtxFileAddress) \
and os.path.isfile(self.__directMtxFileAddress):
self.__isCalculated = True
return
if not pathToRadianceBinaries: pathToRadianceBinaries = r"c:\radiance\bin"
assert os.path.isfile(os.path.join(pathToRadianceBinaries, "gendaymtx.exe")) \
and os.path.isfile(os.path.join(pathToRadianceBinaries, "rcollate.exe")), \
"Can't find gendaymtx.exe or rcollate.exe in radiance binary folder."
# make sure daymtx and rcollate can be executed
assert os.access(os.path.join(pathToRadianceBinaries, "gendaymtx.exe"), os.X_OK), \
"%s is blocked by system! Right click on the file,"%os.path.join(pathToRadianceBinaries, "gendaymtx.exe") + \
" select properties and unblock it."
assert os.access(os.path.join(pathToRadianceBinaries, "rcollate.exe"), os.X_OK), \
"%s is blocked by system! Right click on the file,"%os.path.join(pathToRadianceBinaries, "rcollate.exe") + \
" select properties and unblock it."
# assure wea file is calculated
if not hasattr(self, "__weaFileAddress"): self.epw2wea()
__name = self.__epw.fileName[:-4] + "_calculate_sky_mtx.bat"
batchFileAddress = os.path.join(self.__workingDir, __name)
batchFile = """
@echo off
echo.
echo HELLO #username! DO NOT CLOSE THIS WINDOW.
echo.
echo IT WILL BE CLOSED AUTOMATICALLY WHEN THE CALCULATION IS OVER!
echo.
echo AND MAY TAKE FEW MINUTES...
echo.
echo CALCULATING DIFFUSE COMPONENT OF THE SKY...
#pathToRadianceBinaries\\gendaymtx -m #skyDensity -s -O1 #weaFileAddress | #pathToRadianceBinaries\\rcollate -t > #diffuseMtxFileAddress
echo.
echo CALCULATING DIRECT COMPONENT OF THE SKY...
#pathToRadianceBinaries\\gendaymtx -m #skyDensity -d -O1 #weaFileAddress | #pathToRadianceBinaries\\rcollate -t > #directMtxFileAddress
""".replace("#pathToRadianceBinaries", pathToRadianceBinaries) \
.replace("#skyDensity", str(self.skyDensity + 1)) \
.replace("#weaFileAddress", self.__weaFileAddress) \
.replace("#diffuseMtxFileAddress", self.__diffuseMtxFileAddress) \
.replace("#directMtxFileAddress", self.__directMtxFileAddress)
# write batch file
with open(batchFileAddress, "w") as genskymtxbatfile:
genskymtxbatfile.write(batchFile)
subprocess.Popen(batchFileAddress, shell = True)
def __calculateLuminanceFromRGB(self, R, G, B, patchNumber):
return (.265074126 * float(R) + .670114631 * float(G) + .064811243 * float(B)) * self.steradianConversionFactor(patchNumber)
def __loadMtxFiles(self):
"""load the values from .mtx files. use self.skyMtx to get the results
"""
if self.__isLoaded:
print "Matrix has been already loaded!"
return
assert self.__isCalculated, "You need to calculate the materix first before" + \
" loading the results. Use calculateMtx method. If you see this error from inside " + \
"Dynamo reconnect one of the inputs and re-run the file!"
assert os.path.getsize(self.__diffuseMtxFileAddress) > 0 and \
os.path.getsize(self.__directMtxFileAddress) > 0, \
"Size of matrix files is 0. Try to recalculate cumulative sky matrix."
try:
# open files and read the lines
diffSkyFile = open(self.__diffuseMtxFileAddress, "rb")
dirSkyFile = open(self.__directMtxFileAddress, "rb")
# pass header
for i in range(9):
diffSkyFile.readline()
dirSkyFile.readline()
# import hourly data
analysisPeriod = core.AnalysisPeriod()
for patchNumber in range(self.numberOfPatches):
# create header for each patch
difHeader = core.LBHeader(city = self.__epw.location.city, frequency ='Hourly', \
analysisPeriod = analysisPeriod, \
dataType = "Patch #%d diffuse radiation"%patchNumber, unit = "Wh")
dirHeader = core.LBHeader(city = self.__epw.location.city, frequency ='Hourly', \
analysisPeriod = analysisPeriod, \
dataType = "Patch #%d direct radiation"%patchNumber, unit = "Wh")
# create an empty data list with the header
self.__data['diffuse'][patchNumber] = core.DataList(header =difHeader)
self.__data['direct'][patchNumber] = core.DataList(header =dirHeader)
for HOY in range(8760):
diffLine = diffSkyFile.readline()
dirLine = dirSkyFile.readline()
timestamp = core.LBDateTime.fromHOY(HOY + 1)
for patchNumber, (diffData, dirData) in enumerate(zip(diffLine.split("\t"), dirLine.split("\t"))):
_difR, _difG, _difB = diffData.split(" ")
_dirR, _dirG, _dirB = dirData.split(" ")
_difValue = self.__calculateLuminanceFromRGB(_difR, _difG, _difB, patchNumber)
_dirValue = self.__calculateLuminanceFromRGB(_dirR, _dirG, _dirB, patchNumber)
self.__data["diffuse"][patchNumber].append(core.LBData(_difValue, timestamp))
self.__data["direct"][patchNumber].append(core.LBData(_dirValue, timestamp))
self.__isLoaded = True
except Exception, e:
print e
finally:
diffSkyFile.close()
dirSkyFile.close()
del(diffLine)
del(dirLine)
# TODO: Analysis perios in headers should be adjusted based on the input
def gendaymtx(self, pathToRadianceBinaries = None, diffuse = True, direct = True, \
recalculate = False, analysisPeriod = None):
"""Get sky matrix for direct, diffuse and total radiation as three separate lists
Args:
pathToRadianceBinaries: Path to Radiance libraries. Default is C:\radiance\bin.
diffuse: Set to True to include diffuse radiation
direct: Set to True to iclude direct radiation
recalculate: Set to True if you want the sky to be recalculated even it has been calculated already
analysisPeriod: An analysis period or a list of integers between 1-8760 for hours of the year. Default is All the hours of the year
"""
# calculate sky if it's not already calculated
if not self.__isCalculated or recalculate:
self.calculateMtx(pathToRadianceBinaries = pathToRadianceBinaries, recalculate = recalculate)
# load matrix files if it's not loaded
if not self.__isLoaded:
self.__loadMtxFiles()
if not analysisPeriod:
HOYs = range(1, 8761)
else:
if isinstance(analysisPeriod, list):
HOYs = [int(h) if 0 < h < 8761 else -1 for h in analysisPeriod]
assert (not -1 in HOYs), "Hour should be between 1-8760"
elif isinstance(analysisPeriod, core.AnalysisPeriod):
HOYs = analysisPeriod.HOYs
else:
raise ValueError("Analysis period should be a list of integers or an analysis period.")
# calculate values and return them as 3 lists
# put 0 value for all the patches
__cumulativeRaditionValues = {"diffuse": [0] * self.numberOfPatches, \
"direct": [0] * self.numberOfPatches}
for patchNumber in range(self.numberOfPatches):
for HOY in HOYs:
__cumulativeRaditionValues["diffuse"][patchNumber] += self.__data["diffuse"][patchNumber][HOY - 1]
__cumulativeRaditionValues["direct"][patchNumber] += self.__data["direct"][patchNumber][HOY - 1]
# create header for each patch
difHeader = core.LBHeader(city = self.__epw.location.city, frequency ='NA', \
analysisPeriod = None, \
dataType = "Sky Patches' Diffues Radiation", unit = "Wh")
dirHeader = core.LBHeader(city = self.__epw.location.city, frequency ='NA', \
analysisPeriod = None, \
dataType = "Sky Patches' Direct Radiation", unit = "Wh")
totalHeader = core.LBHeader(city = self.__epw.location.city, frequency ='NA', \
analysisPeriod = None, \
dataType = "Sky Patches' Total Radiation", unit = "Wh")
# create an empty data list with the header
__skyVectors = skyvector.Skyvectors(self.__skyDensity)
self.__results = {}
self.__results['diffuse'] = core.DataList(header =difHeader)
self.__results['direct'] = core.DataList(header =dirHeader)
self.__results['total'] = core.DataList(header =totalHeader)
for patchNumber in range(self.numberOfPatches):
__diff = __cumulativeRaditionValues["diffuse"][patchNumber] if diffuse else 0
__dir = __cumulativeRaditionValues["direct"][patchNumber] if direct else 0
self.__results['diffuse'].append(core.LBPatchData( __diff, __skyVectors[patchNumber]))
self.__results['direct'].append(core.LBPatchData( __dir, __skyVectors[patchNumber]))
self.__results['total'].append(core.LBPatchData( __diff + __dir, __skyVectors[patchNumber]))
del(__cumulativeRaditionValues)
def mtx2json(self):
"convert sky matrix files to json object"
raise NotImplementedError()
def __repr__(self):
return "Ladybug.SkyMatrix > %s"%self.__epw.location.city
Classes
class CumulativeSkyMtx
Cumulative Sky
Attributes: epwFileAddress: Path to EPW file. skyDensity: Density of the sky. 0 generates a Tregenza sky, which will divide up the sky dome with a coarse density of 145 sky patches. Set to 1 to generate a Reinhart sky, which will divide up the sky dome using a density of 580 sky patches. workingDir: A local directory to run the study and write the results
Usage: epwfile = r"C:\EnergyPlusV8-3-0\WeatherData\USA_CO_Golden-NREL.724666_TMY3.epw" cumSky = CumulativeSkyMtx(epwfile) #calculate the sky # annual results cumSky.annualResults()
# results for an anlysis period
ap = AnalysisPeriod(startMonth = 2, endMonth = 12)
results = cumSky.filterByAnalysisPeriod(ap)
print results.diffuseValues
class CumulativeSkyMtx(object):
"""Cumulative Sky
Attributes:
epwFileAddress: Path to EPW file.
skyDensity: Density of the sky. 0 generates a Tregenza sky, which will
divide up the sky dome with a coarse density of 145 sky patches.
Set to 1 to generate a Reinhart sky, which will divide up the sky dome
using a density of 580 sky patches.
workingDir: A local directory to run the study and write the results
Usage:
epwfile = r"C:\EnergyPlusV8-3-0\WeatherData\USA_CO_Golden-NREL.724666_TMY3.epw"
cumSky = CumulativeSkyMtx(epwfile) #calculate the sky
# annual results
cumSky.annualResults()
# results for an anlysis period
ap = AnalysisPeriod(startMonth = 2, endMonth = 12)
results = cumSky.filterByAnalysisPeriod(ap)
print results.diffuseValues
"""
def __init__(self, epwFileAddress, skyDensity = 0, workingDir = None):
self.__epw = epw.EPW(epwFileAddress)
self.__data = {"diffuse": {}, "direct": {}}
self.__results = {}
self.skyDensity = skyDensity
self.__isCalculated = False
self.__isLoaded = False
self.workingDir = workingDir
@property
def skyDensity(self):
"Return sky density. 0: Tregenza, 1: Reinhart"
return self.__skyDensity
@skyDensity.setter
def skyDensity(self, density):
assert int(density) <= 1, "Sky density is should be 0: Tregenza sky, 1: Reinhart sky"
self.__skyDensity = int(density)
#prepare place holder for patches
for patch in range(self.numberOfPatches):
self.__data['diffuse'] = {}
self.__data['direct'] = {}
@property
def workingDir(self):
"A local directory to run the study and write the results"
return self.__workingDir
@workingDir.setter
def workingDir(self, workingDir):
# update addresses
if not hasattr(self, "__workingDir"):
# user has initiated the class
self.__workingDir = workingDir
if not self.__workingDir:
self.__workingDir = self.__epw.filePath
# add name of city to path
if not self.__workingDir.endswith(self.__epw.location.city):
self.__workingDir = os.path.join(self.__workingDir, self.__epw.location.city.replace(" ", "_"))
# create the folder is it's not created
if not os.path.isdir(self.__workingDir):
os.mkdir(self.__workingDir)
# update path for other files if it's a new workingDir
# naming convention is weatherFileName_[diffuse/direct]_[skyDensity].mtx
__name = self.__epw.fileName[:-4] + "_%s_%d.mtx"
self.__diffuseMtxFileAddress = os.path.join(self.__workingDir, __name%("dif", self.__skyDensity))
self.__directMtxFileAddress = os.path.join(self.__workingDir, __name%("dir", self.__skyDensity))
self.__jsonFileAddress= os.path.join(self.__workingDir, self.__epw.fileName[:-4] + \
"_" + str(self.__skyDensity) + ".json")
@property
def numberOfPatches(self):
return self.__patchData("numberOfPatches")
@property
def skyDiffuseRadiation(self):
"""Diffuse values for sky patches as a LBDataList"""
assert self.__isCalculated, "You need to calculate the materix first before" + \
" loading the results. Use calculateMtx method.\nIf you see this error from inside " + \
"Dynamo reconnect one of the inputs and re-run the file!\nFiles are created under %s"%self.workingDir
assert self.__isLoaded, "The values are not loaded. Use skyMtx method."
return self.__results["diffuse"]
@property
def skyDirectRadiation(self):
"""Direct values for sky patches as a LBDataList"""
assert self.__isCalculated, "You need to calculate the materix first before" + \
" loading the results. Use calculateMtx method.\nIf you see this error from inside " + \
"Dynamo reconnect one of the inputs and re-run the file!\nFiles are created under %s"%self.workingDir
assert self.__isLoaded, "The values are not loaded. Use skyMtx method."
return self.__results["direct"]
@property
def skyTotalRadiation(self):
"""Total values for sky patches as a LBDataList"""
assert self.__isCalculated, "You need to calculate the materix first before" + \
" loading the results. Use calculateMtx method.\nIf you see this error from inside " + \
"Dynamo reconnect one of the inputs and re-run the file!\nFiles are created under %s"%self.workingDir
assert self.__isLoaded, "The values are not loaded. Use skyMtx method."
return self.__results["total"]
def steradianConversionFactor(self, patchNumber):
"Steradian Conversion Factor"
rowNumber = self.__calculateRowNumber(patchNumber)
strConv = self.__patchData("steradianConversionFactor")[rowNumber]
return strConv
def __patchData(self, key):
"""
Return data for sky patches based on key
Args:
key: valid keys are numberOfPatches, numberOfPatchesInEachRow and steradianConversionFactor.
Return:
Data for this sky based on the sky density. Depending on the key it can be a number or a list of numbers
Usage:
self.__patchesData("numberOfPatches")
>> 146
"""
# first row is horizon and last row is values for the zenith
# first patch is the ground. I put 0 on conversion
__data = {
"numberOfPatches": {0 : 146, 1 : 578},
"numOfPatchesInEachRow": {0: [1, 30, 30, 24, 24, 18, 12, 6, 1], \
1: [1, 60, 60, 60, 60, 48, 48, 48, 48, 36, 36, 24, 24, 12, 12, 1]},
"steradianConversionFactor": {0 : [0, 0.0435449227, 0.0416418006, 0.0473984151, \
0.0406730411, 0.0428934136, 0.0445221864, 0.0455168385, 0.0344199465],
1: [0, 0.0113221971, 0.0111894547, 0.0109255262, 0.0105335058, 0.0125224872, \
0.0117312774, 0.0108025291, 0.00974713106, 0.011436609, 0.00974295956, \
0.0119026242, 0.00905126163, 0.0121875626, 0.00612971396, 0.00921483254]}
}
assert key in __data, "Invalid key: %s"%key
return __data[key][self.__skyDensity]
def __calculateRowNumber(self, patchNumber):
"""Calculate number of row for sky patch"""
if patchNumber ==0 : return 0
__numOfPatchesInEachRow = self.__patchData(key = "numOfPatchesInEachRow")
for rowCount, patchCountInRow in enumerate(__numOfPatchesInEachRow):
if patchNumber < sum(__numOfPatchesInEachRow[:rowCount+1]):
return rowCount
def epw2wea(self, filePath = None):
if not filePath:
filePath = os.path.join(self.__workingDir, self.__epw.fileName[:-4] + ".wea")
self.__weaFileAddress = self.__epw.epw2wea(filePath)
def calculateMtx(self, pathToRadianceBinaries = r"c:\radiance\bin", recalculate = False):
"""use Radiance gendaymtx to generate the sky
Args:
pathToRadianceBinaries: Path to Radiance libraries. Default is C:\radiance\bin.
recalculate: Set to True if you want the sky to be recalculated even it has been calculated already
"""
#check if the result is already calculated
if not recalculate:
if os.path.isfile(self.__diffuseMtxFileAddress) \
and os.path.isfile(self.__directMtxFileAddress):
self.__isCalculated = True
return
if not pathToRadianceBinaries: pathToRadianceBinaries = r"c:\radiance\bin"
assert os.path.isfile(os.path.join(pathToRadianceBinaries, "gendaymtx.exe")) \
and os.path.isfile(os.path.join(pathToRadianceBinaries, "rcollate.exe")), \
"Can't find gendaymtx.exe or rcollate.exe in radiance binary folder."
# make sure daymtx and rcollate can be executed
assert os.access(os.path.join(pathToRadianceBinaries, "gendaymtx.exe"), os.X_OK), \
"%s is blocked by system! Right click on the file,"%os.path.join(pathToRadianceBinaries, "gendaymtx.exe") + \
" select properties and unblock it."
assert os.access(os.path.join(pathToRadianceBinaries, "rcollate.exe"), os.X_OK), \
"%s is blocked by system! Right click on the file,"%os.path.join(pathToRadianceBinaries, "rcollate.exe") + \
" select properties and unblock it."
# assure wea file is calculated
if not hasattr(self, "__weaFileAddress"): self.epw2wea()
__name = self.__epw.fileName[:-4] + "_calculate_sky_mtx.bat"
batchFileAddress = os.path.join(self.__workingDir, __name)
batchFile = """
@echo off
echo.
echo HELLO #username! DO NOT CLOSE THIS WINDOW.
echo.
echo IT WILL BE CLOSED AUTOMATICALLY WHEN THE CALCULATION IS OVER!
echo.
echo AND MAY TAKE FEW MINUTES...
echo.
echo CALCULATING DIFFUSE COMPONENT OF THE SKY...
#pathToRadianceBinaries\\gendaymtx -m #skyDensity -s -O1 #weaFileAddress | #pathToRadianceBinaries\\rcollate -t > #diffuseMtxFileAddress
echo.
echo CALCULATING DIRECT COMPONENT OF THE SKY...
#pathToRadianceBinaries\\gendaymtx -m #skyDensity -d -O1 #weaFileAddress | #pathToRadianceBinaries\\rcollate -t > #directMtxFileAddress
""".replace("#pathToRadianceBinaries", pathToRadianceBinaries) \
.replace("#skyDensity", str(self.skyDensity + 1)) \
.replace("#weaFileAddress", self.__weaFileAddress) \
.replace("#diffuseMtxFileAddress", self.__diffuseMtxFileAddress) \
.replace("#directMtxFileAddress", self.__directMtxFileAddress)
# write batch file
with open(batchFileAddress, "w") as genskymtxbatfile:
genskymtxbatfile.write(batchFile)
subprocess.Popen(batchFileAddress, shell = True)
def __calculateLuminanceFromRGB(self, R, G, B, patchNumber):
return (.265074126 * float(R) + .670114631 * float(G) + .064811243 * float(B)) * self.steradianConversionFactor(patchNumber)
def __loadMtxFiles(self):
"""load the values from .mtx files. use self.skyMtx to get the results
"""
if self.__isLoaded:
print "Matrix has been already loaded!"
return
assert self.__isCalculated, "You need to calculate the materix first before" + \
" loading the results. Use calculateMtx method. If you see this error from inside " + \
"Dynamo reconnect one of the inputs and re-run the file!"
assert os.path.getsize(self.__diffuseMtxFileAddress) > 0 and \
os.path.getsize(self.__directMtxFileAddress) > 0, \
"Size of matrix files is 0. Try to recalculate cumulative sky matrix."
try:
# open files and read the lines
diffSkyFile = open(self.__diffuseMtxFileAddress, "rb")
dirSkyFile = open(self.__directMtxFileAddress, "rb")
# pass header
for i in range(9):
diffSkyFile.readline()
dirSkyFile.readline()
# import hourly data
analysisPeriod = core.AnalysisPeriod()
for patchNumber in range(self.numberOfPatches):
# create header for each patch
difHeader = core.LBHeader(city = self.__epw.location.city, frequency ='Hourly', \
analysisPeriod = analysisPeriod, \
dataType = "Patch #%d diffuse radiation"%patchNumber, unit = "Wh")
dirHeader = core.LBHeader(city = self.__epw.location.city, frequency ='Hourly', \
analysisPeriod = analysisPeriod, \
dataType = "Patch #%d direct radiation"%patchNumber, unit = "Wh")
# create an empty data list with the header
self.__data['diffuse'][patchNumber] = core.DataList(header =difHeader)
self.__data['direct'][patchNumber] = core.DataList(header =dirHeader)
for HOY in range(8760):
diffLine = diffSkyFile.readline()
dirLine = dirSkyFile.readline()
timestamp = core.LBDateTime.fromHOY(HOY + 1)
for patchNumber, (diffData, dirData) in enumerate(zip(diffLine.split("\t"), dirLine.split("\t"))):
_difR, _difG, _difB = diffData.split(" ")
_dirR, _dirG, _dirB = dirData.split(" ")
_difValue = self.__calculateLuminanceFromRGB(_difR, _difG, _difB, patchNumber)
_dirValue = self.__calculateLuminanceFromRGB(_dirR, _dirG, _dirB, patchNumber)
self.__data["diffuse"][patchNumber].append(core.LBData(_difValue, timestamp))
self.__data["direct"][patchNumber].append(core.LBData(_dirValue, timestamp))
self.__isLoaded = True
except Exception, e:
print e
finally:
diffSkyFile.close()
dirSkyFile.close()
del(diffLine)
del(dirLine)
# TODO: Analysis perios in headers should be adjusted based on the input
def gendaymtx(self, pathToRadianceBinaries = None, diffuse = True, direct = True, \
recalculate = False, analysisPeriod = None):
"""Get sky matrix for direct, diffuse and total radiation as three separate lists
Args:
pathToRadianceBinaries: Path to Radiance libraries. Default is C:\radiance\bin.
diffuse: Set to True to include diffuse radiation
direct: Set to True to iclude direct radiation
recalculate: Set to True if you want the sky to be recalculated even it has been calculated already
analysisPeriod: An analysis period or a list of integers between 1-8760 for hours of the year. Default is All the hours of the year
"""
# calculate sky if it's not already calculated
if not self.__isCalculated or recalculate:
self.calculateMtx(pathToRadianceBinaries = pathToRadianceBinaries, recalculate = recalculate)
# load matrix files if it's not loaded
if not self.__isLoaded:
self.__loadMtxFiles()
if not analysisPeriod:
HOYs = range(1, 8761)
else:
if isinstance(analysisPeriod, list):
HOYs = [int(h) if 0 < h < 8761 else -1 for h in analysisPeriod]
assert (not -1 in HOYs), "Hour should be between 1-8760"
elif isinstance(analysisPeriod, core.AnalysisPeriod):
HOYs = analysisPeriod.HOYs
else:
raise ValueError("Analysis period should be a list of integers or an analysis period.")
# calculate values and return them as 3 lists
# put 0 value for all the patches
__cumulativeRaditionValues = {"diffuse": [0] * self.numberOfPatches, \
"direct": [0] * self.numberOfPatches}
for patchNumber in range(self.numberOfPatches):
for HOY in HOYs:
__cumulativeRaditionValues["diffuse"][patchNumber] += self.__data["diffuse"][patchNumber][HOY - 1]
__cumulativeRaditionValues["direct"][patchNumber] += self.__data["direct"][patchNumber][HOY - 1]
# create header for each patch
difHeader = core.LBHeader(city = self.__epw.location.city, frequency ='NA', \
analysisPeriod = None, \
dataType = "Sky Patches' Diffues Radiation", unit = "Wh")
dirHeader = core.LBHeader(city = self.__epw.location.city, frequency ='NA', \
analysisPeriod = None, \
dataType = "Sky Patches' Direct Radiation", unit = "Wh")
totalHeader = core.LBHeader(city = self.__epw.location.city, frequency ='NA', \
analysisPeriod = None, \
dataType = "Sky Patches' Total Radiation", unit = "Wh")
# create an empty data list with the header
__skyVectors = skyvector.Skyvectors(self.__skyDensity)
self.__results = {}
self.__results['diffuse'] = core.DataList(header =difHeader)
self.__results['direct'] = core.DataList(header =dirHeader)
self.__results['total'] = core.DataList(header =totalHeader)
for patchNumber in range(self.numberOfPatches):
__diff = __cumulativeRaditionValues["diffuse"][patchNumber] if diffuse else 0
__dir = __cumulativeRaditionValues["direct"][patchNumber] if direct else 0
self.__results['diffuse'].append(core.LBPatchData( __diff, __skyVectors[patchNumber]))
self.__results['direct'].append(core.LBPatchData( __dir, __skyVectors[patchNumber]))
self.__results['total'].append(core.LBPatchData( __diff + __dir, __skyVectors[patchNumber]))
del(__cumulativeRaditionValues)
def mtx2json(self):
"convert sky matrix files to json object"
raise NotImplementedError()
def __repr__(self):
return "Ladybug.SkyMatrix > %s"%self.__epw.location.city
Ancestors (in MRO)
- CumulativeSkyMtx
- __builtin__.object
Instance variables
var numberOfPatches
var skyDensity
var skyDiffuseRadiation
Diffuse values for sky patches as a LBDataList
var skyDirectRadiation
Direct values for sky patches as a LBDataList
var skyTotalRadiation
Total values for sky patches as a LBDataList
var workingDir
Methods
def __init__(
self, epwFileAddress, skyDensity=0, workingDir=None)
def __init__(self, epwFileAddress, skyDensity = 0, workingDir = None):
self.__epw = epw.EPW(epwFileAddress)
self.__data = {"diffuse": {}, "direct": {}}
self.__results = {}
self.skyDensity = skyDensity
self.__isCalculated = False
self.__isLoaded = False
self.workingDir = workingDir
def calculateMtx(
self, pathToRadianceBinaries='c:\\radiance\\bin', recalculate=False)
use Radiance gendaymtx to generate the sky
Args: pathToRadianceBinaries: Path to Radiance libraries. Default is C: adiancein. recalculate: Set to True if you want the sky to be recalculated even it has been calculated already
def calculateMtx(self, pathToRadianceBinaries = r"c:\radiance\bin", recalculate = False):
"""use Radiance gendaymtx to generate the sky
Args:
pathToRadianceBinaries: Path to Radiance libraries. Default is C:\radiance\bin.
recalculate: Set to True if you want the sky to be recalculated even it has been calculated already
"""
#check if the result is already calculated
if not recalculate:
if os.path.isfile(self.__diffuseMtxFileAddress) \
and os.path.isfile(self.__directMtxFileAddress):
self.__isCalculated = True
return
if not pathToRadianceBinaries: pathToRadianceBinaries = r"c:\radiance\bin"
assert os.path.isfile(os.path.join(pathToRadianceBinaries, "gendaymtx.exe")) \
and os.path.isfile(os.path.join(pathToRadianceBinaries, "rcollate.exe")), \
"Can't find gendaymtx.exe or rcollate.exe in radiance binary folder."
# make sure daymtx and rcollate can be executed
assert os.access(os.path.join(pathToRadianceBinaries, "gendaymtx.exe"), os.X_OK), \
"%s is blocked by system! Right click on the file,"%os.path.join(pathToRadianceBinaries, "gendaymtx.exe") + \
" select properties and unblock it."
assert os.access(os.path.join(pathToRadianceBinaries, "rcollate.exe"), os.X_OK), \
"%s is blocked by system! Right click on the file,"%os.path.join(pathToRadianceBinaries, "rcollate.exe") + \
" select properties and unblock it."
# assure wea file is calculated
if not hasattr(self, "__weaFileAddress"): self.epw2wea()
__name = self.__epw.fileName[:-4] + "_calculate_sky_mtx.bat"
batchFileAddress = os.path.join(self.__workingDir, __name)
batchFile = """
@echo off
echo.
echo HELLO #username! DO NOT CLOSE THIS WINDOW.
echo.
echo IT WILL BE CLOSED AUTOMATICALLY WHEN THE CALCULATION IS OVER!
echo.
echo AND MAY TAKE FEW MINUTES...
echo.
echo CALCULATING DIFFUSE COMPONENT OF THE SKY...
#pathToRadianceBinaries\\gendaymtx -m #skyDensity -s -O1 #weaFileAddress | #pathToRadianceBinaries\\rcollate -t > #diffuseMtxFileAddress
echo.
echo CALCULATING DIRECT COMPONENT OF THE SKY...
#pathToRadianceBinaries\\gendaymtx -m #skyDensity -d -O1 #weaFileAddress | #pathToRadianceBinaries\\rcollate -t > #directMtxFileAddress
""".replace("#pathToRadianceBinaries", pathToRadianceBinaries) \
.replace("#skyDensity", str(self.skyDensity + 1)) \
.replace("#weaFileAddress", self.__weaFileAddress) \
.replace("#diffuseMtxFileAddress", self.__diffuseMtxFileAddress) \
.replace("#directMtxFileAddress", self.__directMtxFileAddress)
# write batch file
with open(batchFileAddress, "w") as genskymtxbatfile:
genskymtxbatfile.write(batchFile)
subprocess.Popen(batchFileAddress, shell = True)
def epw2wea(
self, filePath=None)
def epw2wea(self, filePath = None):
if not filePath:
filePath = os.path.join(self.__workingDir, self.__epw.fileName[:-4] + ".wea")
self.__weaFileAddress = self.__epw.epw2wea(filePath)
def gendaymtx(
self, pathToRadianceBinaries=None, diffuse=True, direct=True, recalculate=False, analysisPeriod=None)
Get sky matrix for direct, diffuse and total radiation as three separate lists
Args: pathToRadianceBinaries: Path to Radiance libraries. Default is C: adiancein. diffuse: Set to True to include diffuse radiation direct: Set to True to iclude direct radiation recalculate: Set to True if you want the sky to be recalculated even it has been calculated already analysisPeriod: An analysis period or a list of integers between 1-8760 for hours of the year. Default is All the hours of the year
def gendaymtx(self, pathToRadianceBinaries = None, diffuse = True, direct = True, \
recalculate = False, analysisPeriod = None):
"""Get sky matrix for direct, diffuse and total radiation as three separate lists
Args:
pathToRadianceBinaries: Path to Radiance libraries. Default is C:\radiance\bin.
diffuse: Set to True to include diffuse radiation
direct: Set to True to iclude direct radiation
recalculate: Set to True if you want the sky to be recalculated even it has been calculated already
analysisPeriod: An analysis period or a list of integers between 1-8760 for hours of the year. Default is All the hours of the year
"""
# calculate sky if it's not already calculated
if not self.__isCalculated or recalculate:
self.calculateMtx(pathToRadianceBinaries = pathToRadianceBinaries, recalculate = recalculate)
# load matrix files if it's not loaded
if not self.__isLoaded:
self.__loadMtxFiles()
if not analysisPeriod:
HOYs = range(1, 8761)
else:
if isinstance(analysisPeriod, list):
HOYs = [int(h) if 0 < h < 8761 else -1 for h in analysisPeriod]
assert (not -1 in HOYs), "Hour should be between 1-8760"
elif isinstance(analysisPeriod, core.AnalysisPeriod):
HOYs = analysisPeriod.HOYs
else:
raise ValueError("Analysis period should be a list of integers or an analysis period.")
# calculate values and return them as 3 lists
# put 0 value for all the patches
__cumulativeRaditionValues = {"diffuse": [0] * self.numberOfPatches, \
"direct": [0] * self.numberOfPatches}
for patchNumber in range(self.numberOfPatches):
for HOY in HOYs:
__cumulativeRaditionValues["diffuse"][patchNumber] += self.__data["diffuse"][patchNumber][HOY - 1]
__cumulativeRaditionValues["direct"][patchNumber] += self.__data["direct"][patchNumber][HOY - 1]
# create header for each patch
difHeader = core.LBHeader(city = self.__epw.location.city, frequency ='NA', \
analysisPeriod = None, \
dataType = "Sky Patches' Diffues Radiation", unit = "Wh")
dirHeader = core.LBHeader(city = self.__epw.location.city, frequency ='NA', \
analysisPeriod = None, \
dataType = "Sky Patches' Direct Radiation", unit = "Wh")
totalHeader = core.LBHeader(city = self.__epw.location.city, frequency ='NA', \
analysisPeriod = None, \
dataType = "Sky Patches' Total Radiation", unit = "Wh")
# create an empty data list with the header
__skyVectors = skyvector.Skyvectors(self.__skyDensity)
self.__results = {}
self.__results['diffuse'] = core.DataList(header =difHeader)
self.__results['direct'] = core.DataList(header =dirHeader)
self.__results['total'] = core.DataList(header =totalHeader)
for patchNumber in range(self.numberOfPatches):
__diff = __cumulativeRaditionValues["diffuse"][patchNumber] if diffuse else 0
__dir = __cumulativeRaditionValues["direct"][patchNumber] if direct else 0
self.__results['diffuse'].append(core.LBPatchData( __diff, __skyVectors[patchNumber]))
self.__results['direct'].append(core.LBPatchData( __dir, __skyVectors[patchNumber]))
self.__results['total'].append(core.LBPatchData( __diff + __dir, __skyVectors[patchNumber]))
del(__cumulativeRaditionValues)
def mtx2json(
self)
convert sky matrix files to json object
def mtx2json(self):
"convert sky matrix files to json object"
raise NotImplementedError()
def steradianConversionFactor(
self, patchNumber)
Steradian Conversion Factor
def steradianConversionFactor(self, patchNumber):
"Steradian Conversion Factor"
rowNumber = self.__calculateRowNumber(patchNumber)
strConv = self.__patchData("steradianConversionFactor")[rowNumber]
return strConv