Skip to content

Commit

Permalink
Merge pull request #2 from cdeline/configFile
Browse files Browse the repository at this point in the history
Config file - allow custom module definition
  • Loading branch information
cdeline authored Nov 7, 2017
2 parents fc5fd56 + 84067fd commit c67887c
Show file tree
Hide file tree
Showing 3 changed files with 257 additions and 78 deletions.
228 changes: 183 additions & 45 deletions bifacial_radiance/bifacial_radiance.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,44 @@
bifacial_radiance.py - module to develop radiance bifacial scenes, including gendaylit and gencumulativesky
7/5/2016 - test script based on G173_journal_height
5/1/2017 - standalone module
Pre-requisites:
This software is written in Python 2.7 leveraging many Anaconda tools (e.g. pandas, numpy, etc)
*RADIANCE software should be installed from https://github.com/NREL/Radiance/releases
*If you want to use gencumulativesky, move 'gencumulativesky.exe' from
'bifacial_radiance\data' into your RADIANCE source directory.
*If using a Windows machine you should download the Jaloxa executables at
http://www.jaloxa.eu/resources/radiance/radwinexe.shtml#Download
Overview:
Bifacial_radiance includes several helper functions to make it easier to evaluate
different PV system orientations for rear bifacial irradiance.
Note that this is simply an optical model - identifying available rear irradiance under different conditions.
There are two solar resource modes in bifacial_radiance: `gendaylit` uses hour-by-hour solar
resource descriptions using the Perez diffuse tilted plane model.
`gencumulativesky` is an annual average solar resource that combines hourly
Perez skies into one single solar source, and computes an annual average.
bifacial_radiance includes five object-oriented classes:
RadianceObj: top level class to work on radiance objects, keep track of filenames,
sky values, PV module type etc.
GroundObj: details for the ground surface and reflectance
SceneObj: scene information including array configuration (row spacing, ground height)
MetObj: meteorological data from EPW (energyplus) file.
Future work: include other file support including TMY files
AnalysisObj: Analysis class for plotting and reporting
'''

#start in pylab space to enable plotting
#get_ipython().magic(u'pylab')
import os, datetime
Expand All @@ -15,6 +52,11 @@
from subprocess import Popen, PIPE # replacement for os.system()
#import shlex

import pkg_resources
global DATA_PATH # path to data files including module.json. Global context
DATA_PATH = pkg_resources.resource_filename('bifacial_radiance', 'data/')


def _findme(lst, a): #find string match in a list. found this nifty script on stackexchange
return [i for i, x in enumerate(lst) if x==a]

Expand All @@ -39,8 +81,8 @@ def _popen(cmd, data_in, data_out=PIPE):
return data




# start developing the class

class RadianceObj:
'''
Expand Down Expand Up @@ -88,6 +130,7 @@ def __init__(self, basename=None, path=None):
self.skyfiles = [] # skyfiles for oconv
self.radfiles = [] # scene rad files for oconv
self.octfile = [] #octfile name for analysis


now = datetime.datetime.now()
self.nowstr = str(now.date())+'_'+str(now.hour)+str(now.minute)+str(now.second)
Expand Down Expand Up @@ -136,15 +179,15 @@ def _checkPath(path): # create the file structure if it doesn't exist

_checkPath('images/'); _checkPath('objects/'); _checkPath('results/'); _checkPath('skies/');
# if materials directory doesn't exist, populate it with ground.rad
if not os.path.exists('materials/'):
# figure out where pip installed support files.
from shutil import copy2

if not os.path.exists('materials/'): #copy ground.rad to /materials
os.makedirs('materials/')
print('Making path: materials/')
# figure out where pip installed support files. copy ground.rad to /materials
from shutil import copy2
import pkg_resources
DATA_PATH = pkg_resources.resource_filename('bifacial_radiance', 'data/')

copy2(os.path.join(DATA_PATH,'ground.rad'),'materials')
# if views directory doesn't exist, create it with two default views
# if views directory doesn't exist, create it with two default views - side.vp and front.vp
if not os.path.exists('views/'):
os.makedirs('views/')
with open('views/side.vp', 'wb') as f:
Expand Down Expand Up @@ -497,10 +540,12 @@ def makeOct(self,filelist=None,octname = None):
self.octfile = '%s.oct' % (octname)
return '%s.oct' % (octname)

"""
def analysis(self, octfile = None, basename = None):
'''
default to AnalysisObj.PVSCanalysis(self.octfile, self.basename)
Not sure how wise it is to have RadianceObj.analysis - perhaps this is best eliminated entirely?
Most analysis is not done on the PVSC scene any more... eliminate this and update example notebook?
'''
if octfile is None:
octfile = self.octfile
Expand All @@ -511,13 +556,77 @@ def analysis(self, octfile = None, basename = None):
analysis_obj.PVSCanalysis(octfile, basename)
return analysis_obj
"""
def makeModule(self,name=None,x=1,y=1,bifi=1,orientation='portrait',modulefile = None,text = None):
'''
add module details to the .JSON module config file module.json
This needs to be in the RadianceObj class because this is defined before a SceneObj is.
Parameters
------------
name: string input to name the module type
module configuration dictionary inputs:
x # width of module (meters).
y # height of module (meters).
bifi # bifaciality of the panel (not currently used)
orientation #default orientation of the scene (portrait or landscape)
modulefile # existing radfile location in \objects. Otherwise a default value is used
text = '' # generation text
Returns: None
-------
'''
if name is None:
print('usage: makeModule(name,x,y)')

import json
if modulefile is None:
#replace whitespace with underlines. what about \n and other weird characters?
name2 = str(name).strip().replace(' ', '_')
modulefile = 'objects\\' + name2 + '.rad'
if text is None:
text = '! genbox black PVmodule {} {} 0.02 | xform -t {} 0 0'.format(x, y, -x/2.0)
moduledict = {'x':x,
'y':y,
'bifi':bifi,
'orientation':orientation,
'text':text,
'modulefile':modulefile
}


filedir = os.path.join(DATA_PATH,'module.json') # look in global DATA_PATH for module config file
with open( filedir ) as configfile:
data = json.load(configfile)


data.update({name:moduledict})
with open(os.path.join(DATA_PATH,'module.json') ,'w') as configfile:
json.dump(data,configfile)

print('Module {} successfully created'.format(name))

def printModules(self):
# print available module types by creating a dummy SceneObj
temp = SceneObj('simple_panel')
modulenames = temp.readModule()
print('Available module names: {}'.format([str(x) for x in modulenames]))






def makeScene(self, moduletype=None, sceneDict=None, nMods = 20, nRows = 7):
'''
return a SceneObj
return a SceneObj which contains details of the PV system configuration including
tilt, orientation, row pitch, height, nMods per row, nRows in the system...
'''
if moduletype is None:
print('makeScene(moduletype, sceneDict, nMods, nRows). Available moduletypes: monopanel, simple_panel' )
print('makeScene(moduletype, sceneDict, nMods, nRows). Available moduletypes: monopanel, simple_panel' ) #TODO: read in config file to identify available module types
return
self.scene = SceneObj(moduletype)

Expand Down Expand Up @@ -636,42 +745,69 @@ class SceneObj:
def __init__(self,moduletype=None):
''' initialize SceneObj
'''
modulenames = self.readModule()

if moduletype is None:
moduletype = 'simple_panel'
self.moduletype = moduletype

if moduletype == 'simple_panel': #next module type
radfile = 'objects\\simple_panel.rad'
self.x = 0.95 # width of module.
self.y = 1.59 # height of module.
self.bifi = 1 # bifaciality of the panel
self.orientation = 'portrait' #default orientation of the scene
if not os.path.isfile(radfile):
with open(radfile, 'wb') as f:
f.write('!genbox black PVmodule 0.95 1.59 0.02 | xform -t -0.475 0 0 ')
self.modulefile = radfile
print('Usage: SceneObj(moduletype)\nNo module type selected. Available module types: {}'.format(modulenames))
return
else:
if moduletype in modulenames:
# read in module details from configuration file.
self.readModule(name = moduletype)
else:
print('incorrect panel type selection')
return




def readModule(self,name = None):
'''
Read in available modules in module.json. If a specific module name is
passed, return those details into the SceneObj. Otherwise return available module list.
Parameters
------------
name # name of module.
Returns
-------
dict of module parameters
-or-
list of modulenames if name is not passed in
'''
import json
filedir = os.path.join(DATA_PATH,'module.json')
with open( filedir ) as configfile:
data = json.load(configfile)

modulenames = data.keys()
if name is None:

elif moduletype == 'monopanel' :
self.x = 0.95 # width of module.
self.y = 1.59 # height of module.
self.bifi = 1 # bifaciality of the panel
self.orientation = 'portrait' #default orientation of the scene
self.modulefile = 'objects\\monopanel_1.rad'

elif moduletype == 'mini_panel' :
radfile = 'objects\\mini_panel.rad'
self.x = 0.6096 # width of module.
self.y = 0.9144 # height of module.
self.bifi = 1 # bifaciality of the panel
self.orientation = 'landscape' #default orientation of the scene
return modulenames

if name in modulenames:
moduledict = data[name]
self.moduletype = name

radfile = moduledict['modulefile']
self.x = moduledict['x'] # width of module.
self.y = moduledict['y'] # height of module.
self.bifi = moduledict['bifi'] # bifaciality of the panel. Not currently used
self.orientation = moduledict['orientation'] #default orientation of the scene
#create new .RAD file
if not os.path.isfile(radfile):
with open(radfile, 'wb') as f:
f.write('!genbox black PVmodule 0.6096 0.9144 0.012192 | xform -t -0.3048 0 0 ')
f.write(moduledict['text'])
#if not os.path.isfile(radfile):
# raise Exception('Error: module file not found {}'.format(radfile))
self.modulefile = radfile

return moduledict
else:
print('incorrect panel type selection')
return
print('Error: module name {} doesnt exist'.format(name))
return {}

def makeSceneNxR(self, tilt, height, pitch, orientation = None, azimuth = 180, nMods = 20, nRows = 7):
'''
Expand Down Expand Up @@ -705,8 +841,8 @@ def makeSceneNxR(self, tilt, height, pitch, orientation = None, azimuth = 180, n

text += self.modulefile
# save the .RAD file

radfile = 'objects\\%s_%s_%s_%sx%s.rad'%(self.moduletype,height,pitch, nMods, nRows)
radname = str(self.moduletype).strip().replace(' ', '_')# remove whitespace
radfile = 'objects\\%s_%s_%s_%sx%s.rad'%(radname,height,pitch, nMods, nRows)
with open(radfile, 'wb') as f:
f.write(text)

Expand Down Expand Up @@ -990,10 +1126,12 @@ def analysis(self, octfile, basename, frontscan, backscan, plotflag = False):
if __name__ == "__main__":
'''
Example of how to run a Radiance routine for a simple bifacial system
Pre-requisites: change testfolder to point to an empty directory on your computer
'''
testfolder = r'C:\Users\cdeline\Documents\Python Scripts\TestFolder' #point to an empty directory or existing Radiance directory
'''
import easygui # this is only required if you want a graphical directory picker
#testfolder = r'C:\Users\cdeline\Documents\Python Scripts\TestFolder' #point to an empty directory or existing Radiance directory
testfolder = easygui.diropenbox(msg = 'Select or create an empty directory for the Radiance tree',title='Browse for empty Radiance directory')
demo = RadianceObj('simple_panel',testfolder) # Create a RadianceObj 'object'
demo.setGround(0.62) # input albedo number or material name like 'concrete'. To see options, run this without any input.
try:
Expand All @@ -1016,5 +1154,5 @@ def analysis(self, octfile, basename, frontscan, backscan, plotflag = False):
analysis = AnalysisObj(octfile, demo.basename) # return an analysis object including the scan dimensions for back irradiance
analysis.analysis(octfile, demo.basename, scene.frontscan, scene.backscan) # compare the back vs front irradiance
print('Annual bifacial ratio: %0.3f - %0.3f' %(min(analysis.backRatio), np.mean(analysis.backRatio)) )


'''
1 change: 1 addition & 0 deletions bifacial_radiance/data/module.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"mini_panel": {"orientation": "landscape", "text": "!genbox black PVmodule 0.6096 0.9144 0.012192 | xform -t -0.3048 0 0 ", "bifi": 1, "modulefile": "objects\\mini_panel.rad", "y": 0.9144, "x": 0.6096}, "simple_panel": {"orientation": "portrait", "text": "! genbox black PVmodule 0.95 1.59 0.02 | xform -t -0.475 0 0", "bifi": 1, "modulefile": "objects\\simple_panel.rad", "y": 1.59, "x": 0.95}, "monopanel": {"orientation": "portrait", "text": "", "bifi": 1, "modulefile": "objects\\monopanel_1.rad", "y": 1.59, "x": 0.95}, "Prism Solar Bi60": {"orientation": "portrait", "text": "! genbox black PVmodule 0.984 1.695 0.02 | xform -t -0.492 0 0", "bifi": 0.9, "modulefile": "objects\\Prism_Solar_Bi60.rad", "y": 1.695, "x": 0.984}}
Loading

0 comments on commit c67887c

Please sign in to comment.