diff --git a/.gitignore b/.gitignore index 06740714..31c10ba2 100644 --- a/.gitignore +++ b/.gitignore @@ -86,7 +86,11 @@ instance/ .scrapy # Sphinx documentation -docs/_build/ +docs/sphinx/build/ +docs/sphinx/source/generated/ +docs/sphinx/source/savefig/ +*.un~ +*.rst~ # PyBuilder target/ diff --git a/.travis.yml b/.travis.yml index f900d686..b0ba6531 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,15 +14,25 @@ env: - REQ_ENV='-r requirements.txt .' # - REQ_ENV='--upgrade --upgrade-strategy=eager .' +# GUI testing requires xvfb +dist: xenial +services: + - xvfb + # Radiance is required to run tests. Download and setup v5.2 before_install: + - pwd - wget https://github.com/NREL/Radiance/releases/download/5.2/radiance-5.2.dd0f8e38a7-Linux.tar.gz -O /tmp/radiance.tar.gz - tar -xvf /tmp/radiance.tar.gz - export PATH=$PATH:$PWD/radiance-5.2.dd0f8e38a7-Linux/usr/local/radiance/bin - export RAYPATH=.:$PWD/radiance-5.2.dd0f8e38a7-Linux/usr/local/radiance/lib - export MANPATH=$MANPATH:$PWD/radiance-5.2.dd0f8e38a7-Linux/usr/local/radiance/man - - ls -l /home/travis/build/NREL/bifacial_radiance/ + +# Copy gencumulativesky into RAYPATH... + - ls -l /home/travis/build/NREL/bifacial_radiance/bifacial_radiance/data + - cp /home/travis/build/NREL/bifacial_radiance/bifacial_radiance/data/gencumulativesky $PWD/radiance-5.2.dd0f8e38a7-Linux/usr/local/radiance/bin/ + - ls -l $PWD/radiance-5.2.dd0f8e38a7-Linux/usr/local/radiance/bin # Example from TravisCI: https://docs.travis-ci.com/user/installing-dependencies #before_install: # - wget https://github.com/n1k0/casperjs/archive/1.0.2.tar.gz -O /tmp/casper.tar.gz @@ -31,10 +41,12 @@ before_install: install: - pip install $REQ_ENV + - pip install coveralls script: - - pytest - + - pytest --cov=bifacial_radiance +after_success: + - coveralls after_script: # debug what's going on - try to ls-l the tests folder to see where things are saved - pwd diff --git a/README.md b/README.md index a60b4e20..4386be6d 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,28 @@ +![logo](docs/images_wiki/bifacial_radiance.png) + # bifacial_radiance -Master branch: [![Build Status](https://travis-ci.com/NREL/bifacial_radiance.svg?branch=master)](https://travis-ci.org/NREL/bifacial_radiance) +Master branch: [![Build Status](https://travis-ci.com/NREL/bifacial_radiance.svg?branch=master)](https://travis-ci.org/NREL/bifacial_radiance) +[![Coverage Status](https://coveralls.io/repos/github/NREL/bifacial_radiance/badge.svg?branch=master)](https://coveralls.io/github/NREL/bifacial_radiance?branch=master) +[![Documentation Status](https://readthedocs.org/projects/bifacial-radiance/badge/?version=latest)](https://bifacial-radiance.readthedocs.io/en/latest/?badge=latest) + Development branch: [![Build Status](https://travis-ci.com/NREL/bifacial_radiance.svg?branch=development)](https://travis-ci.org/NREL/bifacial_radiance) +[![Coverage Status](https://coveralls.io/repos/github/NREL/bifacial_radiance/badge.svg?branch=development)](https://coveralls.io/github/NREL/bifacial_radiance?branch=development) + ## Introduction bifacial_radiance contains a series of Python wrapper functions to make working with RADIANCE easier, particularly for the PV researcher interested in bifacial PV -performance. Please see the instructions here, notebook examples in the -\docs\ folder of the repo, and discussion on the Wiki for more details! +performance. For more information, check out our [documentation](https://bifacial-radiance.readthedocs.io), +[Tutorials in the form of Jupyter Notebooks](docs/tutorials/), or reffer to our [Wiki](https://github.com/NREL/bifacial_radiance/wiki) +and [Issues](https://github.com/NREL/bifacial_radiance/issues) page. -## Installation Video +## Installation https://youtu.be/4A9GocfHKyM -This video shows how to install the bifacial_radiance software and all associated softwares needed. More info on the Wiki. Instructions are also shown below. +This video shows how to install the bifacial_radiance software and all associated software needed. More info on the Wiki. Instructions are also shown below. + +For detailed instructions of how to install bifacial_radiance, you can also refer to the [installation guide](https://bifacial-radiance.readthedocs.io/en/latest/installation.html) ## New: GUI! @@ -20,191 +30,47 @@ A GUI has been added in version 3.0. The GUI reads/writes all input parameters n To run the gui, import bifacial_radiance and run bifacial_radiance.gui() +![GUI](docs/images_wiki/bifacial_radiance_GUI.png) -## Install using pip - - - #### PREREQUISITES (Step 0): - This software requires the previous installation of RADIANCE from https://github.com/NREL/Radiance/releases. - - Make sure you add radiance to the system PATH so Python can interact with the radiance program - - If you are on a PC you should also copy the Jaloxa radwinexe-5.0.a.8-win64.zip executables into `program files/radiance/bin`: http://www.jaloxa.eu/resources/radiance/radwinexe.shtml - - **Note: bifacial_radiance is not endorsed by or officially connected with the Radiance software package or its development team.** - - #### STEP 1: Install and import bifacial_radiance - - - clone the bifacial_radiance repo to your local directory or download and unzip the .zip file - - navigate to the \bifacial_radiance directory using anaconda command line - - run `pip install -e .` ( the period . is required, the -e flag is optional and installs in development mode where changes to the bifacial_radiance.py files are immediately incorporated into the module if you re-start the python kernel) - - for best compatibility, deploy in an Anaconda 2.7 environment, or run `pip install -r requirements.txt` - - #### STEP 2: Move gencumulativesky.exe - Copy gencumulativesky.exe from the repo's `/bifacial_radiance/data/` directory and copy into your Radiance install directory. - This is typically found in `/program files/radiance/bin/`. - - **Note: GenCumulativeSky is detailed in the publication "Robinson, D., Stone, A., Irradiation modeling made simple – the cumulative sky approach and its applications, Proc. PLEA 2004, Eindhoven 2004."** - The source is [available from the authors here.](https://documents.epfl.ch/groups/u/ur/urbansimulation/www/GenCumSky/GenCumSky.zip) - - #### STEP 3: Create a local Radiance directory for storing the scene files created - Keep scene geometry files separate from the bifacial_radiance directory. Create a local directory somewhere to be used for storing scene files. - - #### STEP 4: Reboot the computer - This makes sure the PATH is updated ## Usage +Check out the [Jupyter Tutorial Notebooks](docs/tutorials/) to see detailed examples of the capacities of bifacial_radiance. +The [Intro examples](https://bifacial-radiance.readthedocs.io/en/latest/introexamples.html) and the [readthedocs documentation](https://bifacial-radiance.readthedocs.io) also provide a good starting point. - from bifacial_radiance import RadianceObj # the main container for working with radiance - -Now that the module is loaded, let's use it. - - demo = RadianceObj(name = 'Testrun', path = 'myfolder') #create a new demo run. Files will have the Testrun prefix, and be saved to 'myfolder' - - demo.setGround(0.3) # input albedo number or material name like 'concrete'. To see options, run this without any input. - - # Now download an EPW climate file for any global lat/lon value : - epwfile = demo.getEPW(37.5,-77.6) # pull EPW data for any global lat/lon - - # let's load this epw file into our MetObj container class. - metdata = demo.readEPW(epwfile) # read in the EPW weather data as metdata object. Run this with no input parameters to load a graphical picker - # if you'd rather use a TMY3 file, select one that you've already downloaded: - metdata = demo.readTMY() # select an existing TMY3 climate file. return metdata object. - -Now that we have ground albedo and a climate file loaded, we need to start designing the PV system. -Fixed tilt systems can have hourly simulations with gendaylit, or annual simulations with gencumulativesky - - - # create cumulativesky skyfiles and save it to the \skies\ directory, along with a .cal file in root - demo.genCumSky(demo.epwfile) - ---- optionally ---- - - demo.gendaylit(metdata,4020) # pass in the metdata object, plus the integer number of the hour in the year you want to run (0 to 8759) - # note that for genCumSky, you pass the *name* of the EPW file. for gendaylit you pass the metdata object. - - -The nice thing about the RadianceObject is that it keeps track of where all of your skyfiles and calfiles are being saved. -Next let's put a PV system together. The details are saved in a dictionary and passed into makeScene. Let's start with a PV module: - - # Create a new moduletype: Prism Solar Bi60. width = .984m height = 1.695m. - demo.makeModule(name='Prism Solar Bi60',x=0.984,y=1.695) #x is assumed module width, y is height. - - # Let's print the available module types - demo.printModules() - -the module details are stored in a module.json file in the bifacial_radiance\data directory so you can re-use module parameters. -Each unit module generates a corresponding .RAD file in \objects\ which is referenced in our array scene. - -Starting in version 0.2.3 there are some nifty module generation options including stacking them (e.g. 2-up or 3-up but any number) with a gap, and torque tube down the middle of the string. - -Since version 0.2.4, orientation of the module has been deprecated as an input. Now, to define the orientation it has to be done in the makeModule step, assigning the correct values to the x and y of the module. x is the size of the module along the row, therefore for a landscape module x > y. - - - # make a 72-cell module 2m x 1m arranged 2-up in portrait with a 10cm torque tube behind. - # a 5cm offset between panels and the tube, along with a 5cm array gap between the modules: - - demo.makeModule(name = '1axis_2up', x = 1.995, y = 0.995, torquetube = True, tubetype = 'round', - diameter = 0.1, zgap = 0.05, ygap = 0.05, numpanels = 2) - - -Now we make a sceneDict with details of our PV array. We'll make a rooftop array of Prism Solar modules in landscape -at 10 degrees tilt. - - module_name = 'Prism Solar Bi60' - sceneDict = {'tilt':10,'pitch':1.5,'clearance_height':0.2,'azimuth':180, 'nMods': 20, 'nRows': 7} - # this is passed into makeScene to generate the RADIANCE .rad file - scene = demo.makeScene(module_name,sceneDict) #makeScene creates a .rad file with 20 modules per row, 7 rows. - -OK, we're almost done. RADIANCE has to combine the skyfiles, groundfiles, material (\*.mtl) files, and scene geometry (.rad) files -into an OCT file using makeOct. Instead of having to remember where all these files are, the RadianceObj keeps track. Or call .getfilelist() - - octfile = demo.makeOct(demo.getfilelist()) # the input parameter is optional - maybe you have a custom file list you want to use - -The final step is to query the front and rear irradiance of our array. The default is a 9-point scan through the center module of the center row of the array. The actual scan values are set up by .makeScene and returned in your sceneObj (sceneObj.frontscan, sceneObj.backscan). To do this we use an AnalysisObj. - - - analysis = AnalysisObj(octfile, demo.name) # return an analysis object including the scan dimensions for back irradiance - analysis.analysis(octfile, demo.name, scene.frontscan, scene.backscan) # compare the back vs front irradiance - print('Annual bifacial ratio average: %0.3f' %( sum(analysis.Wm2Back) / sum(analysis.Wm2Front) ) ) - -Beginning in v0.2.5 we can query specific scans along the array. - - # Do a 4-point scan along the 5th module in the 2nd row of the array. - scene = demo.makeScene(module_name,sceneDict) - octfile = demo.makeOct() - analysis = AnalysisObj(octfile, demo.name) - frontscan, backscan = analysis.moduleAnalysis(scene, sensorsy = 4, modWanted = 5, rowWanted = 2) - frontresults,backresults = analysis.analysis(octfile, demo.name, scene.frontscan, scene.backscan) - print('Annual bifacial ratio on 5th Module average: %0.3f' %( sum(analysis.Wm2Back) / sum(analysis.Wm2Front) ) ) - - # And you can run the scanning for another module. - frontscan, backscan = analysis.moduleAnalysis(scene, sensorsy = 4, modWanted = 1, rowWanted = 2) - frontresults,backresults = analysis.analysis(octfile, demo.name, scene.frontscan, scene.backscan) - print('Annual bifacial ratio average on 1st Module: %0.3f' %( sum(analysis.Wm2Back) / sum(analysis.Wm2Front) ) ) - - - - -For more usage examples including 1-axis tracking examples, carport examples, and examples of scenes with multiple sceneObjects (different trackers/modules/etc) see the Jupyter notebooks in \docs\ - - - - -## Functions -`RadianceObj(basename,path)`: This is the basic container for radiance projects. -Pass in a `basename` string to name your radiance scene and append to various -result and image files. `path` points to an existing or empty Radiance directory. -If the directory is empty it will be populated with appropriate ground.rad and view -files. -Default behavior: basename defaults to current date/time, and path defaults to current directory - -`RadianceObj.getfilelist()` : return list of material, sky and rad files for the scene - -`RadianceObj.returnOctFiles()` : return files in the root directory with .oct extension - -`RadianceObj.setGround(material_or_albedo, material_file)`: set the ground to either -a material type (e.g. 'litesoil') or albedo value e.g. 0.25. 'material_file' is a -filename for a specific material RAD file to load with your material description - -`RadianceObj.getEPW(lat,lon)` : download the closest EnergyPlus EPW file for a give lat / lon value. -return: filename of downloaded file - -`RadianceObj.readWeatherFile(weatherFile)` : call readEPW or readTMY functions to read in a epw or tmy file. Return: metdata +## Contributing -`RadianceObj.readEPW(epwfilename)` : use pyepw to read in a epw file. Return: metdata +We need your help to make bifacial_radiance a great tool! Please see the [Contributing page](https://bifacial-radiance.readthedocs.io/en/latest/contributing.html) for more on how you can contribute. The long-term success of pvlib-python requires substantial community support. -`RadianceObj.readTMY(tmyfilename)` : use pvlib to read in a tmy3 file. Return: metdata +## License -`RadianceObj.gendaylit(metdata,timeindex)` : pass in data read from a EPW file. -Select a single time slice of the annual timeseries to conduct gendaylit Perez model -for that given time +BSD 3-clause -`RadianceObj.gencumsky(epwfilename, startdt, enddt)` : use gencumulativesky.exe to do an entire year simulation. -If no epwfilename is passed, the most recent EPW file read by `readEPW` will be used. startdt and enddt are optional -start and endtimes for the gencumulativesky. NOTE: if you don't have gencumulativesky.exe loaded, -look in bifacial_radiance/data/ for a copy +## Getting Support -`RadianceObj.makeOct(filelist, octname)`: create a .oct file from the scene .RAD files. By default -this will use RadianceObj.getfilelist() to build the .oct file, and use RadianceObj.basename as the filename. +If you suspect that you may have discovered a bug or if you'd like to +change something about pvlib, then please make an issue on our +[GitHub issues page](https://github.com/NREL/bifacial_radiance/issues). -`RadianceObj.makeScene(moduletype, sceneDict)` : create a PV array scene with nMods modules per row and nRows number of rows. moduletype specifies the type of module which be one of the options saved in module.JSON (makeModule adds a customModule to the Json file). Pre-loaded module options are 'simple_panel', which generates a simple 0.95m x 1.59m module, or 'monopanel' which looks for 'objects/monopanel_1.rad'. sceneDict is a dictionary containing the following keys: 'tilt','pitch','clearance_height','azimuth', 'nMods', 'nRows'. - Return: SceneObj which includes details about the PV scene including frontscan and backscan details +bifacial_radiance questions can be asked on +[Stack Overflow](http://stackoverflow.com) and tagged with +the [bifacial_radiance](http://stackoverflow.com/questions/tagged/bifacial_radiance) tag. -`RadianceObj.getTrackingGeometryTimeIndex(metdata, timeindex, angledelta, roundTrackerAngleBool, backtrack, gcr, hubheight, sceney)`: returns tracker tilt and clearance height for a specific point in time. - Return: tracker_theta, tracker_height, tracker_azimuth_ang +The [bifacial-radiance google group](https://groups.google.com/forum/#!forum/bifacial_radiance) +has just started, and will be used for discussing various topics of interest to the bifacial-radiance +community. We also make new version announcements on the google group. -`AnalysisObj(octfile,basename)` : Object for conducting analysis on a .OCT file. +## Citing -`AnalysisObj.makeImage(viewfile,octfile, basename)` : create visual render of scene 'octfile' from view 'views/viewfile' +If you use bifacial_radiance in a published work, please cite: -`AnalysisObj.makeFalseColor(viewfile,octfile, basename)` : create false color Wm-2 -render of scene 'octfile' from view 'views/viewfile' + Deline, Chris, Marion, William, and Ayala, Silvana. Bifacial_Radiance. + Computer Software. https://github.com/NREL/bifacial_radiance. + 17 Dec. 2017. Web. doi:10.11578/dc.20180530.16. https://www.osti.gov/doecode/biblio/6869 -`AnalysisObj.analysis(octfile, basename, frontscan, backscan)` : conduct a general front / back ratio -analysis of a .oct file. frontscan, backscan: dictionary input for linePtsMakeDict that -is passed from AnalysisObj.makeScene. +Additional bifacial_radiance publications with validation of the software include: +* Ayala Pelaez S, Deline C, Greenberg P, Stein JS, Kostuk RK. Model and validation of single-axis tracking with bifacial PV. IEEE J Photovoltaics. 2019;9(3):715-721. https://ieeexplore.ieee.org/document/8644027 and https://www.nrel.gov/docs/fy19osti/72039.pdf (pre-print, conference version) +* Ayala Pelaez, Deline C, MacAlpine M, Marion B, Stein J, Kostuk K. Comparison of Bifacial Solar Irradiance Model Predictions with Field Validation. IEEE J Photovoltaics. 2019; 9(1):82-87. https://ieeexplore.ieee.org/document/8534404 -MORE DOCS TO COME: +Or check our [Github Wiki](https://github.com/NREL/bifacial_radiance/wiki) for a complete list of publications. diff --git a/README.md~ b/README.md~ new file mode 100644 index 00000000..d1ba0266 --- /dev/null +++ b/README.md~ @@ -0,0 +1,218 @@ +![logo](docs/images_wiki/bifacial_radiance.png) + +# bifacial_radiance +Master branch: [![Build Status](https://travis-ci.com/NREL/bifacial_radiance.svg?branch=master)](https://travis-ci.org/NREL/bifacial_radiance) +Development branch: [![Build Status](https://travis-ci.com/NREL/bifacial_radiance.svg?branch=development)](https://travis-ci.org/NREL/bifacial_radiance) + +## Introduction + +bifacial_radiance contains a series of Python wrapper functions to make working with +RADIANCE easier, particularly for the PV researcher interested in bifacial PV +performance. Please see the instructions here, notebook examples in the +\docs\ folder of the repo, and discussion on the Wiki for more details! + +## Installation Video + +https://youtu.be/4A9GocfHKyM +This video shows how to install the bifacial_radiance software and all associated software needed. More info on the Wiki. Instructions are also shown below. + +## New: GUI! + +A GUI has been added in version 3.0. The GUI reads/writes all input parameters necessary to run a simulation, and runs the specified simulation by calling the correct functions with the specified parameters. So no need to use a journal or a script! But you still need to install following the procedure below. + +To run the gui, import bifacial_radiance and run bifacial_radiance.gui() + +![GUI](docs/images_wiki/bifacial_radiance_GUI.png) + + +## Install using pip + + + #### PREREQUISITES (Step 0): + This software requires the previous installation of RADIANCE from https://github.com/NREL/Radiance/releases. + + Make sure you add radiance to the system PATH so Python can interact with the radiance program + + If you are on a PC you should also copy the Jaloxa radwinexe-5.0.a.8-win64.zip executables into `program files/radiance/bin`: http://www.jaloxa.eu/resources/radiance/radwinexe.shtml + + **Note: bifacial_radiance is not endorsed by or officially connected with the Radiance software package or its development team.** + + #### STEP 1: Install and import bifacial_radiance + + - run `pip install bifacial_radiance ` + + #### Alternative if you want a development mode: + + - clone the bifacial_radiance repo to your local directory or download and unzip the .zip file + - navigate to the \bifacial_radiance directory using anaconda command line + - run `pip install -e .` ( the period . is required, the -e flag is optional and installs in development mode where changes to the bifacial_radiance.py files are immediately incorporated into the module if you re-start the python kernel) + - for best compatibility, deploy in an Anaconda Python 3.7 environment, or run `pip install -r requirements.txt` + + #### STEP 2: Move gencumulativesky.exe + Copy gencumulativesky.exe from the repo's `/bifacial_radiance/data/` directory and copy into your Radiance install directory. + This is typically found in `/program files/radiance/bin/`. + + **Note: GenCumulativeSky is detailed in the publication "Robinson, D., Stone, A., Irradiation modeling made simple – the cumulative sky approach and its applications, Proc. PLEA 2004, Eindhoven 2004."** + The source is [available from the authors here.](https://documents.epfl.ch/groups/u/ur/urbansimulation/www/GenCumSky/GenCumSky.zip) + + #### STEP 3: Create a local Radiance directory for storing the scene files created + Keep scene geometry files separate from the bifacial_radiance directory. Create a local directory somewhere to be used for storing scene files. + + #### STEP 4: Reboot the computer + This makes sure the PATH is updated + +## Usage + + + from bifacial_radiance import RadianceObj # the main container for working with radiance + +Now that the module is loaded, let's use it. + + demo = RadianceObj(name = 'Testrun', path = 'myfolder') #create a new demo run. Files will have the Testrun prefix, and be saved to 'myfolder' + + demo.setGround(0.3) # input albedo number or material name like 'concrete'. To see options, run this without any input. + + # Now download an EPW climate file for any global lat/lon value : + epwfile = demo.getEPW(37.5,-77.6) # pull EPW data for any global lat/lon + + # let's load this epw file into our MetObj container class. + metdata = demo.readEPW(epwfile) # read in the EPW weather data as metdata object. Run this with no input parameters to load a graphical picker + # if you'd rather use a TMY3 file, select one that you've already downloaded: + metdata = demo.readTMY() # select an existing TMY3 climate file. return metdata object. + +Now that we have ground albedo and a climate file loaded, we need to start designing the PV system. +Fixed tilt systems can have hourly simulations with gendaylit, or annual simulations with gencumulativesky + + + # create cumulativesky skyfiles and save it to the \skies\ directory, along with a .cal file in root + demo.genCumSky(demo.epwfile) + +--- optionally ---- + + demo.gendaylit(metdata,4020) # pass in the metdata object, plus the integer number of the hour in the year you want to run (0 to 8759) + # note that for genCumSky, you pass the *name* of the EPW file. for gendaylit you pass the metdata object. + + +The nice thing about the RadianceObject is that it keeps track of where all of your skyfiles and calfiles are being saved. +Next let's put a PV system together. The details are saved in a dictionary and passed into makeScene. Let's start with a PV module: + + # Create a new moduletype: Prism Solar Bi60. width = .984m height = 1.695m. + demo.makeModule(name='Prism Solar Bi60',x=0.984,y=1.695) #x is assumed module width, y is height. + + # Let's print the available module types + demo.printModules() + +the module details are stored in a module.json file in the bifacial_radiance\data directory so you can re-use module parameters. +Each unit module generates a corresponding .RAD file in \objects\ which is referenced in our array scene. + +Starting in version 0.2.3 there are some nifty module generation options including stacking them (e.g. 2-up or 3-up but any number) with a gap, and torque tube down the middle of the string. + +Since version 0.2.4, orientation of the module has been deprecated as an input. Now, to define the orientation it has to be done in the makeModule step, assigning the correct values to the x and y of the module. x is the size of the module along the row, therefore for a landscape module x > y. + + + # make a 72-cell module 2m x 1m arranged 2-up in portrait with a 10cm torque tube behind. + # a 5cm offset between panels and the tube, along with a 5cm array gap between the modules: + + demo.makeModule(name = '1axis_2up', x = 1.995, y = 0.995, torquetube = True, tubetype = 'round', + diameter = 0.1, zgap = 0.05, ygap = 0.05, numpanels = 2) + + +Now we make a sceneDict with details of our PV array. We'll make a rooftop array of Prism Solar modules in landscape +at 10 degrees tilt. + + module_name = 'Prism Solar Bi60' + sceneDict = {'tilt':10,'pitch':1.5,'clearance_height':0.2,'azimuth':180, 'nMods': 20, 'nRows': 7} + # this is passed into makeScene to generate the RADIANCE .rad file + scene = demo.makeScene(module_name,sceneDict) #makeScene creates a .rad file with 20 modules per row, 7 rows. + +OK, we're almost done. RADIANCE has to combine the skyfiles, groundfiles, material (\*.mtl) files, and scene geometry (.rad) files +into an OCT file using makeOct. Instead of having to remember where all these files are, the RadianceObj keeps track. Or call .getfilelist() + + octfile = demo.makeOct(demo.getfilelist()) # the input parameter is optional - maybe you have a custom file list you want to use + +The final step is to query the front and rear irradiance of our array. The default is a 9-point scan through the center module of the center row of the array. The actual scan values are set up by .makeScene and returned in your sceneObj (sceneObj.frontscan, sceneObj.backscan). To do this we use an AnalysisObj. + + + analysis = AnalysisObj(octfile, demo.name) # return an analysis object including the scan dimensions for back irradiance + analysis.analysis(octfile, demo.name, scene.frontscan, scene.backscan) # compare the back vs front irradiance + print('Annual bifacial ratio average: %0.3f' %( sum(analysis.Wm2Back) / sum(analysis.Wm2Front) ) ) + +Beginning in v0.2.5 we can query specific scans along the array. + + # Do a 4-point scan along the 5th module in the 2nd row of the array. + scene = demo.makeScene(module_name,sceneDict) + octfile = demo.makeOct() + analysis = AnalysisObj(octfile, demo.name) + frontscan, backscan = analysis.moduleAnalysis(scene, sensorsy = 4, modWanted = 5, rowWanted = 2) + frontresults,backresults = analysis.analysis(octfile, demo.name, scene.frontscan, scene.backscan) + print('Annual bifacial ratio on 5th Module average: %0.3f' %( sum(analysis.Wm2Back) / sum(analysis.Wm2Front) ) ) + + # And you can run the scanning for another module. + frontscan, backscan = analysis.moduleAnalysis(scene, sensorsy = 4, modWanted = 1, rowWanted = 2) + frontresults,backresults = analysis.analysis(octfile, demo.name, scene.frontscan, scene.backscan) + print('Annual bifacial ratio average on 1st Module: %0.3f' %( sum(analysis.Wm2Back) / sum(analysis.Wm2Front) ) ) + + + + +For more usage examples including 1-axis tracking examples, carport examples, and examples of scenes with multiple sceneObjects (different trackers/modules/etc) see the Jupyter notebooks in \docs\ + + + + +## Functions +`RadianceObj(basename,path)`: This is the basic container for radiance projects. +Pass in a `basename` string to name your radiance scene and append to various +result and image files. `path` points to an existing or empty Radiance directory. +If the directory is empty it will be populated with appropriate ground.rad and view +files. +Default behavior: basename defaults to current date/time, and path defaults to current directory + +`RadianceObj.getfilelist()` : return list of material, sky and rad files for the scene + +`RadianceObj.returnOctFiles()` : return files in the root directory with .oct extension + +`RadianceObj.setGround(material_or_albedo, material_file)`: set the ground to either +a material type (e.g. 'litesoil') or albedo value e.g. 0.25. 'material_file' is a +filename for a specific material RAD file to load with your material description + +`RadianceObj.getEPW(lat,lon)` : download the closest EnergyPlus EPW file for a give lat / lon value. +return: filename of downloaded file + +`RadianceObj.readWeatherFile(weatherFile)` : call readEPW or readTMY functions to read in a epw or tmy file. Return: metdata + +`RadianceObj.readEPW(epwfilename)` : use pyepw to read in a epw file. Return: metdata + +`RadianceObj.readTMY(tmyfilename)` : use pvlib to read in a tmy3 file. Return: metdata + +`RadianceObj.gendaylit(metdata,timeindex)` : pass in data read from a EPW file. +Select a single time slice of the annual timeseries to conduct gendaylit Perez model +for that given time + +`RadianceObj.gencumsky(epwfilename, startdt, enddt)` : use gencumulativesky.exe to do an entire year simulation. +If no epwfilename is passed, the most recent EPW file read by `readEPW` will be used. startdt and enddt are optional +start and endtimes for the gencumulativesky. NOTE: if you don't have gencumulativesky.exe loaded, +look in bifacial_radiance/data/ for a copy + +`RadianceObj.makeOct(filelist, octname)`: create a .oct file from the scene .RAD files. By default +this will use RadianceObj.getfilelist() to build the .oct file, and use RadianceObj.basename as the filename. + +`RadianceObj.makeScene(moduletype, sceneDict)` : create a PV array scene with nMods modules per row and nRows number of rows. moduletype specifies the type of module which be one of the options saved in module.JSON (makeModule adds a customModule to the Json file). Pre-loaded module options are 'simple_panel', which generates a simple 0.95m x 1.59m module, or 'monopanel' which looks for 'objects/monopanel_1.rad'. sceneDict is a dictionary containing the following keys: 'tilt','pitch','clearance_height','azimuth', 'nMods', 'nRows'. + Return: SceneObj which includes details about the PV scene including frontscan and backscan details + +`RadianceObj.getTrackingGeometryTimeIndex(metdata, timeindex, angledelta, roundTrackerAngleBool, backtrack, gcr, hubheight, sceney)`: returns tracker tilt and clearance height for a specific point in time. + Return: tracker_theta, tracker_height, tracker_azimuth_ang + +`AnalysisObj(octfile,basename)` : Object for conducting analysis on a .OCT file. + +`AnalysisObj.makeImage(viewfile,octfile, basename)` : create visual render of scene 'octfile' from view 'views/viewfile' + +`AnalysisObj.makeFalseColor(viewfile,octfile, basename)` : create false color Wm-2 +render of scene 'octfile' from view 'views/viewfile' + +`AnalysisObj.analysis(octfile, basename, frontscan, backscan)` : conduct a general front / back ratio +analysis of a .oct file. frontscan, backscan: dictionary input for linePtsMakeDict that +is passed from AnalysisObj.makeScene. + + +MORE DOCS TO COME: diff --git a/bifacial_radiance/TEMP/.gitignore b/bifacial_radiance/TEMP/.gitignore new file mode 100644 index 00000000..86d0cb27 --- /dev/null +++ b/bifacial_radiance/TEMP/.gitignore @@ -0,0 +1,4 @@ +# Ignore everything in this directory +* +# Except this file +!.gitignore \ No newline at end of file diff --git a/bifacial_radiance/__init__.py b/bifacial_radiance/__init__.py index bf40d65e..3ee28b3e 100644 --- a/bifacial_radiance/__init__.py +++ b/bifacial_radiance/__init__.py @@ -1,9 +1,10 @@ from __future__ import absolute_import from bifacial_radiance.main import AnalysisObj, GroundObj, MetObj, RadianceObj, SceneObj -from bifacial_radiance.readepw import readepw +#from bifacial_radiance.readepw import readepw from bifacial_radiance import load from bifacial_radiance import modelchain from bifacial_radiance.gui import gui +from bifacial_radiance import mismatch from ._version import get_versions __version__ = get_versions()['version'] del get_versions diff --git a/bifacial_radiance/analysis.py b/bifacial_radiance/analysis.py deleted file mode 100644 index 541da0fe..00000000 --- a/bifacial_radiance/analysis.py +++ /dev/null @@ -1,486 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Tue Mar 26 20:16:47 2019 - -@author: sayala -""" - -#from load import * -import load -import os -from pvmismatch import * # this imports everything we need -import numpy as np -import csv -from statsmodels import robust - - -def analysisIrradianceandPowerMismatch(testfolder, writefiletitle, numpanels, sensorsy, portraitorlandscape='landscape'): - ''' - Reads and calculates power output and mismatch for each file in the - testfolder where all the bifacial_radiance irradiance results .csv are saved. - First load each file, cleans it and resamples it to the numsensors set in this function, - and then calculates irradiance mismatch and PVMismatch power output for averaged, minimum, - or detailed irradiances on each cell for the cases of A) only 12 or 8 downsmaples values are - considered (at the center of each cell), and B) 12 or 8 values are obtained from averaging - all the irradiances falling in the area of the cell (No edges or inter-cell spacing are considered - at this moment). Then it saves all the A and B irradiances, as well as the cleaned/resampled - front and rear irradiances. - - Ideally sensorsy in the read data is >> 12 to give results for the irradiance mismatch in the cell. - - Also ideally n - - Parameters - ---------- - testfolder: folder containing output .csv files for bifacial_radiance - writefiletitle: .csv title where the output results will be saved. - numpanels: 1 or 2 only at hte moment, necessary for the cleaning routine. - portraitorlandscape: 'portrait' or 'landscape', for PVMismatch input - which defines the electrical interconnects inside the module. - sensorsy : number of sensors. Ideally this number is >> 12 and - is also similar to the number of sensors (points) in the .csv result files. - We want more than 12 sensors to be able to calculate mismatch of - irradiance in the cell. - - ''' - - - - #INPUT VARIABLES NECESSARY: - #\\nrel.gov\shared\5J00\Staff\CDeline\Bifacial mismatch data\Tracker mismatch data\3_26_19 Cairo_mismatch_1up tube - #testfolder = r'C:\Users\sayala\Documents\RadianceScenes\Demo3\results' - #testfolder = r'\\nrel.gov\shared\5J00\Staff\CDeline\Bifacial mismatch data\Tracker mismatch data\3_26_19 Cairo_mismatch_1up tube\results_noTorqueTube' - #writefiletitle = r'C:\Users\sayala\Documents\RadianceScenes\results_Cairo_mismatch_1up_noTorqueTube.csv' - #numpanels= 1 - #portraitorlandscape = 'portrait' # portrait has 12 cells, landscape has 8 - #sensorsy = 120 # deepclean will clean and resample to this number of sensors. - #ideally close nubmer to the original number of sample points. - # Also, if it's just 12 or 8 (for landscape or portrait), all the averagd values and cell mismatch - # become a mooth point - - # User information. - filelist = sorted(os.listdir(testfolder)) - print('{} files in the directory'.format(filelist.__len__())) - - # PVMISMATCH Initialization of System - pvsys = pvsystem.PVsystem(numberStrs=1, numberMods=1) # makes the system # 1 module, in portrait mode. - pmp_ideal=pvsys.Pmp # Panel ideal. Monofacial. - stdpl=np.array([[0, 23, 24, 47, 48, 71, 72, 95], - [1, 22, 25, 46, 49, 70, 73, 94], - [2, 21, 26, 45, 50, 69, 74, 93], - [3, 20, 27, 44, 51, 68, 75, 92], - [4, 19, 28, 43, 52, 67, 76, 91], - [5, 18, 29, 42, 53, 66, 77, 90], - [6, 17, 30, 41, 54, 65, 78, 89], - [7, 16, 31, 40, 55, 64, 79, 88], - [8, 15, 32, 39, 56, 63, 80, 87], - [9, 14, 33, 38, 57, 62, 81, 86], - [10, 13, 34, 37, 58, 61, 82, 85], - [11, 12, 35, 36, 59, 60, 83, 84]]) - - - if portraitorlandscape == 'portrait': - samplecells=12 - repeatedcells=8 - - if portraitorlandscape == 'landscape': - samplecells=8 - repeatedcells=12 - stdpl = stdpl.transpose() - - # SAMPLE POINT AND HEADER DEFINITION - cellCenterPVM=[] # This grabs just the value at the 'center' of the cell. - cellFrontandBackMismatch_Header = [] - cellBackMismatch_Header = [] - cellCenterFrontValue_Header = [] - cellCenterBackValue_Header = [] - cellFrontAveragedValue_Header = [] - cellBackAveragedValue_Header = [] - frontres_header = [] - backres_header = [] - - for i in range (0, samplecells): - cellCenterPVM.append((i*sensorsy/(samplecells*1.0)+(i+1)*sensorsy/(samplecells*1.0)/2)) - cellFrontandBackMismatch_Header.append('FrontplusBack_Mismatch_cell_'+str(i)) - cellBackMismatch_Header.append('Back_Mismatch_cell_'+str(i)) - cellCenterFrontValue_Header.append('CellCenterFrontValue_cell'+str(i)) - cellCenterBackValue_Header.append('CellCenterBackValue_cell'+str(i)) - cellBackAveragedValue_Header.append('CellBack_AveragedValue_cell_'+str(i)) - cellFrontAveragedValue_Header.append('CellFront_AveragedValue_cell_'+str(i)) - - for i in range (0, sensorsy): - frontres_header.append('Clean_Front_cell'+str(i)) - backres_header.append('Clean_Back_cell'+str(i)) - - # HEADERS: - outputheaders = ['Timestamp', 'PowerAveraged_CellCenter', 'PowerMin_CellCenter', 'PowerDetailed_CellCenter', 'PowerAveraged_AverageValues', 'PowerMin_AverageValues', 'PowerDetailed_AverageValues', - 'MAD_cellCenterVal', 'MAD_cellAverage', 'MAD_frontplusback_clean', 'Cell Front Min', 'Cell Back Min', 'Irradiance Mismatch Front+Back Max', - 'Irradiance Mismatch Back Max'] - outputheaders += cellFrontandBackMismatch_Header - outputheaders += cellBackMismatch_Header - outputheaders += cellCenterFrontValue_Header - outputheaders += cellCenterBackValue_Header - outputheaders += cellBackAveragedValue_Header - outputheaders += cellFrontAveragedValue_Header - outputheaders += frontres_header - outputheaders += backres_header - - with open (writefiletitle,'w') as csvfile: - - sw = csv.writer(csvfile, delimiter=',', quotechar='|', quoting=csv.QUOTE_MINIMAL, lineterminator='\n') - - sw.writerow(outputheaders) - # LOOP OVER FILES HERE - for z in range(0, filelist.__len__()): - #for z in range(0, 1): - - data=load.read1Result(os.path.join(testfolder,filelist[z])) - #sensorsy = len(data) # 210 for this case. deepclean resamples to value given. - - [frontres, backres] = load.deepcleanResult(data, sensorsy, numpanels, automatic=True) - cellAverageValues_FrontPlusBack=[] - cellFrontAverage=[] # This averages the number of sensors. - cellBackAverage=[] - cellFrontandBackMismatch=[] - cellBackMismatch=[] - cellFrontMin=[] - cellBackMin=[] - cellFrontPlusBackMin=[] - frontandbackres = frontres+ backres - cellRows=len(frontres) # this is the same as sensorsy.... maybe replace? #TODO - - if cellRows != samplecells: - for i in range (0, samplecells): - istart = int(i*cellRows/samplecells) - iend = int((i+1)*cellRows/samplecells) - cellFrontAverage.append(np.average(frontres[istart:iend])) - cellBackAverage.append(np.average(backres[istart:iend])) - cellAverageValues_FrontPlusBack.append(np.average(frontres[istart:iend])+np.average(backres[istart:iend])) - cellFrontandBackMismatch.append((max(frontandbackres[istart:iend])-min(frontandbackres[istart:iend]))*100/(max(frontandbackres[istart:iend])+min(frontandbackres[istart:iend]))) - cellBackMismatch.append((max(backres[istart:iend])-min(backres[istart:iend]))*100/(max(backres[istart:iend])+min(backres[istart:iend]))) - cellFrontMin.append(min(frontres[istart:iend])) - cellBackMin.append(min(backres[istart:iend])) - cellFrontPlusBackMin.append(min(frontandbackres[istart:iend])) - cellCenterValFront= np.interp(cellCenterPVM, list(range(0,cellRows)), frontres) - cellCenterValBack= np.interp(cellCenterPVM, list(range(0,cellRows)), backres) - else: - cellCenterValFront = frontres - cellCenterValBack = backres - - - sunmatDetailed_CellCenter=[] - sunmatAveraged_CellCenter=[] - sunmatMin_CellCenter=[] - sunmatDetailed_AverageValues=[] - sunmatAveraged_AverageValues=[] - sunmatMin_AverageValues=[] - - # Center of Cell only - cellCenterValues_FrontPlusBack = cellCenterValFront+cellCenterValBack - AveFront_CellCenter=cellCenterValFront.mean() - AveBack_CellCenter=cellCenterValBack.mean() - - # Average of Cell - #cellAverageValues_FrontPlusBack = sum(cellFrontAverage,cellBackAverage) - AveFront_AverageValues = np.mean(cellFrontAverage) - AveBack_AverageValues = np.mean(cellBackAverage) - - # Repeat to create a matrix to pass matrix. - for j in range (0, len(cellCenterValues_FrontPlusBack)): - sunmatDetailed_CellCenter.append([cellCenterValues_FrontPlusBack[j]/1000]*repeatedcells) - sunmatDetailed_AverageValues.append([cellAverageValues_FrontPlusBack[j]/1000]*repeatedcells) - - for j in range (0, len(cellCenterValFront)): - sunmatAveraged_CellCenter.append([(AveFront_CellCenter+AveBack_CellCenter)/1000]*repeatedcells) - sunmatAveraged_AverageValues.append([(AveFront_AverageValues+AveBack_AverageValues)/1000]*repeatedcells) - - for j in range (0, len(cellCenterValFront)): - sunmatMin_CellCenter.append([min(cellCenterValues_FrontPlusBack)/1000]*repeatedcells) - sunmatMin_AverageValues.append([min(cellFrontPlusBackMin)/1000]*repeatedcells) - - # ACtually do calculations - pvsys.setSuns({0: {0: [sunmatAveraged_CellCenter, stdpl]}}) - PowerAveraged_CellCenter=pvsys.Pmp - - pvsys.setSuns({0: {0: [sunmatDetailed_CellCenter, stdpl]}}) - PowerDetailed_CellCenter=pvsys.Pmp - - pvsys.setSuns({0: {0: [sunmatMin_CellCenter, stdpl]}}) - PowerMinimum_CellCenter=pvsys.Pmp - - # ACtually do calculations - pvsys.setSuns({0: {0: [sunmatAveraged_AverageValues, stdpl]}}) - PowerAveraged_AverageValues=pvsys.Pmp - - pvsys.setSuns({0: {0: [sunmatDetailed_AverageValues, stdpl]}}) - PowerDetailed_AverageValues=pvsys.Pmp - - pvsys.setSuns({0: {0: [sunmatMin_AverageValues, stdpl]}}) - PowerMinimum_AverageValues=pvsys.Pmp - - - #flattened = [val for sublist in dictvalues for val in sublist] - - - # Append Values - # Append Values - #cellCenterValFrontFlat = [val for sublist in cellCenterValFront for val in sublist] - outputvalues = [filelist[z],PowerAveraged_CellCenter, PowerMinimum_CellCenter, PowerDetailed_CellCenter, PowerAveraged_AverageValues, PowerMinimum_AverageValues, PowerDetailed_AverageValues, - robust.mad(cellCenterValues_FrontPlusBack), robust.mad(cellAverageValues_FrontPlusBack), robust.mad(frontandbackres), - min(cellFrontMin), min(cellBackMin), - max(cellFrontandBackMismatch), max(cellBackMismatch)] - outputvalues+=cellFrontandBackMismatch # 12 - outputvalues+=cellBackMismatch # 12 - outputvalues+=list(cellCenterValFront) # 12 - outputvalues+=list(cellCenterValBack) # 12 - outputvalues+=list(cellFrontAverage) # 12 - outputvalues+=list(cellBackAverage) # 12 - outputvalues+=list(frontres) # sensorsy # 210 - outputvalues+=list(backres) # sensorsy 210 - - sw.writerow(outputvalues) - - -def analysisIrradianceandPowerMismatch2(testfolder, writefiletitle, numpanels, sensorsy, portraitorlandscape='landscape'): - ''' - Reads and calculates power output and mismatch for each file in the - testfolder where all the bifacial_radiance irradiance results .csv are saved. - First load each file, cleans it and resamples it to the numsensors set in this function, - and then calculates irradiance mismatch and PVMismatch power output for averaged, minimum, - or detailed irradiances on each cell for the cases of A) only 12 or 8 downsmaples values are - considered (at the center of each cell), and B) 12 or 8 values are obtained from averaging - all the irradiances falling in the area of the cell (No edges or inter-cell spacing are considered - at this moment). Then it saves all the A and B irradiances, as well as the cleaned/resampled - front and rear irradiances. - - Ideally sensorsy in the read data is >> 12 to give results for the irradiance mismatch in the cell. - - Also ideally n - - Parameters - ---------- - testfolder: folder containing output .csv files for bifacial_radiance - writefiletitle: .csv title where the output results will be saved. - numpanels: 1 or 2 only at hte moment, necessary for the cleaning routine. - portraitorlandscape: 'portrait' or 'landscape', for PVMismatch input - which defines the electrical interconnects inside the module. - sensorsy : number of sensors. Ideally this number is >> 12 and - is also similar to the number of sensors (points) in the .csv result files. - We want more than 12 sensors to be able to calculate mismatch of - irradiance in the cell. - - ''' - - - - #INPUT VARIABLES NECESSARY: - #\\nrel.gov\shared\5J00\Staff\CDeline\Bifacial mismatch data\Tracker mismatch data\3_26_19 Cairo_mismatch_1up tube - #testfolder = r'C:\Users\sayala\Documents\RadianceScenes\Demo3\results' - #testfolder = r'\\nrel.gov\shared\5J00\Staff\CDeline\Bifacial mismatch data\Tracker mismatch data\3_26_19 Cairo_mismatch_1up tube\results_noTorqueTube' - #writefiletitle = r'C:\Users\sayala\Documents\RadianceScenes\results_Cairo_mismatch_1up_noTorqueTube.csv' - #numpanels= 1 - #portraitorlandscape = 'portrait' # portrait has 12 cells, landscape has 8 - #sensorsy = 120 # deepclean will clean and resample to this number of sensors. - #ideally close nubmer to the original number of sample points. - # Also, if it's just 12 or 8 (for landscape or portrait), all the averagd values and cell mismatch - # become a mooth point - - # User information. - filelist = sorted(os.listdir(testfolder)) - print('{} files in the directory'.format(filelist.__len__())) - - # PVMISMATCH Initialization of System - pvsys = pvsystem.PVsystem(numberStrs=1, numberMods=1) # makes the system # 1 module, in portrait mode. - pmp_ideal=pvsys.Pmp # Panel ideal. Monofacial. - stdpl=np.array([[0, 23, 24, 47, 48, 71, 72, 95], - [1, 22, 25, 46, 49, 70, 73, 94], - [2, 21, 26, 45, 50, 69, 74, 93], - [3, 20, 27, 44, 51, 68, 75, 92], - [4, 19, 28, 43, 52, 67, 76, 91], - [5, 18, 29, 42, 53, 66, 77, 90], - [6, 17, 30, 41, 54, 65, 78, 89], - [7, 16, 31, 40, 55, 64, 79, 88], - [8, 15, 32, 39, 56, 63, 80, 87], - [9, 14, 33, 38, 57, 62, 81, 86], - [10, 13, 34, 37, 58, 61, 82, 85], - [11, 12, 35, 36, 59, 60, 83, 84]]) - - - if portraitorlandscape == 'portrait': - samplecells=12 - repeatedcells=8 - - if portraitorlandscape == 'landscape': - samplecells=8 - repeatedcells=12 - stdpl = stdpl.transpose() - - # SAMPLE POINT AND HEADER DEFINITION - cellCenterPVM=[] # This grabs just the value at the 'center' of the cell. - cellFrontandBackMismatch_Header = [] - cellBackMismatch_Header = [] - cellCenterFrontValue_Header = [] - cellCenterBackValue_Header = [] - cellFrontAveragedValue_Header = [] - cellBackAveragedValue_Header = [] - frontres_header = [] - backres_header = [] - - for i in range (0, samplecells): - cellCenterPVM.append((i*sensorsy/(samplecells*1.0)+(i+1)*sensorsy/(samplecells*1.0)/2)) - cellFrontandBackMismatch_Header.append('FrontplusBack_Mismatch_cell_'+str(i)) - cellBackMismatch_Header.append('Back_Mismatch_cell_'+str(i)) - cellCenterFrontValue_Header.append('CellCenterFrontValue_cell'+str(i)) - cellCenterBackValue_Header.append('CellCenterBackValue_cell'+str(i)) - cellBackAveragedValue_Header.append('CellBack_AveragedValue_cell_'+str(i)) - cellFrontAveragedValue_Header.append('CellFront_AveragedValue_cell_'+str(i)) - - for i in range (0, sensorsy): - frontres_header.append('Clean_Front_cell'+str(i)) - backres_header.append('Clean_Back_cell'+str(i)) - - # HEADERS: - outputheaders = ['Timestamp', 'PowerAveraged_CellCenter', 'PowerMin_CellCenter', 'PowerDetailed_CellCenter', 'PowerAveraged_AverageValues', 'PowerMin_AverageValues', 'PowerDetailed_AverageValues', - 'PowerFRONT_Averaged', 'PowerFRONT_Detailed', - 'MAD_cellCenterVal', 'MAD_cellAverage', 'MAD_frontplusback_clean', 'Cell Front Min', 'Cell Back Min', 'Irradiance Mismatch Front+Back Max', - 'Irradiance Mismatch Back Max'] - outputheaders += cellFrontandBackMismatch_Header - outputheaders += cellBackMismatch_Header - outputheaders += cellCenterFrontValue_Header - outputheaders += cellCenterBackValue_Header - outputheaders += cellBackAveragedValue_Header - outputheaders += cellFrontAveragedValue_Header - outputheaders += frontres_header - outputheaders += backres_header - - with open (writefiletitle,'w') as csvfile: - - sw = csv.writer(csvfile, delimiter=',', quotechar='|', quoting=csv.QUOTE_MINIMAL, lineterminator='\n') - - sw.writerow(outputheaders) - # LOOP OVER FILES HERE - for z in range(0, filelist.__len__()): - #for z in range(0, 1): - - data=load.read1Result(os.path.join(testfolder,filelist[z])) - #sensorsy = len(data) # 210 for this case. deepclean resamples to value given. - - [frontres, backres] = load.deepcleanResult(data, sensorsy, numpanels, automatic=True) - cellAverageValues_FrontPlusBack=[] - cellFrontAverage=[] # This averages the number of sensors. - cellBackAverage=[] - cellFrontandBackMismatch=[] - cellBackMismatch=[] - cellFrontMin=[] - cellBackMin=[] - cellFrontPlusBackMin=[] - frontandbackres = frontres+ backres - cellRows=len(frontres) # this is the same as sensorsy.... maybe replace? #TODO - - if cellRows != samplecells: - for i in range (0, samplecells): - istart = int(i*cellRows/samplecells) - iend = int((i+1)*cellRows/samplecells) - cellFrontAverage.append(np.average(frontres[istart:iend])) - cellBackAverage.append(np.average(backres[istart:iend])) - cellAverageValues_FrontPlusBack.append(np.average(frontres[istart:iend])+np.average(backres[istart:iend])) - cellFrontandBackMismatch.append((max(frontandbackres[istart:iend])-min(frontandbackres[istart:iend]))*100/(max(frontandbackres[istart:iend])+min(frontandbackres[istart:iend]))) - cellBackMismatch.append((max(backres[istart:iend])-min(backres[istart:iend]))*100/(max(backres[istart:iend])+min(backres[istart:iend]))) - cellFrontMin.append(min(frontres[istart:iend])) - cellBackMin.append(min(backres[istart:iend])) - cellFrontPlusBackMin.append(min(frontandbackres[istart:iend])) - cellCenterValFront= np.interp(cellCenterPVM, list(range(0,cellRows)), frontres) - cellCenterValBack= np.interp(cellCenterPVM, list(range(0,cellRows)), backres) - else: - cellCenterValFront = frontres - cellCenterValBack = backres - - - sunmatDetailed_CellCenter=[] - sunmatAveraged_CellCenter=[] - sunmatMin_CellCenter=[] - sunmatDetailed_AverageValues=[] - sunmatAveraged_AverageValues=[] - sunmatMin_AverageValues=[] - sunmatFrontOnly_Averaged=[] - sunmatFrontOnly_Detailed=[] - - # Center of Cell only - cellCenterValues_FrontPlusBack = cellCenterValFront+cellCenterValBack - AveFront_CellCenter=cellCenterValFront.mean() - AveBack_CellCenter=cellCenterValBack.mean() - - # Average of Cell - #cellAverageValues_FrontPlusBack = sum(cellFrontAverage,cellBackAverage) - AveFront_AverageValues = np.mean(cellFrontAverage) - AveBack_AverageValues = np.mean(cellBackAverage) - - # Repeat to create a matrix to pass matrix. - for j in range (0, len(cellCenterValues_FrontPlusBack)): - sunmatDetailed_CellCenter.append([cellCenterValues_FrontPlusBack[j]/1000]*repeatedcells) - sunmatDetailed_AverageValues.append([cellAverageValues_FrontPlusBack[j]/1000]*repeatedcells) - - for j in range (0, len(cellCenterValFront)): - sunmatAveraged_CellCenter.append([(AveFront_CellCenter+AveBack_CellCenter)/1000]*repeatedcells) - sunmatAveraged_AverageValues.append([(AveFront_AverageValues+AveBack_AverageValues)/1000]*repeatedcells) - - for j in range (0, len(cellCenterValFront)): - sunmatMin_CellCenter.append([min(cellCenterValues_FrontPlusBack)/1000]*repeatedcells) - sunmatMin_AverageValues.append([min(cellFrontPlusBackMin)/1000]*repeatedcells) - - # FRONT MISMATCH - for j in range (0, len(cellCenterValFront)): - sunmatFrontOnly_Averaged.append([cellFrontAverage[j]/1000]*repeatedcells) - sunmatFrontOnly_Detailed.append([cellCenterValFront[j]/1000]*repeatedcells) - - - # ACtually do calculations - pvsys.setSuns({0: {0: [sunmatAveraged_CellCenter, stdpl]}}) - PowerAveraged_CellCenter=pvsys.Pmp - - pvsys.setSuns({0: {0: [sunmatDetailed_CellCenter, stdpl]}}) - PowerDetailed_CellCenter=pvsys.Pmp - - pvsys.setSuns({0: {0: [sunmatMin_CellCenter, stdpl]}}) - PowerMinimum_CellCenter=pvsys.Pmp - - # ACtually do calculations - pvsys.setSuns({0: {0: [sunmatAveraged_AverageValues, stdpl]}}) - PowerAveraged_AverageValues=pvsys.Pmp - - pvsys.setSuns({0: {0: [sunmatDetailed_AverageValues, stdpl]}}) - PowerDetailed_AverageValues=pvsys.Pmp - - pvsys.setSuns({0: {0: [sunmatMin_AverageValues, stdpl]}}) - PowerMinimum_AverageValues=pvsys.Pmp - - # ACtually do calculations - pvsys.setSuns({0: {0: [sunmatFrontOnly_Averaged, stdpl]}}) - PowerFRONT_Averaged=pvsys.Pmp - - pvsys.setSuns({0: {0: [sunmatFrontOnly_Detailed, stdpl]}}) - PowerFRONT_Detailed=pvsys.Pmp - - #flattened = [val for sublist in dictvalues for val in sublist] - - - # Append Values - # Append Values - #cellCenterValFrontFlat = [val for sublist in cellCenterValFront for val in sublist] - outputvalues = [filelist[z],PowerAveraged_CellCenter, - PowerMinimum_CellCenter, PowerDetailed_CellCenter, - PowerAveraged_AverageValues, PowerMinimum_AverageValues, - PowerDetailed_AverageValues, PowerFRONT_Averaged, PowerFRONT_Detailed, - robust.mad(cellCenterValues_FrontPlusBack), robust.mad(cellAverageValues_FrontPlusBack), robust.mad(frontandbackres), - min(cellFrontMin), min(cellBackMin), - max(cellFrontandBackMismatch), max(cellBackMismatch)] - outputvalues+=cellFrontandBackMismatch # 12 - outputvalues+=cellBackMismatch # 12 - outputvalues+=list(cellCenterValFront) # 12 - outputvalues+=list(cellCenterValBack) # 12 - outputvalues+=list(cellFrontAverage) # 12 - outputvalues+=list(cellBackAverage) # 12 - outputvalues+=list(frontres) # sensorsy # 210 - outputvalues+=list(backres) # sensorsy 210 - - sw.writerow(outputvalues) \ No newline at end of file diff --git a/bifacial_radiance/data/gencumulativesky b/bifacial_radiance/data/gencumulativesky new file mode 100755 index 00000000..b53f1f0e Binary files /dev/null and b/bifacial_radiance/data/gencumulativesky differ diff --git a/bifacial_radiance/data/module.json b/bifacial_radiance/data/module.json index e688b963..32e57b9a 100644 --- a/bifacial_radiance/data/module.json +++ b/bifacial_radiance/data/module.json @@ -1 +1 @@ -{"Prism Solar Bi60": {"x": 0.984, "y": 1.695, "scenex": 1.084, "sceney": 1.695, "scenez": 0.15, "numpanels": 1, "bifi": 0.9, "text": "! genbox black Prism_Solar_Bi60 0.984 1.695 0.02 | xform -t -0.492 -0.8475 0.15 -a 1 -t 0 1.695 0", "modulefile": "objects\\Prism_Solar_Bi60.rad", "moduleoffset": 0.15, "xgap": 0.1, "ygap": 0.0, "zgap": 0.1, "cellModule": null, "torquetube": {"bool": false, "diameter": 0.1, "tubetype": "round", "material": "Metal_Grey"}}, "2upTracker": {"x": 0.984, "y": 1.7, "scenex": 1.004, "sceney": 3.45, "scenez": 0.1, "numpanels": 2, "bifi": 1.0, "text": "! genbox black 2upTracker 0.984 1.7 0.02 | xform -t -0.492 -1.7249999999999999 0.1 -a 2 -t 0 1.75 0\r\n! genrev Metal_Grey tube1 t*1.004 0.05 32 | xform -ry 90 -t -0.502 0 0", "modulefile": "objects\\2upTracker.rad", "moduleoffset": 0.1, "xgap": 0.02, "ygap": 0.05, "zgap": 0.05, "cellModule": null, "torquetube": {"bool": true, "diameter": 0.1, "tubetype": "round", "material": "Metal_Grey"}}, "test": {"x": 1.59, "y": 0.95, "scenex": 1.59, "sceney": 0.95, "scenez": 0.15, "numpanels": 1, "bifi": 1, "text": "! genbox black test 1.59 0.95 0.02 | xform -t -0.795 -0.475 0 -a 1 -t 0 0.95 0", "modulefile": "objects\\test.rad", "offsetfromaxis": 0, "xgap": 0, "ygap": 0.0, "zgap": 0.1, "cellModule": null, "torquetube": {"bool": false, "diameter": 0.1, "tubetype": "Round", "material": "Metal_Grey"}}, "Prism Solar Bi60 landscape": {"x": 1.695, "y": 0.984, "scenex": 1.705, "sceney": 0.984, "scenez": 0.15, "numpanels": 1, "bifi": 0.9, "text": "! genbox black Prism_Solar_Bi60_landscape 1.695 0.984 0.02 | xform -t -0.8475 -0.492 0.15 -a 1 -t 0 0.984 0", "modulefile": "objects\\Prism_Solar_Bi60_landscape.rad", "moduleoffset": 0.15, "xgap": 0.01, "ygap": 0.0, "zgap": 0.1, "cellModule": null, "torquetube": {"bool": false, "diameter": 0.1, "tubetype": "hex", "material": "Metal_Grey"}}, "cellModule": {"x": 1.91, "y": 0.95, "scenex": 1.92, "sceney": 0.95, "scenez": 0.0, "numpanels": 1, "bifi": 0.9, "text": "! genbox black cellPVmodule 0.15 0.15 0.02 | xform -t -0.88 -0.475 0.0 -a 12 -t 0.16 0 0 -a 6 -t 0 0.16 0 -a 1 -t 0 0.95 0", "modulefile": "objects\\cellModule.rad", "moduleoffset": 0.0, "xgap": 0.01, "ygap": 0.0, "zgap": 0.0, "cellModule": {"numcellsx": 12, "numcellsy": 6, "xcell": 0.15, "ycell": 0.15, "xcellgap": 0.01, "ycellgap": 0.01}, "torquetube": {"bool": false, "diameter": 0.0, "tubetype": "round", "material": "Metal_Grey"}}} \ No newline at end of file +{"Prism Solar Bi60": {"x": 0.984, "y": 1.695, "scenex": 1.084, "sceney": 1.695, "scenez": 0.15, "numpanels": 1, "bifi": 0.9, "text": "! genbox black Prism_Solar_Bi60 0.984 1.695 0.02 | xform -t -0.492 -0.8475 0.15 -a 1 -t 0 1.695 0", "modulefile": "objects\\Prism_Solar_Bi60.rad", "moduleoffset": 0.15, "xgap": 0.1, "ygap": 0.0, "zgap": 0.1, "cellModule": null, "torquetube": {"bool": false, "diameter": 0.1, "tubetype": "round", "material": "Metal_Grey"}}, "2upTracker": {"x": 0.984, "y": 1.7, "scenex": 1.004, "sceney": 3.45, "scenez": 0.1, "numpanels": 2, "bifi": 1.0, "text": "! genbox black 2upTracker 0.984 1.7 0.02 | xform -t -0.492 -1.7249999999999999 0.1 -a 2 -t 0 1.75 0\r\n! genrev Metal_Grey tube1 t*1.004 0.05 32 | xform -ry 90 -t -0.502 0 0", "modulefile": "objects\\2upTracker.rad", "moduleoffset": 0.1, "xgap": 0.02, "ygap": 0.05, "zgap": 0.05, "cellModule": null, "torquetube": {"bool": true, "diameter": 0.1, "tubetype": "round", "material": "Metal_Grey"}}, "test": {"x": 1.59, "y": 0.95, "scenex": 1.59, "sceney": 0.95, "scenez": 0.15, "numpanels": 1, "bifi": 1, "text": "! genbox black test 1.59 0.95 0.02 | xform -t -0.795 -0.475 0 -a 1 -t 0 0.95 0", "modulefile": "objects\\test.rad", "offsetfromaxis": 0, "xgap": 0, "ygap": 0.0, "zgap": 0.1, "cellModule": null, "torquetube": {"bool": false, "diameter": 0.1, "tubetype": "Round", "material": "Metal_Grey"}}, "Prism Solar Bi60 landscape": {"x": 1.695, "y": 0.984, "scenex": 1.705, "sceney": 0.984, "scenez": 0.15, "numpanels": 1, "bifi": 1, "text": "! genbox black Prism_Solar_Bi60_landscape 1.695 0.984 0.02 | xform -t -0.8475 -0.492 0 -a 1 -t 0 0.984 0", "modulefile": "objects\\Prism_Solar_Bi60_landscape.rad", "offsetfromaxis": 0, "xgap": 0.01, "ygap": 0.0, "zgap": 0.1, "cellModule": null, "torquetube": {"bool": false, "diameter": 0.1, "tubetype": "Round", "material": "Metal_Grey"}}, "cellModule": {"x": 1.91, "y": 0.95, "scenex": 1.92, "sceney": 0.95, "scenez": 0.0, "numpanels": 1, "bifi": 0.9, "text": "! genbox black cellPVmodule 0.15 0.15 0.02 | xform -t -0.88 -0.475 0.0 -a 12 -t 0.16 0 0 -a 6 -t 0 0.16 0 -a 1 -t 0 0.95 0", "modulefile": "objects\\cellModule.rad", "moduleoffset": 0.0, "xgap": 0.01, "ygap": 0.0, "zgap": 0.0, "cellModule": {"numcellsx": 12, "numcellsy": 6, "xcell": 0.15, "ycell": 0.15, "xcellgap": 0.01, "ycellgap": 0.01}, "torquetube": {"bool": false, "diameter": 0.0, "tubetype": "round", "material": "Metal_Grey"}}, "Custom Tracker Module": {"x": 0.984, "y": 1.7, "scenex": 1.004, "sceney": 3.5, "scenez": 0.1, "numpanels": 2, "bifi": 1, "text": "! genbox black Custom_Tracker_Module 0.984 1.7 0.02 | xform -t -0.492 -1.75 0.1 -a 2 -t 0 1.8 0\r\n! genrev Metal_Grey tube1 t*1.004 0.05 32 | xform -ry 90 -t -0.502 0 0", "modulefile": "objects\\Custom_Tracker_Module.rad", "offsetfromaxis": 0.1, "xgap": 0.02, "ygap": 0.1, "zgap": 0.05, "cellModule": null, "torquetube": {"bool": true, "diameter": 0.1, "tubetype": "round", "material": "Metal_Grey"}}} \ No newline at end of file diff --git a/bifacial_radiance/gui.py b/bifacial_radiance/gui.py index ec73a11a..bd63c01b 100644 --- a/bifacial_radiance/gui.py +++ b/bifacial_radiance/gui.py @@ -99,6 +99,25 @@ def read_valuesfromGUI(): ''' + def _set_daily_endtimes(): + ''' + for hourly for a day simulations, only start month & day is used + enddate and times must be set + ''' + try: + entries = [entry_enddate_day, entry_enddate_month, entry_startdate_hour, entry_enddate_hour] + values = [entry_startdate_day.get(), entry_startdate_month.get(), "1", "23"] + for entry,val in zip(entries, values): + entry.config(state='normal') + entry.delete(0,END) + entry.insert(0,val) + entry.config(state='disabled') + + except: + # no startdate / hour entered + raise Exception('something went wrong') + + testfolder, weatherfile, weatherinputMode, simulation,\ moduletype, rewriteModule, cellLevelModule, axisofrotationTorqueTube,\ torqueTube, fixedortracking, cumulativesky, timestampRangeSimulation,\ @@ -120,6 +139,41 @@ def read_valuesfromGUI(): try: inputvariablefile = entry_inputvariablefile.get() except: inputvariablefile = os.path.join('data','default.ini') + # Initializing + daydateSimulation = False + timestampRangeSimulation = False + if rb_fixedortracking.get() == 0: + fixedortracking=False # False, fixed. Fixed, Cumulative Sky Yearly + cumulativesky = True + if rb_fixedortracking.get() == 1: + fixedortracking=False # True, 'tracking' Fixed, Cumulative Sky with Start/End + cumulativesky = True + daydateSimulation = True #TODO: check this out. new 8/20/19 + #timestampRangeSimulation = True + if rb_fixedortracking.get() == 2: + fixedortracking=False # True, 'tracking' Fixed, Hourly with Start/End times + cumulativesky = False + daydateSimulation = True + if rb_fixedortracking.get() == 3: + fixedortracking=False # True, 'tracking' Fixed, Hourly for the Whole Year + cumulativesky = False + if rb_fixedortracking.get() == 4: + fixedortracking=True # True, 'tracking' Tracking, Cumulative Sky Yearly + cumulativesky = True + if rb_fixedortracking.get() == 5: + fixedortracking=True # True, 'tracking' Tracking, Hourly for a Day + cumulativesky = False + daydateSimulation = True + _set_daily_endtimes() + if rb_fixedortracking.get() == 6: + fixedortracking=True # True, 'tracking' Tracking, Hourly with Start/End times + cumulativesky = False + daydateSimulation = True #TODO: check this out. new 8/20/19 + #timestampRangeSimulation = True + if rb_fixedortracking.get() == 7: + fixedortracking=True # True, 'tracking' Tracking, Hourly for the Whole Year + cumulativesky = False + # TODO: Improve validation method. try: albedo = entry_albedo.get() #this can either be a number or a material string except: print("ALBEDO: Please type in a number!") @@ -196,6 +250,7 @@ def read_valuesfromGUI(): if len(entry_zgap.get()) != 0: zgap = float(entry_zgap.get()) + if len(entry_enddate_day.get()) != 0: enddate_day = int(entry_enddate_day.get()) if len(entry_enddate_hour.get()) != 0: @@ -218,39 +273,9 @@ def read_valuesfromGUI(): if rb_cellLevelModule.get() == 0: cellLevelModule=False if rb_cellLevelModule.get() == 1: cellLevelModule=True - + - # Initializing - daydateSimulation = False - timestampRangeSimulation = False - if rb_fixedortracking.get() == 0: - fixedortracking=False # False, fixed - cumulativesky = True - if rb_fixedortracking.get() == 1: - fixedortracking=False # True, 'tracking' - cumulativesky = True - timestampRangeSimulation = True - if rb_fixedortracking.get() == 2: - fixedortracking=False # True, 'tracking' - cumulativesky = False - timestampRangeSimulation = True - if rb_fixedortracking.get() == 3: - fixedortracking=False # True, 'tracking' - cumulativesky = False - if rb_fixedortracking.get() == 4: - fixedortracking=True # True, 'tracking' - cumulativesky = True - if rb_fixedortracking.get() == 5: - fixedortracking=True # True, 'tracking' - cumulativesky = False - daydateSimulation = True - if rb_fixedortracking.get() == 6: - fixedortracking=True # True, 'tracking' - cumulativesky = False - timestampRangeSimulation = True - if rb_fixedortracking.get() == 7: - fixedortracking=True # True, 'tracking' - cumulativesky = False + if rb_GCRorPitch.get() == 0: GCRorPitch='gcr' if rb_GCRorPitch.get() == 1: GCRorPitch='pitch' @@ -269,8 +294,8 @@ def read_valuesfromGUI(): if rb_tubeType.get() == 2: tubeType='hex' if rb_tubeType.get() == 3: tubeType='oct' - if rb_weatherinputModule.get() == 0: weatherinputMode='True' # True reads EPW or TMY - if rb_weatherinputModule.get() == 1: weatherinputMode='False' # False reads epw + if rb_weatherinputModule.get() == 0: weatherinputMode='True' # True gets epw + if rb_weatherinputModule.get() == 1: weatherinputMode='False' # False reads epw or TMY #TODO: add validation for inputs depending on options selected @@ -317,9 +342,13 @@ def read_valuesfromGUI(): if xgap is not None: moduleParamsDict['xgap'] = xgap if ygap is not None: moduleParamsDict['ygap'] = ygap if zgap is not None: moduleParamsDict['zgap'] = zgap - if GCRorPitch is not None: sceneParamsDict['gcrorpitch'] = GCRorPitch - if gcr is not None: sceneParamsDict['gcr'] = gcr + if GCRorPitch == 'gcr' and gcr is not None: + sceneParamsDict['gcr'] = gcr + sceneParamsDict['pitch'] = None + elif pitch is not None: + sceneParamsDict['pitch'] = pitch + sceneParamsDict['gcr'] = None if pitch is not None: sceneParamsDict['pitch'] = pitch if albedo is not None: sceneParamsDict['albedo'] = albedo if nMods is not None: sceneParamsDict['nMods'] = nMods @@ -959,7 +988,7 @@ def setDefaults(): # MAIN CONTROL ################### - # Create teh widgets for the maincontrol_frame + # Create the widgets for the maincontrol_frame def selGetEPW(): getepwfile_label.config(state='normal') entry_getepwfileLat.config(state='normal') @@ -1163,9 +1192,10 @@ def tcTwo(): # Fixed, Hourly by Tiestamps: def tcThree(): selfixed() - tcTimestamps() + tcStartEndDate() + #tcTimestamps() - # Fixed, Hourly for hte whole Year: + # Fixed, Hourly for the whole Year: def tcFour(): selfixed() tcAll() @@ -1194,7 +1224,7 @@ def tcEight(): rb_fixedortracking=IntVar() rad1_fixedortracking = Radiobutton(simulationcontrol_frame, variable=rb_fixedortracking, indicatoron = 0, width = 50, text='Fixed, Cumulative Sky Yearly', value=0, command=tcOne) rad2_fixedortracking = Radiobutton(simulationcontrol_frame, variable=rb_fixedortracking, indicatoron = 0, width = 50, text='Fixed, Cumulative Sky with Start/End times', value=1, command=tcTwo) - rad3_fixedortracking = Radiobutton(simulationcontrol_frame, variable=rb_fixedortracking, indicatoron = 0, width = 50, text='Fixed, Hourly by Timestamps', value=2, command=tcThree) + rad3_fixedortracking = Radiobutton(simulationcontrol_frame, variable=rb_fixedortracking, indicatoron = 0, width = 50, text='Fixed, Hourly with Start/End times', value=2, command=tcThree) #text='Fixed, Hourly by Timestamps' rad4_fixedortracking = Radiobutton(simulationcontrol_frame, variable=rb_fixedortracking, indicatoron = 0, width = 50, text='Fixed, Hourly for the Whole Year', value=3, command=tcFour) rad5_fixedortracking = Radiobutton(simulationcontrol_frame, variable=rb_fixedortracking, indicatoron = 0, width = 50, text='Tracking, Cumulative Sky Yearly', value=4, command=tcFive) rad6_fixedortracking = Radiobutton(simulationcontrol_frame, variable=rb_fixedortracking, indicatoron = 0, width = 50, text='Tracking, Hourly for a Day', value=5, command=tcSix) @@ -1765,4 +1795,4 @@ def gui(): # If the script is run as a file, it needs to call gui(). if __name__ == '__main__': - gui() \ No newline at end of file + gui() diff --git a/bifacial_radiance/load.py b/bifacial_radiance/load.py index 35047b8c..90414f78 100644 --- a/bifacial_radiance/load.py +++ b/bifacial_radiance/load.py @@ -1,72 +1,64 @@ # -*- coding: utf-8 -*- """ -Created on Tue Feb 19 08:38:45 2019 - -@author: cdeline, sayala - -load.py - load bifacial_radiance results. Module to load and clean -bifacial_radiance irradiance result files, csv format, usually stored in RadianceScene\results folder. - -Introduced in bifacial_radiance v0.2.4 - -functions: - load_inputvariablesfile(inputfile) - loads a .py file which has all of the input variables required for a simulation. - Everything is loaded into a dictionary - - loadRadianceObj(savefile) - unpickle a RadianceObj saved with RadianceObj.save() - - resultsdf = read1Result(csvfile) - read in a csv file - Returns: resultsDF - - cleanResult(resultsDF, matchers=None) - replace irradiance values with NaN's when the scan intersects ground, sky, or anything in `matchers`. - Returns: resultsDF - - deepcleanResult(resultsDF, sensorsy, numpanels, azimuth, automatic=True) - - Returns: resultsDF - - loadTrackerDict(trackerdict, fileprefix=None) - load all csv files in a \results\ folder matching timestamps in trackerdict - Intended to be called from RadianceObj.loadTrackerDict() +Module providing routines for loading and cleaning results from bifacial_radiance. +Bifacial_radiance results are .csv format files stored in a results folder +autogenerated in the location where the RadianceObj was set to build its scene. +If no path was provided for the RadianceObj to build its scene, it defaults to +TEMP folder in bifacial_radiance \\ bifacial_radiance """ - - - + def load_inputvariablesfile(intputfile): - ''' - Description - ----------- - load_inputvariablesfile(inputfile) + """ Loads inputfile which must be in the bifacial_radiance directory, - and must be a *.py file with all the variables, and organizes the variables + and must be a ``.py`` file with all the variables, and organizes the variables into dictionaries that it returns Parameters ---------- - inputfile: string of a *.py file in the bifacial_radiance directory. + inputfile : str + String of a ``.py`` file in the bifacial_radiance directory. - Returns (Dictionaries) + Returns ------- - simulationParamsDict testfolder, weatherfile, getEPW, simulationname, - moduletype, rewritemodule, - rcellLevelmodule, axisofrotationtorquetube, - torqueTube, hpc, tracking, cumulativesky, - daydateSimulation, timestampRangeSimulation - sceneParamsDict: gcrorpitch, gcr, pitch, albedo, nMods, nRows, - hub_height, clearance_height, azimuth, hub_height, axis_Azimuth - timeControlParamsDict: hourstart, hourend, daystart, dayend, monthstart, monthend, - timestampstart, timestampend, - moduleParamsDict: numpanels, x, y, bifi, xgap, ygap, zgap - cellLevelModuleParamsDict: numcellsx, numcellsy, xcell, ycell, xcellgap, ycellgap - trackingParamsDict: backtrack, limit_angle,angle_delta - torquetubeParamsDict: diameter, tubetype, torqueTubeMaterial - analysisParamsDict: sensorsy, modWanted, rowWanted - ''' + simulationParamsDict : Dictionary + Dictionary containing the parameters for performing the simulation, + including simulation names, and types of sky, fixed or tracked systems: + ======================== ======= ============================= + variable type Description + ======================== ======= ============================= + testfolder str Path to testfolder + weatherfile str File (with path) to weatherfile + getEPW bool + simulationname str Name for simulation + moduletype str Module name as is / or will be defined in JSON + rewritemodule bool If moduletype exists in JSON, True will rewrite with new parameters + cellLevelmodule bool + axisofrotationtorquetube bool + torqueTube bool + hpc bool + tracking bool + cumulativesky bool + daydateSimulation bool + timestampRangeSimulation bool + ======================== ======= ============================= + sceneParamsDict : Dictionary + gcrorpitch, gcr, pitch, albedo, nMods, nRows, + hub_height, clearance_height, azimuth, hub_height, axis_Azimuth + timeControlParamsDict : Dictionary + hourstart, hourend, daystart, dayend, monthstart, monthend, + timestampstart, timestampend, + moduleParamsDict : Dictionary + numpanels, x, y, bifi, xgap, ygap, zgap + cellLevelModuleParamsDict : Dictionary + numcellsx, numcellsy, xcell, ycell, xcellgap, ycellgap + trackingParamsDict : Dictionary + backtrack, limit_angle,angle_delta + torquetubeParamsDict : Dictionary + diameter, tubetype, torqueTubeMaterial + analysisParamsDict : Dictionary + sensorsy, modWanted, rowWanted + """ import inputfile as ibf @@ -109,6 +101,7 @@ def load_inputvariablesfile(intputfile): trackingParamsDict = {'backtrack': ibf.backtrack, 'limit_angle': ibf.limit_angle, 'angle_delta': ibf.angle_delta} + # #TODO: Figure out how to return this optional return items. #cdeline: this isn't returned by the function ?? torquetubeParamsDict = {'diameter': ibf.diameter, 'tubetype': ibf.tubetype, 'torqueTubeMaterial': ibf.torqueTubeMaterial} @@ -127,16 +120,18 @@ def load_inputvariablesfile(intputfile): def loadRadianceObj(savefile=None): - ''' + """ Load the pickled radiance object for further use - usage: (once you're in the correct local directory) - demo = bifacial_radiance.loadRadianceObj(savefile) + Usage (once you're in the correct local directory):: + + demo = bifacial_radiance.loadRadianceObj(savefile) Parameters ---------- - savefile : optional savefile. Otherwise default to save.pickle + savefile : str + Optional savefile name. Otherwise default to `save.pickle` - ''' + """ import pickle if savefile is None: @@ -148,10 +143,22 @@ def loadRadianceObj(savefile=None): return loadObj def read1Result(selectfile): - ''' - load in bifacial_radiance.csv result file name `selectfile` - and return pandas dataframe resultsdf - ''' + """ + Loads in a bifacial_radiance results file ``.csv`` format, + and return a :py:class:`~pandas.DataFrame` + + Parameters + ---------- + selectfile : str + File name (with path if not in working folder) that has been produced by + bifacial_radiance. + + Returns + ------- + resultsDF : :py:class:`~pandas.DataFrame` + Dataframe with the bifacial_radiance .csv values read. + + """ import pandas as pd #resultsDict = pd.read_csv(os.path.join('results',selectfile)) @@ -162,17 +169,31 @@ def read1Result(selectfile): # End read1Result subroutine def cleanResult(resultsDF, matchers=None): - ''' - cleanResult(resultsDF, matchers=None) - check for 'sky' or 'tube' or 'pole' or 'ground in the front or back material - and substitute NaN in Wm2Front and Wm2Back + """ + Replace irradiance values with NaN's when the scan intersects ground, + sky, or anything in `matchers`. - matchers 3267 and 1540 is to get rid of inner-sides of the module. + Matchers are words in the dataframe like 'sky' or 'tube' + in the front or back material description column that + get substituted by NaN in Wm2Front and Wm2Back + There are default matchers established in this routine but other matchers + can be passed. + Default matchers: 'sky', 'tube', 'pole', 'ground', '3267', '1540'. + Matchers 3267 and 1540 is to get rid of inner-sides of the module. - Parameters: - resultsDF: pandas dataframe read from read1Result + Parameters + ---------- + resultsDF : :py:class:`~pandas.DataFrame` + DataFrame of results from bifacial_radiance, for example read + from :py:class:`~bifacial_radiance.load.read1Result` + + Returns + -------- + resultsDF : :py:class:`~pandas.DataFrame` + Updated resultsDF + + """ - ''' import numpy as np if matchers is None: @@ -189,26 +210,33 @@ def cleanResult(resultsDF, matchers=None): def loadTrackerDict(trackerdict, fileprefix=None): - ''' - Load a trackerdict by reading all files in the \results\ directory. - fileprefix is used to select only certain matching files in \results\ + """ + Load a trackerdict by reading all files in the `\\results` directory. + fileprefix is used to select only certain matching files in `\\results` - It will then save the Wm2Back, Wm2Front and backRatio by reading in all valid files in the - \results\ directory. Note: it will match any file ending in '_key.csv' + It will then save the `Wm2Back`, `Wm2Front` and `backRatio` by reading in all valid files in the + `\\results` directory. Note: it will match any file ending in `_key.csv` Parameters - -------------- - trackerdict: You need to pass in a valid trackerdict with correct keys from RadianceObj.set1axis() - fileprefix: (None): optional parameter to specify the initial part of the savefile prior to '_key.csv' + ---------- + trackerdict : + You need to pass in a valid trackerdict with correct keys from + :py:class:`~bifacial_radiance.RadianceObj.set1axis` + fileprefix : str + Optional parameter to specify the initial part of the savefile prior + to '_key.csv' Returns - ------------- - trackerdict: dictionary with additional ['Wm2Back'], ['Wm2Front'], ['backRatio'] - totaldict: totalized dictionary with ['Wm2Back'], ['Wm2Front']. - Also ['numfiles'] (number of csv files loaded) and - ['finalkey'] (last index file in directory) + ------- + trackerdict : Dictionary + Dictionary with additional keys ``Wm2Back``, ``Wm2Front``, ``backRatio`` + totaldict : Dictionary + totalized dictionary with ``Wm2Back``, ``Wm2Front``. + Also ``numfiles`` (number of csv files loaded) and + ``finalkey`` (last index file in directory) - ''' + """ + import re, os import numpy as np @@ -253,65 +281,93 @@ def loadTrackerDict(trackerdict, fileprefix=None): #end loadTrackerDict subroutine. set demo.Wm2Front = totaldict.Wm2Front. demo.Wm2Back = totaldict.Wm2Back -def exportTrackerDict(trackerdict, savefile, reindex): - ''' - save a TrackerDict output as a csv file. - - Inputs: - trackerdict: the tracker dictionary to save - savefile: path to .csv save file location - reindex: boolean to resample time index - - ''' - from pandas import DataFrame as df - import numpy as np - import pandas as pd - - # convert trackerdict into dataframe - d = df.from_dict(trackerdict,orient='index',columns=['dhi','ghi','Wm2Back','Wm2Front','theta','surf_tilt','surf_azm','ground_clearance']) - d['Wm2BackAvg'] = [np.nanmean(i) for i in d['Wm2Back']] - d['Wm2FrontAvg'] = [np.nanmean(i) for i in d['Wm2Front']] - d['BifiRatio'] = d['Wm2BackAvg'] / d['Wm2FrontAvg'] - - if reindex is True: # change to proper timestamp and interpolate to get 8760 output - d['measdatetime'] = d.index - d=d.set_index(pd.to_datetime(d['measdatetime'] , format='%m_%d_%H')) - d=d.resample('H').asfreq() +def _exportTrackerDict(trackerdict, savefile, reindex): + """ + Save a TrackerDict output as a ``.csv`` file. + + Parameters + ---------- + trackerdict : Dictionary + The tracker dictionary to save + savefile : str + Path to .csv save file location + reindex : bool + Boolean indicating if trackerdict should be resampled to include + all 8760 hours in the year (even those when the sun is not up and + irradiance results is empty). + + """ + from pandas import DataFrame as df + import numpy as np + import pandas as pd + + # convert trackerdict into dataframe + d = df.from_dict(trackerdict,orient='index',columns=['dhi','ghi','Wm2Back','Wm2Front','theta','surf_tilt','surf_azm','ground_clearance']) + d['Wm2BackAvg'] = [np.nanmean(i) for i in d['Wm2Back']] + d['Wm2FrontAvg'] = [np.nanmean(i) for i in d['Wm2Front']] + d['BifiRatio'] = d['Wm2BackAvg'] / d['Wm2FrontAvg'] + + if reindex is True: # change to proper timestamp and interpolate to get 8760 output + d['measdatetime'] = d.index + d=d.set_index(pd.to_datetime(d['measdatetime'] , format='%m_%d_%H')) + d=d.resample('H').asfreq() - d.to_csv(savefile) + d.to_csv(savefile) def deepcleanResult(resultsDict, sensorsy, numpanels, automatic=True): - ''' - cleanResults(resultsDict, sensorsy, numpanels, azimuth) - @author: SAyala - - cleans results read by read1Result specifically for 1 UP and 2UP configurations in v0.2.4 + """ + Cleans results read by read1Result specifically for 1 UP and 2UP configurations in v0.2.4 Asks user to select material of the module (usually the one with the most results) - and removes sky, ground, and other materials (side of module, for exmaple) - - TODO: add automatization of panel select. + and removes sky, ground, and other materials (side of module, for example) - PARAMETERS + Parameters ----------- - sensorsy For the interpolation routine. Can be more than original sensory or same value. - numpanels 1 or 2 - azimuth of the tracker for the results generated. So that it knows if sensors - should be flipped or not. Particular crucial for 2 UP configurations. - automatic Automaticatlly detects module and ignores Ground, torque tube and sky values. If set to off, user gets queried about the right surfaces. + sensorsy : int + For the interpolation routine. Can be more than original sensory or same value. + numpanels : int + Options are 1 or 2 panels for this function. + automatic : bool + Default True. Automaticatlly detects module and ignores Ground, torque tube and + sky values. If set to off, user gets queried about the right surfaces. Returns ------- - Frontresults, Backresults; dataframe with only values of the material selected, - length is the number of sensors desired. + Frontresults : :py:class:`~pandas.DataFrame` + Dataframe with only front-irradiance values for the module material selected, + length is the number of sensors desired. + Backresults : :py:class:`~pandas.DataFrame` + Dataframe with only rear-irradiance values for the module material selected, + length is the number of sensors desired. + """ + + # #TODO: add automatization of panel select. - ''' import numpy as np - from scipy.interpolate import interp1d + #from scipy.interpolate import interp1d fronttypes = resultsDict.groupby('mattype').count() backtypes = resultsDict.groupby('rearMat').count() + def interp_sub(panelDict, sensorsy, frontbackkey): + """ + Parameters + ----------- + panelDict : Dictionary + resultsDict filtered for either panelA or panelB from above. + sensorsy : int + Number of y sensors to interpolate to + frontbackkey : str + Either 'Wm2Front' or 'Wm2Back' + + """ + + x_0 = np.linspace(0, len(panelDict)-1, len(panelDict)) + x_i = np.linspace(0, len(panelDict)-1, int(sensorsy)) + #f_linear = interp1d(x_0, panelB['Wm2Front']) + interp_out = np.interp(x_i, x_0, panelDict[frontbackkey]) + return interp_out + if numpanels == 2: if automatic == True: @@ -352,20 +408,14 @@ def deepcleanResult(resultsDict, sensorsy, numpanels, automatic=True): # Interpolating to original or givne number of sensors (so all hours results match after deleting wrong sensors). # This could be a sub-function but, hmm.. - x_0 = np.linspace(0, len(panelB)-1, len(panelB)) - x_i = np.linspace(0, len(panelB)-1, int(sensorsy/2)) - f_linear = interp1d(x_0, panelB['Wm2Front']) - panelB_front = f_linear(x_i) - f_linear = interp1d(x_0, panelB['Wm2Back']) - panelB_back = f_linear(x_i) + - # Interpolating to 200 because - x_0 = np.linspace(0, len(panelA)-1, len(panelA)) - x_i = np.linspace(0, len(panelA)-1, int(sensorsy/2)) - f_linear = interp1d(x_0, panelA['Wm2Front']) - panelA_front = f_linear(x_i) - f_linear = interp1d(x_0, panelA['Wm2Back']) - panelA_back = f_linear(x_i) + panelB_front = interp_sub(panelB, sensorsy/2, 'Wm2Front') + panelB_back = interp_sub(panelB, sensorsy/2, 'Wm2Back') + panelA_front = interp_sub(panelA, sensorsy/2, 'Wm2Front') + panelA_back = interp_sub(panelA, sensorsy/2, 'Wm2Back') + + Frontresults=np.append(panelB_front,panelA_front) Backresults=np.append(panelB_back,panelA_back) @@ -373,9 +423,8 @@ def deepcleanResult(resultsDict, sensorsy, numpanels, automatic=True): else: # ONLY ONE MODULE if automatic == True: - panBfrontmat = resultsDict[resultsDict['mattype'].str.contains('a0.PVmodule.6457')] - panelB = panBfrontmat[panBfrontmat['rearMat'].str.contains('a0.PVmodule.2310')] # checks rear mat is also panel B only. - + panBfrontmat = resultsDict[resultsDict['mattype'].str.contains('.6457')] + panelB = panBfrontmat[panBfrontmat['rearMat'].str.contains('.2310')] # checks rear mat is also panel B only. else: @@ -398,6 +447,7 @@ def deepcleanResult(resultsDict, sensorsy, numpanels, automatic=True): #panelB = test[(test.mattype == 'a10.3.a0.PVmodule.6457') & (test.rearMat == 'a10.3.a0.PVmodule.2310')] + """ # Interpolating to 200 because x_0 = np.linspace(0, len(panelB)-1, len(panelB)) x_i = np.linspace(0, len(panelB)-1, sensorsy) @@ -405,33 +455,40 @@ def deepcleanResult(resultsDict, sensorsy, numpanels, automatic=True): panelB_front = f_linear(x_i) f_linear = interp1d(x_0, panelB['Wm2Back']) panelB_back = f_linear(x_i) - + Frontresults=panelB_front Backresults=panelB_back - + """ + Frontresults=interp_sub(panelB,sensorsy,'Wm2Front') + Backresults=interp_sub(panelB,sensorsy,'Wm2Back') + return Frontresults, Backresults; # End Deep clean Result subroutine. def readconfigurationinputfile(inifile=None): """ - readconfigurationinputfile(inifile=None) - - input: inifile (string): .ini filename to read in + Function to read configurationinput file for a bifacial_radiance simulation. - returns: simulationParamsDict, - sceneParamsDict, - timeControlParamsDict, - moduleParamsDict, - trackingParamsDict, - torquetubeParamsDict, - analysisParamsDict, - cellLevelModuleParamsDict; - - @author: sayala + Parameters + ---------- + input : str + Filename with extension .ini to read in - #TODO: check if modulename exists on jason and rewrite is set to false, then - don't save moduleParamsDict? Discuss. + Returns + ------- + simulationParamsDict : Dictionary + sceneParamsDict : Dictionary + timeControlParamsDict : Dictionary + moduleParamsDict : Dictionary + trackingParamsDict : Dictionary + torquetubeParamsDict : Dictionary + analysisParamsDict : Dictionary + cellLevelModuleParamsDict : Dictionary """ + + ## #TODO: check if modulename exists on jason and rewrite is set to false, then + #don't save moduleParamsDict? Discuss. + import configparser import os @@ -450,7 +507,7 @@ def boolConvert(d): config = configparser.ConfigParser() config.optionxform = str - config.read(inifile) + config.read_file(open(inifile, 'r')) confdict = {section: dict(config.items(section)) for section in config.sections()} @@ -468,9 +525,9 @@ def boolConvert(d): if simulationParamsDict['timestampRangeSimulation'] or simulationParamsDict['daydateSimulation']: if config.has_section("timeControlParamsDict"): timeControlParamsDict2 = boolConvert(confdict['timeControlParamsDict']) - timeControlParamsDict={} # saving a main dictionary wiht only relevant options. + timeControlParamsDict={} # saving a main dictionary with only relevant options. else: - print("Mising timeControlParamsDict for simulation options specified! Breaking") + print("Missing timeControlParamsDict for simulation options specified! Breaking") # break; if simulationParamsDict['getEPW']: @@ -588,6 +645,51 @@ def boolConvert(d): moduleParamsDict['y'] = 1.95 print("Load Warning: moduleParamsDict['y'] not specified, setting to default value: %s" % moduleParamsDict['y'] ) + # CDELINE - new attempt 8/20/19 + if simulationParamsDict['daydateSimulation']: + try: + timeControlParamsDict['DayEnd']=int(timeControlParamsDict2['DayEnd']) + timeControlParamsDict['DayStart']=int(timeControlParamsDict2['DayStart']) + timeControlParamsDict['MonthEnd']=int(timeControlParamsDict2['MonthEnd']) + timeControlParamsDict['MonthStart']=int(timeControlParamsDict2['MonthStart']) + timeControlParamsDict['HourEnd']=int(timeControlParamsDict2['HourEnd']) + timeControlParamsDict['HourStart']=int(timeControlParamsDict2['HourStart']) + simulationParamsDict['daydateSimulation'] = True + + if simulationParamsDict['timestampRangeSimulation']: + print("Load Warning: timestampRangeSimulation and daydatesimulation both set to True.",\ + "Doing daydateSimulation and setting timestampRangeSimulation to False") + simulationParamsDict['timestampRangeSimulation'] = False + + except: + try: + timeControlParamsDict['DayStart']=int(timeControlParamsDict2['DayStart']) + timeControlParamsDict['MonthStart']=int(timeControlParamsDict2['MonthStart']) + print("Load Warning: timecontrolParamsDict hourend / hourstart is wrong/nan",\ + "but since valid start day and month values were passed, switching simulation to",\ + "daydatesimulation = True, timestampRangeSimulation = False") + simulationParamsDict['daydateSimulation']=True + simulationParamsDict['timestampRangeSimulation']=False + except: + print("Load Warning: no valid day, month and hour passed for simulation.",\ + "setting cumulative to True, and daydatesimulation and ",\ + "timestampRangeSimulation to False") + simulationParamsDict['cumulativeSky']=True + simulationParamsDict['daydateSimulation']=False + simulationParamsDict['timestampRangeSimulation']=False + + if simulationParamsDict['timestampRangeSimulation']: + try: + timeControlParamsDict['timeindexstart']=int(timeControlParamsDict2['timeindexstart']) + timeControlParamsDict['timeindexend']=int(timeControlParamsDict2['timeindexend']) + except: + timeControlParamsDict['timeindexstart']=4020 + timeControlParamsDict['timeindexend']=4021 + print("Load warning: timeindex for start or end are wrong/nan. ", \ + "setting to default %s to % s" % (timeControlParamsDict['timeindexstart'], timeControlParamsDict['timeindexend']) ) + + + ''' #CDELINE: Original version. Replaced with above 8/20/19 if simulationParamsDict['tracking']: if simulationParamsDict['timestampRangeSimulation']: try: @@ -649,7 +751,7 @@ def boolConvert(d): timeControlParamsDict['timeindexend']=4024 print("Load warning: timeindex for start or end are wrong/nan. ", \ "setting to default %s to % s" % (timeControlParamsDict['timeindexstart'], timeControlParamsDict['timeindexend']) ) - + ''' #NEEDED sceneParamsDict parameters sceneParamsDict={} try: @@ -770,9 +872,32 @@ def boolConvert(d): def savedictionariestoConfigurationIniFile(simulationParamsDict, sceneParamsDict, timeControlParamsDict=None, moduleParamsDict=None, trackingParamsDict=None, torquetubeParamsDict=None, analysisParamsDict=None, cellLevelModuleParamsDict=None, inifilename=None): - ''' - inifilename = 'example.ini' - ''' + """ + Saves dictionaries from working memory into a Configuration File + with extension format .ini. + + Parameters + ---------- + simulationParamsDict + sceneParamsDict + timeControlParamsDict + Default None + moduleParamsDict + Default None + trackingParamsDict + Default None + torquetubeParamsDict + Default None + analysisParamsDict + Default None, + cellLevelModuleParamsDict + Default None + + Returns + ------- + Writes output into inifilename passed (default if inifilename=None is + 'example.ini') + """ import configparser @@ -815,9 +940,10 @@ def savedictionariestoConfigurationIniFile(simulationParamsDict, sceneParamsDict class Params(): """ - - model configuration parameters. Including the following: + Model configuration parameters. Including the following: + Parameters + ---------- simulationParams testfolder, weatherfile, getEPW, simulationname, moduletype, rewritemodule, rcellLevelmodule, axisofrotationtorquetube, @@ -834,7 +960,8 @@ class Params(): analysisParams: sensorsy, modWanted, rowWanted """ - # cdeline 5/9/19: new class to try to make some sense of these model parameters? + # #DocumentationCheck : add to updates + # cdeline 5/9/19: new class to try to make some sense of these model parameters? def __init__(self, simulationParams=None, sceneParams=None, @@ -859,4 +986,4 @@ def unpack(self): self.trackingParams, \ self.torquetubeParams, \ self.analysisParams, \ - self.cellLevelModuleParams \ No newline at end of file + self.cellLevelModuleParams diff --git a/bifacial_radiance/main.py b/bifacial_radiance/main.py index 5ad88592..35d0f229 100644 --- a/bifacial_radiance/main.py +++ b/bifacial_radiance/main.py @@ -1,7 +1,7 @@ #!/usr/bin/env python from __future__ import division # avoid integer division issues. from __future__ import absolute_import # this module uses absolute imports -''' +""" @author: cdeline bifacial_radiance.py - module to develop radiance bifacial scenes, including gendaylit and gencumulativesky @@ -50,61 +50,17 @@ AnalysisObj: Analysis class for plotting and reporting -Revision history -0.3.0: New GUI. Modelchains implemented. Dictionaries implemented as inputs - to most functions. cell Level model capability. Axis of rotation torque - tube possible. clerance_height and hub_height distinction. New internal - Geometry handling. New/improved sensor locations. Multiple Scene object - capability for fixed scenes. HPC friendly code. -0.2.4: Module orientation deprecated. Py36 and cross-platform code compliance - implemented. Modified gendaylit to be based on sun positions by default. - More torquetube options added (round, square, hexagonal and octagonal - profiles), custom spacing between modules in a row added, included - accuracy input option for 1-axis scans, updated falsecolor routine, - module-select bug and module scan bug fixed, updates to pytests. - Update to sensor position on 1axistracking. -0.2.3: arbitrary length and position of module scans in makeScene. - Torquetube option to makeModule. New gendaylit1axis and hourly - makeOct1axis, analysis1axis -0.2.2: Negative 1 hour offset to TMY file inputs -0.2.1: Allow tmy3 input files. Use a different EPW file reader. -0.2.0: Critical 1-axis tracking update to fix geometry issues that were - over-predicting 1-axis results -0.1.1: Allow southern latitudes -0.1.0: 1-axis bug fix and validation vs PVSyst and ViewFactor model -0.0.5: 1-axis tracking draft -0.0.4: Include configuration file module.json and custom module configuration -0.0.3: Arbitrary NxR number of modules and rows for SceneObj -0.0.2: Adjustable azimuth angle other than 180 -0.0.1: Initial stable release -''' +""" +import logging +logging.basicConfig() +LOGGER = logging.getLogger(__name__) +LOGGER.setLevel(logging.DEBUG) + import os, datetime, sys from subprocess import Popen, PIPE # replacement for os.system() -import matplotlib.pyplot as plt import pandas as pd -import numpy as np #already imported with above pylab magic -#from scipy.interpolate import * -#from IPython.display import Image - -#import shlex -from time import sleep -#from pathlib import Path - -#from bifacial_radiance.config import * -from bifacial_radiance.readepw import readepw # epw file reader from pvlib development forums #module load format -from bifacial_radiance.load import loadTrackerDict -import bifacial_radiance.modelchain -''' -if __name__ == "__main__": #in case this is run as a script not a module. - from readepw import readepw - from load import loadTrackerDict - # from config import * # Preloads sample values for simulations. - -else: # module imported or loaded normally - from bifacial_radiance.readepw import readepw # epw file reader from pvlib development forums #module load format - from bifacial_radiance.load import loadTrackerDict - # from bifacial_radiance.config import * # Preloads sample values for simulations. -''' +import numpy as np + # Mutual parameters across all processes #daydate=sys.argv[1] @@ -114,11 +70,11 @@ #DATA_PATH = os.path.abspath(pkg_resources.resource_filename('bifacial_radiance', 'data/') ) DATA_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), 'data')) -def _findme(lst, a): #find string match in a list. found this nifty script on stackexchange +def _findme(lst, a): #find string match in a list. script from stackexchange return [i for i, x in enumerate(lst) if x == a] -def _normRGB(r, g, b): #normalize by weight of each color for human vision sensitivity +def _normRGB(r, g, b): #normalize by each color for human vision sensitivity return r*0.216+g*0.7152+b*0.0722 def _popen(cmd, data_in, data_out=PIPE): @@ -128,8 +84,8 @@ def _popen(cmd, data_in, data_out=PIPE): usage: pass to process and return results based on rgbeimage.py (Thomas Bleicher 2010) """ - cmd = str(cmd) # get's rid of unicode oddities - #p = Popen(shlex.split(cmd), bufsize=-1, stdin=PIPE, stdout=data_out, stderr=PIPE) + cmd = str(cmd) # gets rid of unicode oddities + p = Popen(cmd, bufsize=-1, stdin=PIPE, stdout=data_out, stderr=PIPE, shell=True) #shell=True required for Linux? quick fix, but may be security concern data, err = p.communicate(data_in) #if err: @@ -169,28 +125,26 @@ def _interactive_directory(title=None): class RadianceObj: - ''' - RadianceObj: top level class to work on radiance objects, keep track of filenames, - sky values, PV module configuration etc. + """ + The RadianceObj top level class is used to work on radiance objects, + keep track of filenames, sky values, PV module configuration, etc. + Parameters + ---------- + name : text to append to output files + filelist : list of Radiance files to create oconv + nowstr : current date/time string + path : working directory with Radiance materials and objects - values: - name : text to append to output files - filelist : list of Radiance files to create oconv - nowstr : current date/time string - path : working directory with Radiance materials and objects - TODO: populate this more - functions: - __init__ : initialize the object - _setPath : change the working directory - TODO: populate this more + Methods + ------- + __init__ : initialize the object + _setPath : change the working directory - ''' + """ def __init__(self, name=None, path=None): ''' - Description - ----------- initialize RadianceObj with path of Radiance materials and objects, as well as a basename to append to @@ -215,11 +169,14 @@ def __init__(self, name=None, path=None): self.octfile = [] #octfile name for analysis self.Wm2Front = 0 # cumulative tabulation of front W/m2 self.Wm2Back = 0 # cumulative tabulation of rear W/m2 + self.backRatio = 0 # ratio of rear / front Wm2 + self.nMods = None # number of modules per row + self.nRows = None # number of rows per scene now = datetime.datetime.now() self.nowstr = str(now.date())+'_'+str(now.hour)+str(now.minute)+str(now.second) - ''' DEFAULTS ''' + # DEFAULTS if name is None: self.name = self.nowstr # set default filename for output files @@ -236,17 +193,19 @@ def __init__(self, name=None, path=None): self.materialfiles = self.returnMaterialFiles('materials') def _setPath(self, path): - ''' + """ setPath - move path and working directory - ''' - self.path = path + """ + self.path = os.path.abspath(path) print('path = '+ path) try: os.chdir(self.path) - except WindowsError: - print('Path doesn''t exist: %s' % (path)) + except OSError as exc: + LOGGER.error('Path doesn''t exist: %s' % (path)) + LOGGER.exception(exc) + raise(exc) # check for path in the new Radiance directory: def _checkPath(path): # create the file structure if it doesn't exist @@ -276,21 +235,25 @@ def _checkPath(path): # create the file structure if it doesn't exist '-vu 0 0 1 -vh 45 -vv 45 -vo 0 -va 0 -vs 0 -vl 0') def getfilelist(self): - ''' return concat of matfiles, radfiles and skyfiles - ''' + """ + Return concat of matfiles, radfiles and skyfiles + """ return self.materialfiles + self.skyfiles + self.radfiles def save(self, savefile=None): - ''' + """ Pickle the radiance object for further use. Very basic operation - not much use right now. Parameters ---------- - savefile : optional savefile. Otherwise default to save.pickle + savefile : str + Optional savefile name, with .pickle extension. + Otherwise default to save.pickle - ''' + """ + import pickle if savefile is None: @@ -302,16 +265,22 @@ def save(self, savefile=None): def exportTrackerDict(self, trackerdict=None, savefile=None, reindex=None): - ''' - use bifacial_radiance.load.exportTrackerDict to save a + """ + Use :py:func:`~bifacial_radiance.load._exportTrackerDict` to save a TrackerDict output as a csv file. - Inputs: - trackerdict: the tracker dictionary to save - savefile: path to .csv save file location - reindex: boolean to change - - ''' + Parameters + ---------- + trackerdict + The tracker dictionary to save + savefile : str + path to .csv save file location + reindex : bool + True saves the trackerdict in TMY format, including rows for hours + where there is no sun/irradiance results (empty) + + """ + import bifacial_radiance.load if trackerdict is None: @@ -328,19 +297,29 @@ def exportTrackerDict(self, trackerdict=None, else: reindex = True - bifacial_radiance.load.exportTrackerDict(trackerdict, + if self.cumulativesky is True and reindex is True: + # don't re-index for cumulativesky, + # which has angles for index + print ("\n Warning: For cumulativesky simulations, exporting the TrackerDict requires reindex = False. Setting reindex = False and proceeding") + reindex = False + + bifacial_radiance.load._exportTrackerDict(trackerdict, savefile, - reindex - ) + reindex) def loadtrackerdict(self, trackerdict=None, fileprefix=None): - ''' - loadtrackerdict(trackerdict=None, fileprefix=None) - Use bifacial_radiance.load._loadtrackerdict to browse the results directory - and load back any results saved in there. + """ + Use :py:class:`bifacial_radiance.load._loadtrackerdict` + to browse the results directory and load back any results saved in there. - ''' + Parameters + ---------- + trackerdict + fileprefix : str + + """ + from bifacial_radiance.load import loadTrackerDict if trackerdict is None: trackerdict = self.trackerdict (trackerdict, totaldict) = loadTrackerDict(trackerdict, fileprefix) @@ -348,33 +327,37 @@ def loadtrackerdict(self, trackerdict=None, fileprefix=None): self.Wm2Back = totaldict['Wm2Back'] def returnOctFiles(self): - ''' - return files in the root directory with .oct extension + """ + Return files in the root directory with `.oct` extension Returns ------- - oct_files : list of .oct files - - ''' + oct_files : list + List of .oct files + + """ oct_files = [f for f in os.listdir(self.path) if f.endswith('.oct')] #self.oct_files = oct_files return oct_files def returnMaterialFiles(self, material_path=None): - ''' - return files in the Materials directory with .rad extension + """ + Return files in the Materials directory with .rad extension appends materials files to the oconv file list Parameters ---------- - material_path - optional parameter to point to a specific materials directory. - otherwise /materials/ is default + material_path : str + Optional parameter to point to a specific materials directory. + otherwise /materials/ is default Returns ------- - material_files : list of .rad files + material_files : list + List of .rad files - ''' + """ + if material_path is None: material_path = 'materials' @@ -386,8 +369,28 @@ def returnMaterialFiles(self, material_path=None): return materialfilelist def setGround(self, material=None, material_file=None): - ''' use GroundObj constructor and return a ground object - ''' + """ + Use GroundObj constructor class and return a ground object + + Parameters + ------------ + material : numeric or str + If number between 0 and 1 is passed, albedo input is assumed and assigned. + If string is passed with the name of the material desired. e.g. 'litesoil', + properties are searched in `material_file`. + Default Material names to choose from: litesoil, concrete, white_EPDM, + beigeroof, beigeroof_lite, beigeroof_heavy, black, asphalt + material_file : str + Filename of the material information. Default `ground.rad` + + Returns + ------- + self.ground : tuple + self.ground.normval : numeric + Normalized color value + self.gorund.ReflAvg : numeric + Average reflectance + """ ground_data = GroundObj(material, material_file) if material is not None: @@ -396,19 +399,27 @@ def setGround(self, material=None, material_file=None): self.ground = None def getEPW(self, lat=None, lon=None, GetAll=False): - ''' - Subroutine to download nearest epw files available into the directory \EPWs\ - - input parameters: - lat, lon: decimal values to find closest EPW file. - GetAll: (boolean) download all available files. - Note that no epw file will be loaded into memory - - based on github/aahoo - **note that verify=false is required to operate within NREL's network. - to avoid annoying warnings, insecurerequestwarning is disabled - currently this function is not working within NREL's network. annoying! - ''' + """ + Subroutine to download nearest epw files to latitude and longitude provided, + into the directory \EPWs\ + based on github/aahoo. + + .. warning:: + verify=false is required to operate within NREL's network. + to avoid annoying warnings, insecurerequestwarning is disabled + currently this function is not working within NREL's network. annoying! + + Parameters + ---------- + lat : decimal + Used to find closest EPW file. + lon : decimal + Longitude value to find closest EPW file. + GetAll : boolean + Download all available files. Note that no epw file will be loaded into memory + + + """ import requests, re from requests.packages.urllib3.exceptions import InsecureRequestWarning @@ -491,11 +502,25 @@ def getEPW_all(self): ''' - def readWeatherFile(self, weatherFile = None): - r''' - read either a EPW or a TMY file, calls the functions readTMY or readEPW + def readWeatherFile(self, weatherFile=None, starttime=None, + endtime=None, daydate=None): + """ + Read either a EPW or a TMY file, calls the functions + :py:class:`~bifacial_radiance.readTMY` or + :py:class:`~bifacial_radiance.readEPW` according to the weatherfile extention. - ''' + + Parameters + ---------- + weatherFile : str + File containing the weather information. TMY or EPW accepted. + + starttime : str + Limited start time option in 'MM_DD_HH' format + endtime : str + Limited end time option in 'MM_DD_HH' format + + """ if weatherFile is None: try: @@ -505,134 +530,210 @@ def readWeatherFile(self, weatherFile = None): 'on this system. Try installing X-Quartz and reloading') if weatherFile[-3:] == 'epw': - metdata = self.readEPW(weatherFile) + metdata = self.readEPW(weatherFile, starttime=starttime, + endtime=endtime, daydate=daydate) else: - metdata = self.readTMY(weatherFile) + metdata = self.readTMY(weatherFile, starttime=starttime, + endtime=endtime, daydate=daydate) return metdata - def readTMY(self, tmyfile=None): + def _saveTempTMY(self, tmydata, filename=None, starttime=None, endtime=None): ''' - use pvlib to read in a tmy3 file. + private function to save part or all of tmydata into /EPWs/ for use + in gencumsky -G mode and return truncated tmydata + + starttime: 'MM_DD_HH' string for limited time temp file + endtime: 'MM_DD_HH' string for limited time temp file + + returns: tmydata_truncated : subset of tmydata based on start & end + ''' + if filename is None: + filename = 'temp.csv' + if starttime is None: + starttime = '01_01_00' + if endtime is None: + endtime = '12_31_23' + # re-cast index with constant 2001 year to avoid datetime issues. + i = pd.to_datetime({'month':tmydata.index.month, + 'day':tmydata.index.day, + 'hour':tmydata.index.hour, + 'Year':2001*np.ones(tmydata.index.__len__())}) + i.index = i + startdt = pd.to_datetime('2001_'+starttime, format='%Y_%m_%d_%H') + enddt = pd.to_datetime('2001_'+endtime, format='%Y_%m_%d_%H') + + # create mask for when data should be kept. Otherwise set to 0 + indexmask = (i>=startdt) & (i<=enddt) + indexmask.index = tmydata.index + tmydata_trunc = tmydata[indexmask] + #Create new temp file for gencumsky-G: 8760 2-column csv GHI,DHI. + # Pad with zeros if len != 8760 + savedata = pd.DataFrame({'GHI':tmydata['GHI'], 'DHI':tmydata['DHI']}) + savedata[~indexmask]=0 + # switch to 2001 index + savedata.index =i + if savedata.__len__() != 8760: + savedata.loc[pd.to_datetime('2001-01-01 0:0:0')]=0 + savedata.loc[pd.to_datetime('2001-12-31 23:0:0')]=0 + savedata = savedata.resample('1h').asfreq(fill_value=0) + csvfile = os.path.join('EPWs', filename) + print('Saving file {}, # points: {}'.format(csvfile, savedata.__len__())) + savedata.to_csv(csvfile, index=False, header=False, sep=' ', columns=['GHI','DHI']) + self.epwfile = csvfile + + # return tmydata truncated by startdt and enddt + return tmydata_trunc + + + def readTMY(self, tmyfile=None, starttime=None, endtime=None, daydate=None): + ''' + use pvlib to read in a tmy3 file. Parameters ------------ tmyfile: filename of tmy3 to be read with pvlib.tmy.readtmy3 - + starttime: 'MM_DD_HH' string for limited time temp file + endtime: 'MM_DD_HH' string for limited time temp file + daydate : str for single day in 'MM/DD' or MM_DD format. + Returns ------- metdata - MetObj collected from TMY3 file ''' - import pvlib + import pvlib, re - if tmyfile is None: - try: - tmyfile = _interactive_load('Select TMY3 climate file') - except: - raise Exception('Interactive load failed. Tkinter not supported'+ - 'on this system. Try installing X-Quartz and reloading') - - (tmydata, metadata) = pvlib.tmy.readtmy3(filename=tmyfile) - # TODO: replace MetObj _init_ behavior with initTMY behavior - self.metdata = MetObj(tmydata, metadata) - #self.metdata = self.metdata.initTMY(tmydata,metadata) # initialize the MetObj using TMY instead of EPW - csvfile = os.path.join('EPWs', 'tmy3_temp.csv') #temporary filename with 2-column GHI,DHI data - #Create new temp csv file for gencumsky. write 8760 2-column csv: GHI,DHI - #save in 2-column GHI,DHI format for gencumulativesky -G - savedata = pd.DataFrame({'GHI':tmydata['GHI'], 'DHI':tmydata['DHI']}) - print('Saving file {}, # points: {}'.format(csvfile, savedata.__len__())) - savedata.to_csv(csvfile, index=False, header=False, sep=' ', columns=['GHI','DHI']) - self.epwfile = csvfile + if tmyfile is None: # use interactive picker in readWeatherFile() + metdata = self.readWeatherFile() + return metdata + #(tmydata, metadata) = pvlib.tmy.readtmy3(filename=tmyfile) #pvlib<=0.6 + (tmydata, metadata) = pvlib.iotools.tmy.read_tmy3(filename=tmyfile) + + if daydate is not None: + dd = re.split('_|/',daydate) + starttime = dd[0]+'_'+dd[1] + '_00' + endtime = dd[0]+'_'+dd[1] + '_23' + + tmydata_trunc = self._saveTempTMY(tmydata,'tmy3_temp.csv', + starttime=starttime, endtime=endtime) + if daydate is not None: # also remove GHI = 0 for HPC daydate call. + tmydata_trunc = tmydata_trunc[tmydata_trunc.GHI > 0] + + self.metdata = MetObj(tmydata_trunc, metadata) return self.metdata - def readEPW(self, epwfile=None, hpc=False, daydate=None, startindex=None, endindex=None): - ''' - use readepw from pvlib development forums - https://github.com/pvlib/pvlib-python/issues/261 - - rename tmy columns to match: DNI, DHI, GHI, DryBulb, Wspd + def readEPW(self, epwfile=None, hpc=False, starttime=None, endtime=None, daydate=None): + """ + Uses readepw from pvlib>0.6.1 but un-do -1hr offset and + rename columns to match TMY3: DNI, DHI, GHI, DryBulb, Wspd + + Parameters + ---------- + epwfile : str + Direction and filename of the epwfile. + If None, opens interactive loading window. + hpc : bool + Default False. DEPRECATED + daydate : str for single day in 'MM/DD' or MM_DD format. + starttime: 'MM_DD_HH' string for limited time temp file + endtime: 'MM_DD_HH' string for limited time temp file + + """ + + #from bifacial_radiance.readepw import readepw # from pvlib dev forum + import pvlib + import re + + if epwfile is None: # use interactive picker in readWeatherFile() + metdata = self.readWeatherFile() + return metdata ''' - #from readepw import readepw # epw file reader from pvlib development forums - if epwfile is None: - try: - epwfile = _interactive_load() - except: - raise Exception('Interactive load failed. Tkinter not '+ - 'supported on this system. Try installing X-Quartz and reloading') - if hpc is True and daydate is None: - print('Error: HPC computing requested, but Daydate is None in readEPW. Exiting.') + print('Error: HPC computing requested, but Daydate is None '+ + 'in readEPW. Exiting.') sys.exit() - - (tmydata, metadata) = readepw(epwfile) - # rename different field parameters to match output from pvlib.tmy.readtmy: DNI, DHI, DryBulb, Wspd - tmydata.rename(columns={'Direct normal radiation in Wh/m2':'DNI', - 'Diffuse horizontal radiation in Wh/m2':'DHI', - 'Dry bulb temperature in C':'DryBulb', - 'Wind speed in m/s':'Wspd', - 'Global horizontal radiation in Wh/m2':'GHI' + ''' + ''' + NOTE: In PVLib > 0.6.1 the new epw.read_epw() function reads in time + with a default -1 hour offset. This is not reflected in our existing + workflow, and must be investigated further. + ''' + #(tmydata, metadata) = readepw(epwfile) # + (tmydata, metadata) = pvlib.iotools.epw.read_epw(epwfile, coerce_year=2001) #pvlib>0.6.1 + #pvlib uses -1hr offset that needs to be un-done. Why did they do this? + tmydata.index = tmydata.index+pd.Timedelta(hours=1) + # rename different field parameters to match output from + # pvlib.tmy.readtmy: DNI, DHI, DryBulb, Wspd + tmydata.rename(columns={'dni':'DNI', + 'dhi':'DHI', + 'temp_air':'DryBulb', + 'wind_speed':'Wspd', + 'ghi':'GHI' }, inplace=True) - # Daydate will work with or without hpc function. Hpc only works when daydate is passed though. - if daydate is not None: - tmydata = tmydata[(tmydata['day'] == int(daydate[3:5])) & - (tmydata['month'] == int(daydate[0:2])) & - (tmydata['GHI'] > 0) - ] - print("restraining Tmydata by daydate") - - if startindex is not None and endindex is not None: - tmydata = tmydata[startindex:endindex] - print("restraining Tmydata by start and endindex") - - if daydate is not None and startindex is not None and endindex is not None: - print("TMYdata is restrained by daydate, startindex and endindex"+ - "at the same time, which might cause issues on data "+ - "selection. Please use one or the other method.") - - self.metdata = MetObj(tmydata, metadata) - - # copy the epwfile into the /EPWs/ directory in case it isn't in there already - if os.path.isabs(epwfile): - import shutil - dst = os.path.join(self.path, 'EPWs', os.path.split(epwfile)[1]) - try: - shutil.copyfile(epwfile, dst) #this may fail if the source and destination are the same - except shutil.SameFileError: - pass - self.epwfile = os.path.join('EPWs', os.path.split(epwfile)[1]) - - else: - self.epwfile = epwfile + # Hpc only works when daydate is passed through. Daydate gives single- + # day run option with zero GHI values removed. + if daydate is not None: + dd = re.split('_|/',daydate) + starttime = dd[0]+'_'+dd[1] + '_00' + endtime = dd[0]+'_'+dd[1] + '_23' + + tmydata_trunc = self._saveTempTMY(tmydata,'epw_temp.csv', + starttime=starttime, endtime=endtime) + if daydate is not None: # also remove GHI = 0 for HPC daydate call. + tmydata_trunc = tmydata_trunc[tmydata_trunc.GHI > 0] + + self.metdata = MetObj(tmydata_trunc, metadata) + return self.metdata - def getSingleTimestampTrackerAngle(self, metdata, timeindex, gcr=None, axis_azimuth=180, axis_tilt=0, limit_angle=60, backtrack=True): - r''' Helper function to calculate a tracker's angle for use with the + def getSingleTimestampTrackerAngle(self, metdata, timeindex, gcr=None, + axis_azimuth=180, axis_tilt=0, + limit_angle=60, backtrack=True): + """ + Helper function to calculate a tracker's angle for use with the fixed tilt routines of bifacial_radiance. + + Parameters + ---------- + metdata : :py:class:`~bifacial_radiance.MetObj` + Meterological object to set up geometry. Usually set automatically by + `bifacial_radiance` after running :py:class:`bifacial_radiance.readepw`. + Default = self.metdata + timeindex : int + Index between 0 to 8760 indicating hour to simulate. + gcr : float + Ground coverage ratio for calculation backtracking. Defualt [1.0/3.0] + axis_azimuth : float or int + Orientation axis of tracker torque tube. Default North-South (180 deg) + axis_tilt : float or int + Default 0. Axis tilt -- not implemented in sensors locations so it's pointless + at this release to change it. + limit_angle : float or int + Limit angle (+/-) of the 1-axis tracker in degrees. Default 45 + backtrack : boolean + Whether backtracking is enabled (default = True) + + """ ''' elev = metdata.elevation lat = metdata.latitude lon = metdata.longitude timestamp = metdata.datetime[timeindex] + ''' import pvlib - print ("getSingleTimestampTrackerAngle Warning: \n This function does not ",\ - "correct for the weather file half hour displacement",\ - "nor for sunrise/sunset sun position at the moment. IT just calculates the",\ - "Tracker position at the specific timestamp passed.") - #TODO: add weather file, sunrise/sunset correction. - - solpos = pvlib.irradiance.solarposition.get_solarposition(timestamp, lat, - lon, - elev) + solpos = metdata.solpos.iloc[timeindex] + sunzen = float(solpos.apparent_zenith) + sunaz = float(solpos.azimuth) # not substracting the 180 - trackingdata = pvlib.tracking.singleaxis(solpos['zenith'], solpos['azimuth'], + trackingdata = pvlib.tracking.singleaxis(sunzen, sunaz, axis_tilt, axis_azimuth, limit_angle, backtrack, gcr) @@ -643,26 +744,30 @@ def getSingleTimestampTrackerAngle(self, metdata, timeindex, gcr=None, axis_azim def gendaylit(self, metdata, timeindex, debug=False): - ''' - sets and returns sky information using gendaylit. - as of v0.2.4: Uses PVLIB for calculating the sun position angles instead of + """ + Sets and returns sky information using gendaylit. + Uses PVLIB for calculating the sun position angles instead of using Radiance internal sun position calculation (for that use gendaylit function) If material type is known, pass it in to get reflectance info. if material type isn't known, material_info.list is returned - Note - -W and -O1 option is used to create full spectrum analysis in units of Wm-2 + Parameters - ------------ - metdata: MetObj object with 8760 list of dni, dhi, ghi and location - timeindex: index from 0 to 8759 of EPW timestep - debug: boolean flag to print output of sky DHI and DNI + ---------- + metdata : ``MetObj`` + MetObj object with 8760 list of dni, dhi, ghi and location + timeindex : int + Index from 0 to 8759 of EPW timestep + debug : bool + Flag to print output of sky DHI and DNI Returns ------- - skyname: filename of sky in /skies/ directory. If errors exist, - such as DNI = 0 or sun below horizon, this skyname is None - - ''' + skyname : str + Sets as a self.skyname and returns filename of sky in /skies/ directory. + If errors exist, such as DNI = 0 or sun below horizon, this skyname is None + """ + if metdata is None: print('usage: gendaylit(metdata, timeindex) where metdata is'+ 'loaded from readEPW() or readTMY(). ' + @@ -699,10 +804,12 @@ def gendaylit(self, metdata, timeindex, debug=False): return None # We should already be filtering for elevation >0. But just in case... if sunalt <= 0: - sunalt = np.arcsin((ghi-dhi)/dni)*180/np.pi # reverse engineer elevation from ghi, dhi, dni - print('Warning: negative sun elevation passed:'+ - '{:0.2} with positive ghi. '.format(solpos.elevation)+ - 'Re-calculated sun elevation: {:0.2}'.format(sunalt)) + sunalt = np.arcsin((ghi-dhi)/(dni+.001))*180/np.pi # reverse engineer elevation from ghi, dhi, dni + print('Warning: negative sun elevation at '+ + '{}. '.format(metdata.datetime[timeindex])+ + 'Re-calculated elevation: {:0.2}'.format(sunalt)) + + # Note - -W and -O1 option is used to create full spectrum analysis in units of Wm-2 #" -L %s %s -g %s \n" %(dni/.0079, dhi/.0079, self.ground.ReflAvg) + \ skyStr = ("# start of sky definition for daylighting studies\n" + \ "# location name: " + str(locName) + " LAT: " + str(lat) @@ -736,26 +843,38 @@ def gendaylit(self, metdata, timeindex, debug=False): return skyname def gendaylit2manual(self, dni, dhi, sunalt, sunaz): - ''' - sets and returns sky information using gendaylit. + """ + Sets and returns sky information using gendaylit. Uses user-provided data for sun position and irradiance. - NOTE--> Currently half an hour offset is programed on timestamp, for wheater files. - if material type is known, pass it in to get - reflectance info. if material type isn't known, material_info.list is returned - Note - -W and -O1 option is used to create full spectrum analysis in units of Wm-2 + + .. warning:: + Currently half an hour offset is programed on timestamp, for wheater files. + Parameters ------------ - dni: dni value (int or float) - dhi: dhi value (int or float) - sunalt: sun altitude (degrees) (int or float) - sunaz: sun azimuth (degrees) (int or float) + dni: int or float + Direct Normal Irradiance (DNI) value, in W/m^2 + dhi : int or float + Diffuse Horizontal Irradiance (DHI) value, in W/m^2 + sunalt : int or float + Sun altitude (degrees) + sunaz : int or float + Sun azimuth (degrees) Returns ------- - skyname: filename of sky in /skies/ directory - - ''' - + skyname : string + Filename of sky in /skies/ directory + """ + + #TODO: + # #DocumentationCheck + # Is the half hour warning thing still Valid + # + # Documentation note: "if material type is known, pass it in to get + # reflectance info. if material type isn't known, material_info.list is returned" + # I don't think this function is doing that still? Maybe just delete this lines? + print('Sky generated with Gendaylit 2 MANUAL, with DNI: %0.1f, DHI: %0.1f' % (dni, dhi)) sky_path = 'skies' @@ -764,6 +883,7 @@ def gendaylit2manual(self, dni, dhi, sunalt, sunaz): self.skyfiles = [None] return None + # Note: -W and -O1 are used to create full spectrum analysis in units of Wm-2 #" -L %s %s -g %s \n" %(dni/.0079, dhi/.0079, self.ground.ReflAvg) + \ skyStr = ("# start of sky definition for daylighting studies\n" + \ "# Manual inputs of DNI, DHI, SunAlt and SunAZ into Gendaylit used \n" + \ @@ -793,35 +913,45 @@ def gendaylit2manual(self, dni, dhi, sunalt, sunaz): return skyname def genCumSky(self, epwfile=None, startdt=None, enddt=None, savefile=None): - ''' genCumSky - - skydome using gencumsky. note: gencumulativesky.exe is required to be installed, - which is not a standard radiance distribution. - You can find the program in the bifacial_radiance distribution directory - in \Lib\site-packages\bifacial_radiance\data - - TODO: error checking and auto-install of gencumulativesky.exe - - update 0.0.5: allow -G filetype option for support of 1-axis tracking + """ + Generate Skydome using gencumsky. + + .. warning:: + gencumulativesky.exe is required to be installed, + which is not a standard radiance distribution. + You can find the program in the bifacial_radiance distribution directory + in \Lib\site-packages\bifacial_radiance\data + + .. deprecated:: 0.3.2 + startdatetime and enddatetime inputs are deprecated and should not be used. + Use :func:`readWeatherFile(filename, starttime='MM_DD_HH', endtime='MM_DD_HH')` + to limit gencumsky simulations instead. Parameters ------------ - epwfile - filename of the .epw file to read in (-E mode) or 2-column csv (-G mode). - hour - tuple start, end hour of day. default (0,24) - startdatetime - datetime.datetime(Y,M,D,H,M,S) object. - Only M,D,H selected. default: (0,1,1,0) - enddatetime - datetime.datetime(Y,M,D,H,M,S) object. - Only M,D,H selected. default: (12,31,24,0) - savefile - - + epwfile : str + Filename of the .epw file to read in (-E mode) or 2-column csv (-G mode). + + startdatetime : datetime.datetime(Y,M,D,H,M,S) object + Only M,D,H selected. default: (0,1,1,0) + enddatetime : datetime.datetime(Y,M,D,H,M,S) object + Only M,D,H selected. default: (12,31,24,0) + savefile : string + If savefile is None, defaults to "cumulative" + Returns ------- - skyname - filename of the .rad file containing cumulativesky info - ''' + skyname : str + Filename of the .rad file containing cumulativesky info + """ + + # #TODO: error checking and auto-install of gencumulativesky.exe + import datetime + if epwfile is None: epwfile = self.epwfile if epwfile.endswith('epw'): - filetype = '-E' # EPW file input into gencumulativesky + filetype = '-E' # EPW file input into gencumulativesky *DEPRECATED else: filetype = '-G' # 2-column csv input: GHI,DHI @@ -892,54 +1022,66 @@ def genCumSky(self, epwfile=None, startdt=None, enddt=None, savefile=None): return skyname def set1axis(self, metdata=None, axis_azimuth=180, limit_angle=45, - angledelta=5, backtrack=True, gcr=1.0 / 3, cumulativesky=True): - ''' - RadianceObj set1axis - set1axis(metdata=None, axis_azimuth=180, limit_angle=45, angledelta=5, - backtrack=True, gcr=1.0/3.0, cumulativesky=True): - + angledelta=5, backtrack=True, gcr=1.0 / 3, cumulativesky=True, + fixed_tilt_angle=None): + """ Set up geometry for 1-axis tracking. Pull in tracking angle details from pvlib, create multiple 8760 metdata sub-files where datetime of met data matches the tracking angle. Returns 'trackerdict' which has keys equal to either the tracker angles (gencumsky workflow) or timestamps (gendaylit hourly workflow) - Parameters ------------ - cumulativesky # [True] boolean. whether individual csv files are - created with constant tilt angle for the cumulativesky approach. - # if false, the gendaylit tracking approach must be used. - metdata # MetObj to set up geometry. default = self.metdata - axis_azimuth # [180] orientation axis of tracker torque tube. Default North-South (180 deg) - limit_angle # [45] +/- limit angle of the 1-axis tracker in degrees. Default 45 - backtrack # [True] Whether backtracking is enabled (default = True) - gcr # [1.0/3.0] Ground coverage ratio for calculation backtracking. - angledelta # [5] degree of rotation increment to parse irradiance bins - (0.4 % error for DNI). Other options: 4 (.25%), 2.5 (0.1%). - Note: the smaller the angledelta, the more simulations must be run + cumulativesky : bool + [True] Wether individual csv files are + created with constant tilt angle for the cumulativesky approach. + if false, the gendaylit tracking approach must be used. + metdata : :py:class:`~bifacial_radiance.MetObj` + Meterological object to set up geometry. Usually set automatically by + `bifacial_radiance` after running :py:class:`bifacial_radiance.readepw`. + Default = self.metdata + axis_azimuth : numeric + Orientation axis of tracker torque tube. Default North-South (180 deg) + limit_angle : numeric + Limit angle (+/-) of the 1-axis tracker in degrees. Default 45 + backtrack : bool + Whether backtracking is enabled (default = True) + gcr : float + Ground coverage ratio for calculation backtracking. Defualt [1.0/3.0] + angledelta : numeric + Degree of rotation increment to parse irradiance bins. Default 5 degrees. + (0.4 % error for DNI). Other options: 4 (.25%), 2.5 (0.1%). + Note: the smaller the angledelta, the more simulations must be run. + fixed_tilt_angle : numeric + If passed, this changes to a fixed tilt + simulation where each hour uses fixed_tilt_angle + and axis_azimuth as the tilt and azimuth Returns ------- - trackerdict dictionary with keys for tracker tilt angles (gencumsky) or timestamps (gendaylit) - and list of csv metfile, and datetimes at that angle - trackerdict[angle]['csvfile';'surf_azm';'surf_tilt';'UTCtime'] - - or - - trackerdict[time]['tracker_theta';'surf_azm';'surf_tilt'] - - Internal variables - ------- - metdata.solpos dataframe with solar position data - metdata.surface_azimuth list of tracker azimuth data - metdata.surface_tilt list of tracker surface tilt data - metdata.tracker_theta list of tracker tilt angle - ''' + trackerdict : dictionary + Keys represent tracker tilt angles (gencumsky) or timestamps (gendaylit) + and list of csv metfile, and datetimes at that angle + trackerdict[angle]['csvfile';'surf_azm';'surf_tilt';'UTCtime'] + - or - + trackerdict[time]['tracker_theta';'surf_azm';'surf_tilt'] + """ + # Documentaiton check: + # Removed Internal variables + # ------- + # metdata.solpos dataframe with solar position data + # metdata.surface_azimuth list of tracker azimuth data + # metdata.surface_tilt list of tracker surface tilt data + # metdata.tracker_theta list of tracker tilt angle + if metdata == None: metdata = self.metdata if metdata == {}: - raise Exception("metdata doesnt exist yet. Run RadianceObj.readEPW() or .readTMY().") + raise Exception("metdata doesnt exist yet. "+ + "Run RadianceObj.readWeatherFile() ") #backtrack = True # include backtracking support in later version @@ -947,12 +1089,13 @@ def set1axis(self, metdata=None, axis_azimuth=180, limit_angle=45, # get 1-axis tracker angles for this location, rounded to nearest 'angledelta' - trackerdict = metdata.set1axis(cumulativesky=cumulativesky, + trackerdict = metdata._set1axis(cumulativesky=cumulativesky, axis_azimuth=axis_azimuth, limit_angle=limit_angle, angledelta=angledelta, backtrack=backtrack, - gcr=gcr + gcr=gcr, + fixed_tilt_angle=fixed_tilt_angle ) self.trackerdict = trackerdict self.cumulativesky = cumulativesky @@ -961,25 +1104,33 @@ def set1axis(self, metdata=None, axis_azimuth=180, limit_angle=45, def gendaylit1axis(self, metdata=None, trackerdict=None, startdate=None, enddate=None, debug=False, hpc=False): - ''' + """ 1-axis tracking implementation of gendaylit. Creates multiple sky files, one for each time of day. Parameters ------------ - metdata: output from readEPW or readTMY. Needs to have metdata.set1axis() run on it. - startdate: starting point for hourly data run. Optional parameter string 'MM/DD' or 'MM_DD' format - enddate: ending date for hourly data run. Optional parameter string 'MM/DD' or 'MM_DD' format - trackerdict dictionary with keys for tracker tilt angles (gencumsky) or timestamps (gendaylit) + metdata + Output from readEPW or readTMY. Needs to have RadianceObj.set1axis() run on it first. + startdate : str + Starting point for hourly data run. Optional parameter string + 'MM/DD' or 'MM_DD' format + enddate : str + Ending date for hourly data run. Optional parameter string + 'MM/DD' or 'MM_DD' format + trackerdict : dictionary + Dictionary with keys for tracker tilt angles (gencumsky) or timestamps (gendaylit) - Returns: + Returns ------- - trackerdict dictionary with keys for tracker tilt angles (gencumsky) or timestamps (gendaylit) - here the additional dictionary value ['skyfile'] is added + Updated trackerdict dictionary + Dictionary with keys for tracker tilt angles (gencumsky) or timestamps (gendaylit) + with the additional dictionary value ['skyfile'] added - ''' + """ + import dateutil.parser as parser # used to convert startdate and enddate - + import re if metdata is None: metdata = self.metdata @@ -992,41 +1143,49 @@ def gendaylit1axis(self, metdata=None, trackerdict=None, startdate=None, try: metdata.tracker_theta # this may not exist except AttributeError: - print("metdata.tracker_theta doesn't exist. Run metdata.set1axis() first") + print("metdata.tracker_theta doesn't exist. Run RadianceObj.set1axis() first") # look at start and end date if they're passed. Otherwise don't worry about it. - if startdate is not None: - startdate=startdate.replace('_','/') # making sure it is in 'MM/DD' format. - datetemp = parser.parse(startdate) - startindex = (int(datetemp.strftime('%j')) - 1) * 24 -1 - else: + # compare against metdata.datetime because this isn't necessarily an 8760! + temp = pd.to_datetime(metdata.datetime) + temp2 = temp.month*10000+temp.day*100+temp.hour + try: + match1 = re.split('_|/',startdate) + matchval = int(match1[0])*10000+int(match1[1])*100 + if len(match1)>2: + matchval = matchval + int(match1[2]) + startindex = temp2.to_list().index(matchval) + except: # catch ValueError (not in list) and AttributeError (startdate = None) startindex = 0 - if enddate is not None: - enddate=enddate.replace('_','/') # making sure it is in 'MM/DD' format. - datetemp = parser.parse(enddate) - endindex = (int(datetemp.strftime('%j')) ) * 24 # include all of enddate - else: - endindex = 8760 + try: + match1 = re.split('_|/',enddate) + matchval = int(match1[0])*10000+int(match1[1])*100 + if len(match1)>2: + matchval = matchval + int(match1[2]) + endindex = temp2.to_list().index(matchval) + except: # catch ValueError (not in list) and AttributeError + endindex = len(metdata.datetime) if hpc is True: startindex = 0 endindex = len(metdata.datetime) if debug is False: - print('Creating ~4000 skyfiles. Takes 1-2 minutes') + print('Creating ~%d skyfiles. Takes 1-2 minutes'%((endindex-startindex)/2)) count = 0 # counter to get number of skyfiles created, just for giggles trackerdict2={} - for i in range(startindex,endindex): + for i in range(startindex,endindex+1): time = metdata.datetime[i] filename = str(time)[5:-12].replace('-','_').replace(' ','_') self.name = filename #check for GHI > 0 #if metdata.ghi[i] > 0: - if (metdata.ghi[i] > 0) & (~np.isnan(metdata.tracker_theta[i])): # remove NaN tracker theta from trackerdict + if (metdata.ghi[i] > 0) & (~np.isnan(metdata.tracker_theta[i])): skyfile = self.gendaylit(metdata,i, debug=debug) - trackerdict2[filename] = trackerdict[filename] # trackerdict2 helps reduce the trackerdict to only the range specified. + # trackerdict2 reduces the dict to only the range specified. + trackerdict2[filename] = trackerdict[filename] trackerdict2[filename]['skyfile'] = skyfile count +=1 @@ -1034,34 +1193,42 @@ def gendaylit1axis(self, metdata=None, trackerdict=None, startdate=None, self.trackerdict = trackerdict2 return trackerdict2 - def genCumSky1axis(self, trackerdict=None, startdt=None, enddt=None): - ''' + def genCumSky1axis(self, trackerdict=None): + """ 1-axis tracking implementation of gencumulativesky. Creates multiple .cal files and .rad files, one for each tracker angle. - + .. deprecated:: 0.3.2 + startdt and enddt inputs are no longer available. + Use :func:`readWeatherFile(filename, starttime='MM_DD_HH', endtime='MM_DD_HH')` + to limit gencumsky simulations instead. + + Parameters ------------ - trackerdict: output from MetObj.set1axis() - startdt: datetime.datetime(Y,M,D,H,M,S) object. Only M,D,H selected. default: (0,1,1,0) - enddt: datetime.datetime(Y,M,D,H,M,S) object. Only M,D,H selected. default: (12,31,24,0) - + trackerdict + output from RadianceObj.set1axis() + startdt : *DEPRECATED* + + enddt : *DEPRECATED* - Returns: + Returns ------- - trackerdict: append 'skyfile' to the 1-axis dict with the location of the sky .radfile + trackerdict with new entry trackerdict.skyfile + Append 'skyfile' to the 1-axis dict with the location of the sky .radfile - ''' + """ + if trackerdict == None: try: trackerdict = self.trackerdict except AttributeError: print('No trackerdict value passed or available in self') - for theta in trackerdict: + for theta in sorted(trackerdict): # call gencumulativesky with a new .cal and .rad name csvfile = trackerdict[theta]['csvfile'] savefile = '1axis_%s'%(theta) #prefix for .cal file and skies\*.rad file - skyfile = self.genCumSky(epwfile=csvfile, startdt=startdt, enddt=enddt, savefile=savefile) + skyfile = self.genCumSky(epwfile=csvfile, savefile=savefile) trackerdict[theta]['skyfile'] = skyfile print('Created skyfile %s'%(skyfile)) # delete default skyfile (not strictly necessary) @@ -1071,21 +1238,27 @@ def genCumSky1axis(self, trackerdict=None, startdt=None, enddt=None): def makeOct(self, filelist=None, octname=None, hpc=False): - ''' - combine everything together into a .oct file + """ + Combine everything together into a .oct file Parameters - ------------ - filelist: overload files to include. otherwise takes self.filelist - octname: filename (without .oct extension) - hpc: boolean, default False. Activates a wait period in case one of the files for - making the oct is still missing. + ---------- + filelist : list + Files to include. otherwise takes self.filelist + octname : str + filename (without .oct extension) + hpc : bool + Default False. Activates a wait period in case one of the files for + making the oct is still missing. - Returns: Tuple + Returns ------- - octname: filename of .oct file in root directory including extension - err: Error message returned from oconv (if any) - ''' + octname : str + filename of .oct file in root directory including extension + err : str + Error message returned from oconv (if any) + """ + if filelist is None: filelist = self.getfilelist() if octname is None: @@ -1135,21 +1308,26 @@ def makeOct(self, filelist=None, octname=None, hpc=False): return '%s.oct' % (octname) def makeOct1axis(self, trackerdict=None, singleindex=None, customname=None, hpc=False): - ''' - combine files listed in trackerdict into multiple .oct files + """ + Combine files listed in trackerdict into multiple .oct files Parameters ------------ - trackerdict: Output from makeScene1axis - singleindex: Single index for trackerdict to run makeOct1axis in single-value mode (new in 0.2.3) - customname: Custom text string added to the end of the OCT file name. - hpc: boolean, default False. Activates a wait period in case one of the files for - making the oct is still missing. + trackerdict + Output from :py:class:`~bifacial_radiance.RadianceObj.makeScene1axis` + singleindex : str + Single index for trackerdict to run makeOct1axis in single-value mode. + customname : str + Custom text string added to the end of the OCT file name. + hpc : bool + Default False. Activates a wait period in case one of the files for + making the oct is still missing. - Returns: + Returns ------- - trackerdict: append 'octfile' to the 1-axis dict with the location of the scene .octfile - ''' + trackerdict + Append 'octfile' to the 1-axis dict with the location of the scene .octfile + """ if customname is None: customname = '' @@ -1164,8 +1342,8 @@ def makeOct1axis(self, trackerdict=None, singleindex=None, customname=None, hpc= else: # just loop through one single index in tracker dictionary indexlist = [singleindex] - print('\nMaking {} octfiles for 1-axis tracking in root directory.'.format(indexlist.__len__())) - for index in indexlist: # run through either entire key list of trackerdict, or just a single value + print('\nMaking {} octfiles in root directory.'.format(indexlist.__len__())) + for index in sorted(indexlist): # run through either entire key list of trackerdict, or just a single value try: filelist = self.materialfiles + [trackerdict[index]['skyfile'], trackerdict[index]['radfile']] octname = '1axis_%s%s'%(index, customname) @@ -1181,72 +1359,84 @@ def makeModule(self, name=None, x=None, y=None, bifi=1, modulefile=None, text=No xgap=0.01, ygap=0.0, zgap=0.1, numpanels=1, rewriteModulefile=True, axisofrotationTorqueTube=False, cellLevelModuleParams=None, orientation=None, torqueTubeMaterial=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. - The default orientation of the module .rad file is a portrait oriented module, origin at (x/2,0,0) i.e. - center of module along x, at the bottom edge. - - Version 0.3.0: - move cell parameters to cellLevelModuleParams dict. - Version 0.2.4: - remove portrait or landscape `orientation`. - - Now define a module by x (dimension along rack) and y (dimension in slant direction) - - Rename gap variables to be xgap, ygap and zgap - - Introduce scenex and sceney which include torque tube and gap dimensions - Version 0.2.3: add the ability to have torque tubes and module gaps. + makeModule is in the `RadianceObj` class because this is defined before a `SceneObj` is. - TODO: add transparency parameter, make modules with non-zero opacity - TODO: refactor this module to streamline it and accept moduleDict input + Module definitions assume that the module .rad file is defined + with zero tilt, centered along the x-axis and y-axis for the center + of rotation of the module (+X/2, -X/2, +Y/2, -Y/2 on each side). + Tip: to define a module that is in 'portrait' mode, y > x. Parameters ------------ - name: string input to name the module type - - module configuration dictionary inputs: - x # width of module along the axis of the torque tube or racking structure. (meters). - y # length of module (meters). - bifi # bifaciality of the panel (not currently used) - modulefile # existing radfile location in \objects. Otherwise a default value is used - text = '' # text used in the radfile to generate the module - customtext = '' # added-text used in the radfile to generate any - extra details in the racking/module. Does not overwrite - generated module (unlike "text"), but adds to it at the end. - rewriteModulefile # boolean, set to True. Will rewrite module file - each time makeModule is run. - - New inputs as of 0.2.3 for torque tube and gap spacing: - torquetube #boolean. Is torque tube present or no? - diameter #float. tube diameter in meters. For square, - For Square, diameter means the length of one of the - square-tube side. For Hex, diameter is the distance - between two vertices (diameter of the circumscribing circle) - tubetype #'Square', 'Round' (default), 'Hex' or 'Oct'. tube cross section - material #'Metal_Grey' or 'black'. Material for the torque tube. - numpanels #int. number of modules arrayed in the Y-direction. e.g. - 1-up or 2-up, etc. (supports any number for carport/Mesa simulations) - xgap #float. "Panel space in X". Separation between modules in a row. - #DEPRECATED INPUTS: - ygap #float. gap between modules arrayed in the Y-direction if any. - zgap # distance behind the modules in the z-direction to the edge of the tube (m) - axisofrotationTorqueTube # boolean. Default False. IF true, creates geometry - so center of rotation is at the center of the torquetube, with - an offsetfromaxis equal to half the torquetube diameter + the zgap. - If there is no torquetube (torquetube=False), offsetformaxis - will equal the zgap. - - New inputs as of 0.2.4 for creating custom cell-level module: - cellLevelModuleParams: (dict) input parameters for creating a cell-level module - dictionary Keys: - numcellsx #int. number of cells in the X-direction within the module - numcellsy #int. number of cells in the Y-direction within the module - xcell #float. width of each cell (X-direction) in the module - ycell #float. length of each cell (Y-direction) in the module - xcellgap #spacing between cells in the X-direction - ycellgap #spacing between cells in the Y-direction - - Returns: None - ------- + name : str + Input to name the module type + x : numeric + Width of module along the axis of the torque tube or racking structure. (meters). + y : numeric + Length of module (meters) + bifi : numeric + Bifaciality of the panel (not currently used). Between 0 (monofacial) + and 1, default 1. + modulefile : str + Existing radfile location in \objects. Otherwise a default value is used + text : str + Text used in the radfile to generate the module + customtext : str + Added-text used in the radfile to generate any + extra details in the racking/module. Does not overwrite + generated module (unlike "text"), but adds to it at the end. + rewriteModulefile : bool + Default True. Will rewrite module file each time makeModule is run. + torquetube : bool + This variable defines if there is a torque tube or not. + diameter : float + Tube diameter in meters. For square, + For Square, diameter means the length of one of the + square-tube side. For Hex, diameter is the distance + between two vertices (diameter of the circumscribing circle) + tubetype : str + Options: 'Square', 'Round' (default), 'Hex' or 'Oct'. Tube cross section + material : str + Options: 'Metal_Grey' or 'black'. Material for the torque tube. + numpanels : int + Number of modules arrayed in the Y-direction. e.g. + 1-up or 2-up, etc. (supports any number for carport/Mesa simulations) + xgap : float + Panel space in X direction. Separation between modules in a row. + ygap : float + Gap between modules arrayed in the Y-direction if any. + zgap : float + Distance behind the modules in the z-direction to the edge of the tube (m) + cellLevelModuleParams : dict + Dictionary with input parameters for creating a cell-level module. + Dictionary Keys: + ================ ==================================================== + numcellsx : int Number of cells in the X-direction within the module + numcellsy : int Number of cells in the Y-direction within the module + xcell : float Width of each cell (X-direction) in the module + ycell : float Length of each cell (Y-direction) in the module + xcellgap : float Spacing between cells in the X-direction + ycellgap : float Spacing between cells in the Y-direction + ================ ==================================================== + axisofrotationTorqueTube : bool + Default False. IF true, creates geometry + so center of rotation is at the center of the torquetube, with + an offsetfromaxis equal to half the torquetube diameter + the zgap. + If there is no torquetube (torquetube=False), offsetformaxis + will equal the zgap. + + '""" + + # #TODO: add transparency parameter, make modules with non-zero opacity + # #DocumentationCheck: this Todo seems to besolved by doing cell-level modules + # and printing the packaging facotr + + + # #TODO: refactor this module to streamline it and accept moduleDict input + # #DocumentationCheck : do we still need to do this Todo? - ''' import json @@ -1450,21 +1640,28 @@ def makeModule(self, name=None, x=None, y=None, bifi=1, modulefile=None, text=No def makeCustomObject(self, name=None, text=None): - ''' + """ Function for development and experimenting with extraneous objects in the scene. - This function creates a name.rad textfile in the objects folder + This function creates a `name.rad` textfile in the objects folder with whatever text that is passed to it. It is up to the user to pass the correct radiance format. + For example, to create a box at coordinates 0,0 (with its bottom surface - on the plane z=0), - name = 'box' - text='! genbox black PVmodule 0.5 0.5 0.5 | xform -t -0.25 -0.25 0' + on the plane z=0): + + .. code-block: + + name = 'box' + text='! genbox black PVmodule 0.5 0.5 0.5 | xform -t -0.25 -0.25 0' Parameters - ------------ - name: string input to name the module type - text = '' # text used in the radfile to generate the module - ''' + ---------- + name : str + String input to name the module type + text : str + Text used in the radfile to generate the module + + """ customradfile = os.path.join('objects', '%s.rad'%(name)) # update in 0.2.3 to shorten radnames # py2 and 3 compatible: binary write, encode text first @@ -1484,26 +1681,32 @@ def printModules(self): return modulenames def makeScene(self, moduletype=None, sceneDict=None, hpc=False): - ''' - return a SceneObj which contains details of the PV system configuration including + """ + Create a SceneObj which contains details of the PV system configuration including tilt, row pitch, height, nMods per row, nRows in the system... Parameters - ------------ - moduletype: string name of module created with makeModule() - sceneDict: dictionary with keys:[tilt] [clearance_height]* [pitch] - [azimuth] [nMods] [nRows] [hub_height]* [height]* - - *height deprecated from sceneDict. For makeScene (fixed systems) - if passed it is assumed it reffers to clearance_height. - clearance_height recommended for fixed_tracking systems. - hub_height can also be passed as a possibility. - hpc: boolean, default False. For makeScene, it adds the full path - of the objects folder where the module . rad file is saved. - - Returns: SceneObj 'scene' with configuration details + ---------- + moduletype : str + String name of module created with makeModule() + sceneDict : dictionary + Dictionary with keys: `tilt`, `clearance_height`*, `pitch`, + `azimuth`, `nMods`, `nRows`, `hub_height`*, `height`* + * height deprecated from sceneDict. For makeScene (fixed systems) + if passed it is assumed it reffers to clearance_height. + `clearance_height` recommended for fixed_tracking systems. + `hub_height` can also be passed as a possibility. + hpc : bool + Default False. For makeScene, it adds the full path + of the objects folder where the module . rad file is saved. + + Returns ------- - ''' + SceneObj + 'scene' with configuration details + + """ + if moduletype is None: print('makeScene(moduletype, sceneDict, nMods, nRows). '+\ 'Available moduletypes: monopanel, simple_panel' ) @@ -1578,7 +1781,7 @@ def makeScene(self, moduletype=None, sceneDict=None, hpc=False): self.nMods = sceneDict['nMods'] self.nRows = sceneDict['nRows'] - self.sceneRAD = self.scene.makeSceneNxR(moduletype=moduletype, + self.sceneRAD = self.scene._makeSceneNxR(moduletype=moduletype, sceneDict=sceneDict, hpc=hpc) @@ -1605,26 +1808,28 @@ def makeScene(self, moduletype=None, sceneDict=None, hpc=False): return self.scene def appendtoScene(self, radfile=None, customObject=None, text=''): - ''' - demo.addtoScene(scene.radfile, customObject, text='') - Appends to the Scene radfile in \\objects the text command in Radiance + """ + Appends to the `Scene radfile` in folder `\objects` the text command in Radiance lingo created by the user. Useful when using addCustomObject to the scene. - TO DO: Add a custom name and replace radfile name - - Parameters: - ---------------- - 'radfile': directory and name of where .rad scene file is stored - customObject: directory and name of custom object .rad file is stored - text: command to be appended to the radfile. Do not leave empty spaces - at the end. + Parameters + ---------- + radfile: str + Directory and name of where .rad scene file is stored + customObject : str + Directory and name of custom object .rad file is stored + text : str + Command to be appended to the radfile. Do not leave empty spaces + at the end. - Returns: - ---------------- + Returns + ------- Nothing, the radfile must already be created and assigned when running this. - - ''' + + """ + + #TODO: Add a custom name and replace radfile name # py2 and 3 compatible: binary write, encode text first text2 = '\n' + text + ' ' + customObject @@ -1637,40 +1842,57 @@ def appendtoScene(self, radfile=None, customObject=None, text=''): f.write(text2) - - def makeScene1axis(self, trackerdict=None, moduletype=None, sceneDict=None, cumulativesky=None, nMods=None, nRows=None, hpc=False): - ''' - create a SceneObj for each tracking angle which contains details of the PV + """ + Creates a SceneObj for each tracking angle which contains details of the PV system configuration including row pitch, hub_height, nMods per row, nRows in the system... Parameters ------------ - trackerdict: output from GenCumSky1axis - moduletype: string name of module created with makeModule() - sceneDict: dictionary with keys:[tilt] [hub_height] [pitch] [azimuth] - cumulativesky: bool: use cumulativesky or not? - nMods: deprecated. int number of modules per row (default = 20). - If included it will be assigned to the sceneDict - nRows: deprecated. int number of rows in system (default = 7). - If included it will be assigned to the sceneDict - hpc: boolean, default False. For makeScene, it adds the full path - of the objects folder where the module . rad file is saved. + trackerdict + Output from GenCumSky1axis + moduletype : str + Name of module created with makeModule() + sceneDict : + Dictionary with keys:`tilt`, `hub_height`, `pitch`, `azimuth` + cumulativesky : bool + Defines if sky will be generated with cumulativesky or gendaylit. + nMods : int + DEPRECATED. int number of modules per row (default = 20). + If included it will be assigned to the sceneDict + nRows: int + DEPRECATED. int number of rows in system (default = 7). + If included it will be assigned to the sceneDict + hpc : bool + Default False. For makeScene, it adds the full path + of the objects folder where the module . rad file is saved. Returns - ----------- - trackerdict: append the following keys : - 'radfile': directory where .rad scene file is stored - 'scene' : SceneObj for each tracker theta - 'clearance_height' : calculated ground clearance based on - hub height, tilt angle and module length - ''' + -------- + trackerdict + Append the following keys + 'radfile' + directory where .rad scene file is stored + 'scene' + SceneObj for each tracker theta + 'clearance_height' + Calculated ground clearance based on + `hub height`, `tilt` angle and overall collector width `sceney` + + """ + + # #DocumentationCheck + # #TODO + # nMods and nRows were deprecated various versions before. + # Removed them as inputs now. + import math if sceneDict is None: print('usage: makeScene1axis(moduletype, sceneDict, nMods, nRows).'+ - 'sceneDict inputs: .tilt .hub_height .pitch .azimuth') + 'sceneDict inputs: .hub_height .azimuth .nMods .nRows'+ + 'and .pitch or .gcr') return # Check for deprecated variables and assign to dictionary. @@ -1806,7 +2028,7 @@ def makeScene1axis(self, trackerdict=None, moduletype=None, sceneDict=None, 'nMods': sceneDict['nMods'], 'nRows': sceneDict['nRows']} - radfile = scene.makeSceneNxR(moduletype=moduletype, + radfile = scene._makeSceneNxR(moduletype=moduletype, sceneDict=sceneDict2, radname=radname, hpc=hpc) @@ -1816,7 +2038,7 @@ def makeScene1axis(self, trackerdict=None, moduletype=None, sceneDict=None, print('{} Radfiles created in /objects/'.format(trackerdict.__len__())) else: #gendaylit workflow - print('\nMaking ~4000 .rad files for gendaylit 1-axis workflow (this takes a minute..)') + print('\nMaking ~%s .rad files for gendaylit 1-axis workflow (this takes a minute..)' % (len(trackerdict))) count = 0 for time in trackerdict: scene = SceneObj(moduletype) @@ -1850,7 +2072,7 @@ def makeScene1axis(self, trackerdict=None, moduletype=None, sceneDict=None, 'nMods': sceneDict['nMods'], 'nRows': sceneDict['nRows']} - radfile = scene.makeSceneNxR(moduletype=moduletype, + radfile = scene._makeSceneNxR(moduletype=moduletype, sceneDict=sceneDict2, radname=radname, hpc=hpc) @@ -1862,37 +2084,47 @@ def makeScene1axis(self, trackerdict=None, moduletype=None, sceneDict=None, self.trackerdict = trackerdict self.nMods = sceneDict['nMods'] #assign nMods and nRows to RadianceObj self.nRows = sceneDict['nRows'] + self.hub_height = hubheight + return trackerdict#self.scene def analysis1axis(self, trackerdict=None, singleindex=None, accuracy='low', customname=None, modWanted=None, rowWanted=None, sensorsy=9): - ''' - loop through trackerdict and run linescans for each scene and scan in there. + """ + Loop through trackerdict and runs linescans for each scene and scan in there. Parameters ---------------- - trackerdict - singleindex :For single-index mode, just the one index we want to run (new in 0.2.3) - accuracy : 'low' or 'high' - resolution option used during irrPlotNew and rtrace - customname : Custom text string to be added to the file name for the results .CSV files - modWanted : mod to be sampled. Index starts at 1. - rowWanted : row to be sampled. Index starts at 1. (row 1) - sensorsy : Sampling resolution for the irradiance + trackerdict + singleindex : str + For single-index mode, just the one index we want to run (new in 0.2.3). + Example format '11_06_14' for November 6 at 2 PM + accuracy : str + 'low' or 'high', resolution option used during _irrPlot and rtrace + customname : str + Custom text string to be added to the file name for the results .CSV files + modWanted : int + Module to be sampled. Index starts at 1. + rowWanted : int + Row to be sampled. Index starts at 1. (row 1) + sensorsy : int + Sampling resolution for the irradiance across the collector width. Returns - ---------------- + ------- trackerdict with new keys: + 'AnalysisObj' : analysis object for this tracker theta - 'Wm2Front' : list of front Wm2 irradiances, len=sensorsy - 'Wm2Back' : list of rear Wm2 irradiances, len=sensorsy + 'Wm2Front' : list of front Wm-2 irradiances, len=sensorsy + 'Wm2Back' : list of rear Wm-2 irradiances, len=sensorsy 'backRatio' : list of rear irradiance ratios, len=sensorsy - - Also, appends new values to RadianceObj: + RadianceObj with new appended values: 'Wm2Front' : np Array with front irradiance cumulative 'Wm2Back' : np Array with rear irradiance cumulative 'backRatio' : np Array with rear irradiance ratios - ''' + """ + import warnings if customname is None: @@ -1926,8 +2158,8 @@ def analysis1axis(self, trackerdict=None, singleindex=None, accuracy='low', try: # look for missing data analysis = AnalysisObj(octfile,name) name = '1axis_%s%s'%(index,customname,) - frontscan, backscan = analysis.moduleAnalysis(scene, modWanted=modWanted, rowWanted=rowWanted, sensorsy=sensorsy) - analysis.analysis(octfile,name,frontscan,backscan,accuracy) + frontscan, backscan = analysis.moduleAnalysis(scene=scene, modWanted=modWanted, rowWanted=rowWanted, sensorsy=sensorsy) + analysis.analysis(octfile=octfile,name=name,frontscan=frontscan,backscan=backscan,accuracy=accuracy) trackerdict[index]['AnalysisObj'] = analysis except Exception as e: # problem with file. TODO: only catch specific error types here. warnings.warn('Index: {}. Problem with file. Error: {}. Skipping'.format(index,e), Warning) @@ -1958,35 +2190,97 @@ def analysis1axis(self, trackerdict=None, singleindex=None, accuracy='low', self.Wm2Front += frontWm2 # these are accumulated over all indices passed in. self.Wm2Back += backWm2 self.backRatio = backWm2/(frontWm2+.001) - #self.trackerdict = trackerdict # removed v0.2.3 - already mapped to self.trackerdict - return trackerdict # is it really desireable to return the trackerdict here? + # Save compiled results using _saveresults + if singleindex is None: + + print ("Saving a cumulative-results file in the main simulatoin folder." + + "This adds up by sensor location the irradiance over all hours " + + "or configurations considered." + + "\nWarning: This file saving routine does not clean results, so "+ + "if your setup has ygaps, or 2+modules or torque tubes, doing "+ + "a deeper cleaning and working with the individual results "+ + "files in the results folder is highly suggested.") + cumfilename = 'cumulative_results_%s.csv'%(customname) + if self.cumulativesky is True: + frontcum = pd.DataFrame() + rearcum = pd.DataFrame() + temptrackerdict = trackerdict[0.0]['AnalysisObj'] + frontcum ['x'] = temptrackerdict.x + frontcum ['y'] = temptrackerdict.y + frontcum ['z'] = temptrackerdict.z + frontcum ['mattype'] = temptrackerdict.mattype + frontcum ['Wm2'] = self.Wm2Front + rearcum ['x'] = temptrackerdict.x + rearcum ['y'] = temptrackerdict.x + rearcum ['z'] = temptrackerdict.rearZ + rearcum ['mattype'] = temptrackerdict.rearMat + rearcum ['Wm2'] = self.Wm2Back + cumanalysisobj = AnalysisObj() + print ("\nSaving Cumulative results" ) + cumanalysisobj._saveResultsCumulative(frontcum, rearcum, savefile=cumfilename) + else: # trackerkeys are day/hour/min, and there's no easy way to find a + # tilt of 0, so making a fake linepoint object for tilt 0 + # and then saving. + cumscene = trackerdict[trackerkeys[0]]['scene'] + cumscene.sceneDict['tilt']=0 + cumscene.sceneDict['clearance_height'] = self.hub_height + cumanalysisobj = AnalysisObj() + frontscan, backscan = cumanalysisobj.moduleAnalysis(scene=cumscene, modWanted=modWanted, rowWanted=rowWanted, sensorsy = sensorsy) + x,y,z = cumanalysisobj._linePtsArray(frontscan) + x,y,rearz = cumanalysisobj._linePtsArray(backscan) + + frontcum = pd.DataFrame() + rearcum = pd.DataFrame() + frontcum ['x'] = x + frontcum ['y'] = y + frontcum ['z'] = z + frontcum ['mattype'] = trackerdict[trackerkeys[0]]['AnalysisObj'].mattype + frontcum ['Wm2'] = self.Wm2Front + rearcum ['x'] = x + rearcum ['y'] = y + rearcum ['z'] = rearz + rearcum ['mattype'] = trackerdict[trackerkeys[0]]['AnalysisObj'].rearMat + rearcum ['Wm2'] = self.Wm2Back + print ("\nSaving Cumulative results" ) + cumanalysisobj._saveResultsCumulative(frontcum, rearcum, savefile=cumfilename) + + return trackerdict # End RadianceObj definition class GroundObj: - ''' - details for the ground surface and reflectance - ''' - - def __init__(self, materialOrAlbedo=None, material_file=None): - ''' - sets and returns ground materials information. if material type is known, pass it in to get - reflectance info. if material type isn't known, material_info.list is returned - - Parameters - ------------ - materialOrAlbedo - if known, the name of the material desired. e.g. 'litesoil' + """ + Class to set and return details for the ground surface materials and reflectance. + If albedo is passed, it is used as default. + If material type is known, it is used to get reflectance info. + if material type isn't known, material_info.list is returned - material_file - filename of the material information. default ground.rad + Parameters + ------------ + materialOrAlbedo : numeric or str + If number between 0 and 1 is passed, albedo input is assumed and assigned. + If string is passed with the name of the material desired. e.g. 'litesoil', + properties are searched in `material_file`. + Default Material names to choose from: litesoil, concrete, white_EPDM, + beigeroof, beigeroof_lite, beigeroof_heavy, black, asphalt + material_file : str + Filename of the material information. Default `ground.rad` + + Returns + ------- + material_info.normval : numeric + Normalized color value + material_info.ReflAvg : numeric + Average reflectance + material_info.names : list + List of material names in case of wrong/empty materialorAlbedo option passed. + """ - Returns - ------- - material_info.names : list of material names - material_info.normval : normalized color value - material_info.ReflAvg : average reflectance - ''' + # #DocumentationCheck : not really returning material_info.normval but self? + + def __init__(self, materialOrAlbedo=None, material_file=None): self.normval = '' self.ReflAvg = '' @@ -2022,7 +2316,6 @@ def __init__(self, materialOrAlbedo=None, material_file=None): self.ground_type = 'custom' else: - f = open(os.path.join(material_path,material_file)) keys = [] #list of material key names Rrefl = []; Grefl=[]; Brefl=[] #RGB reflectance of the material @@ -2053,11 +2346,6 @@ def __init__(self, materialOrAlbedo=None, material_file=None): print('Input albedo 0-1, or ground material names:'+str(keys)) return None - ''' - #material names to choose from: litesoil, concrete, white_EPDM, - # beigeroof, beigeroof_lite, beigeroof_heavy, black, asphalt - - ''' class SceneObj: ''' scene information including PV module type, bifaciality, array info @@ -2070,10 +2358,10 @@ def __init__(self, moduletype=None): ''' initialize SceneObj ''' modulenames = self.readModule() - # should sceneDict be initialized here? This is set in makeSceneNxR + # should sceneDict be initialized here? This is set in _makeSceneNxR #self.sceneDict = {'nMods':None, 'tilt':None, 'pitch':None, 'clearance_height':None, 'nRows':None, 'azimuth':None} if moduletype is None: - print('Usage: SceneObj(moduletype)\nNo module type selected. Available module types: {}'.format(modulenames)) + #print('Usage: SceneObj(moduletype)\nNo module type selected. Available module types: {}'.format(modulenames)) return else: if moduletype in modulenames: @@ -2087,21 +2375,25 @@ def __init__(self, moduletype=None): 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. + ----------- + name : str + Name of module to be read Returns ------- - dict of module parameters - -or- - list of modulenames if name is not passed in + moduleDict : dictionary + self.scenex : (float) + Overall module width including xgap. + self.sceney : (float) + Overall module(s) height including ygaps along the collector width (CW), + list of modulenames if name is not passed in. - ''' + """ import json filedir = os.path.join(DATA_PATH,'module.json') @@ -2148,28 +2440,12 @@ def readModule(self, name=None): print('Error: module name {} doesnt exist'.format(name)) return {} - def makeSceneNxR(self, moduletype=None, sceneDict=None, radname=None, hpc=False): - - ''' - return a SceneObj which contains details of the PV system configuration including - tilt, row pitch, hub_height or clearance_height, nMods per row, nRows in the system... - - arrange module defined in SceneObj into a N x R array - Valid input ranges: Tilt -90 to 90 degrees. - Axis azimuth: A value denoting the compass direction along which the - axis of rotation lies. Measured in decimal degrees East - of North. [0 to 180) possible. - If azimuth is passed, a warning is given and Axis Azimuth and - tilt are re-calculated. - - Module definitions assume that the module .rad file is defined - with zero tilt, centered along the x-axis and y-axis for the center - of rotation of the module (+X/2, -X/2, +Y/2, -Y/2 on each side) - Y-axis is assumed the bottom edge of the module is at y = 0, - top of the module at y = Y. - self.scenex is overall module width including xgap. - self.sceney is overall series height of module(s) including gaps, - multiple-up configuration, etc + def _makeSceneNxR(self, moduletype=None, sceneDict=None, radname=None, hpc=False): + """ + Arrange module defined in :py:class:`bifacial_radiance.SceneObj` into a N x R array. + Returns a :py:class:`bifacial_radiance.SceneObj` which contains details + of the PV system configuration including `tilt`, `row pitch`, `hub_height` + or `clearance_height`, `nMod`s per row, `nRows` in the system. The returned scene has (0,0) coordinates centered at the module at the center of the array. For 5 rows, that is row 3, for 4 rows, that is @@ -2178,26 +2454,40 @@ def makeSceneNxR(self, moduletype=None, sceneDict=None, radname=None, hpc=False) Parameters ------------ - moduletype: string name of module created with makeModule() - sceneDict: dictionary with keys:[tilt] [height] [pitch] [azimuth]. - Here `height` is CLEARANCE_HEIGHT - nMods: int number of modules per row (default = 20) - nRows: int number of rows in system (default = 7) - sensorsy: int number of scans in the y direction (up tilted module - chord, default = 9) - modwanted: where along row does scan start, Nth module along the row - (default middle module) - rowwanted: which row is scanned? (default middle row) - mode: 0 fixed / 1 singleaxistracking - hpc: boolean, default False. For makeScene, it adds the full path - of the objects folder where the module .rad file is saved. + moduletype: str + Name of module created with :py:class:`~bifacial_radiance.RadianceObj.makeModule`. + sceneDict : dictionary + Dictionary of scene parameters. + clearance_height : numeric + (meters). + pitch : numeric + Separation between rows + tilt : numeric + Valid input ranges -90 to 90 degrees + axis_azimuth : numeric + A value denoting the compass direction along which the + axis of rotation lies. Measured in decimal degrees East + of North. [0 to 180) possible. + If azimuth is passed, a warning is given and Axis Azimuth and + tilt are re-calculated. + nMods : int + Number of modules per row (default = 20) + nRows : int + Number of rows in system (default = 7) + radname : str + String for name for radfile. + hpc : bool + Default False. For makeScene, it adds the full path + of the objects folder where the module .rad file is saved. Returns ------- - radfile: (string) filename of .RAD scene in /objects/ - Returns: SceneObj 'scene' with configuration details + radfile : str + Filename of .RAD scene in /objects/ + scene : :py:class:`~bifacial_radiance.SceneObj ` + Returns a `SceneObject` 'scene' with configuration details - ''' + """ #Cleanup Should this still be here? if moduletype is None: @@ -2209,7 +2499,8 @@ def makeSceneNxR(self, moduletype=None, sceneDict=None, radname=None, hpc=False) if sceneDict is None: print('makeScene(moduletype, sceneDict, nMods, nRows). sceneDict'+ - ' inputs: .tilt .height .pitch .azimuth .nMods .nRows') + ' inputs: .tilt .azimuth .nMods .nRows' + + ' AND .tilt or .gcr ; AND .hub_height or .clearance_height') if 'orientation' in sceneDict: @@ -2361,8 +2652,16 @@ def makeSceneNxR(self, moduletype=None, sceneDict=None, radname=None, hpc=False) return radfile def showModule(self, name): - """ quick method to call objview on a module called 'name' + """ + Method to call objview on a module called 'name' and render it (visualize it). + + Parameters + ---------- + name : str + Name of module to be rendered. + """ + moduleDict = self.readModule(name) modulefile = moduleDict['modulefile'] @@ -2377,7 +2676,9 @@ def showModule(self, name): return def showScene(self): - """ quick method to call objview on the scene included in self + """ + Method to call objview on the scene included in self + """ cmd = 'objview %s %s' % (os.path.join('materials', 'ground.rad'), self.radfiles) @@ -2392,47 +2693,22 @@ def showScene(self): # end of SceneObj class MetObj: - ''' - meteorological data from EPW file + """ + Meteorological data from EPW file. - ''' - def __initOld__(self, epw=None): - ''' initialize MetObj from passed in epwdata from pyepw.epw - used to be __init__ called from readEPW_old - ''' - if epw is not None: - #self.location = epw.location - self.latitude = epw.location.latitude - self.longitude = epw.location.longitude - self.elevation = epw.location.elevation - self.timezone = epw.location.timezone - self.city = epw.location.city - - wd = epw.weatherdata - - - self.datetime = [datetime.datetime( - 1990,x.month,x.day,x.hour-1) - for x in wd - ] - self.ghi = [x.global_horizontal_radiation for x in wd] - self.dhi = [x.diffuse_horizontal_radiation for x in wd] - self.dni = [x.direct_normal_radiation for x in wd] - self.ghl = [x.global_horizontal_illuminance for x in wd] # not used - self.dhl = [x.diffuse_horizontal_illuminance for x in wd]# not used - self.dnl = [x.direct_normal_illuminance for x in wd] # not used - self.epw_raw = epw # not used + Initialize the MetObj from tmy data already read in. + + Parameters + ----------- + tmydata : DataFrame + TMY3 output from :py:class:`~bifacial_radiance.RadianceObj.readTMY` or from :py:class:`~bifacial_radiance.RadianceObj.readEPW`. + metadata : Dictionary + Metadata output from output from :py:class:`~bifacial_radiance.RadianceObj.readTMY`` or from :py:class:`~bifacial_radiance.RadianceObj.readEPW`. + + """ def __init__(self, tmydata, metadata): - ''' - initTMY: initialize the MetObj from a tmy3 file instead of a epw file - Parameters - ----------- - tmydata: tmy3 output from pvlib.readtmy3 - metadata: metadata output from pvlib.readtmy3 - - ''' import pytz import pvlib @@ -2442,7 +2718,10 @@ def __init__(self, tmydata, metadata): self.longitude = metadata['longitude']; lon=self.longitude self.elevation = metadata['altitude']; elev=self.elevation self.timezone = metadata['TZ'] - self.city = metadata['Name'] + try: + self.city = metadata['Name'] # readepw version + except KeyError: + self.city = metadata['city'] # pvlib version #self.location.state_province_region = metadata['State'] # unecessary self.datetime = tmydata.index.tolist() # this is tz-aware. self.ghi = tmydata.GHI.tolist() @@ -2455,15 +2734,18 @@ def __init__(self, tmydata, metadata): datetimetz = datetimetz.tz_localize(pytz.FixedOffset(self.timezone*60))# use pytz.FixedOffset (in minutes) except TypeError: # data is tz-localized already. Just put it in local time. datetimetz = datetimetz.tz_convert(pytz.FixedOffset(self.timezone*60)) - #check for data interval - interval = datetimetz[1]-datetimetz[0] + #check for data interval. default 1h. + try: + interval = datetimetz[1]-datetimetz[0] + except IndexError: + interval = pd.Timedelta('1h') # ISSUE: if 1 datapoint is passed, are we sure it's hourly data? #Offset so it matches the single-axis tracking sun position calculation considering use of weather files if interval== pd.Timedelta('1h'): # get solar position zenith and azimuth based on site metadata #solpos = pvlib.irradiance.solarposition.get_solarposition(datetimetz,lat,lon,elev) # Sunrise/Sunset Check and adjusts position of time for that near sunrise and sunset. - sunup= pvlib.irradiance.solarposition.get_sun_rise_set_transit(datetimetz, lat, lon) #only for pvlib <0.6.1 - #sunup= pvlib.irradiance.solarposition.sun_rise_set_transit_spa(datetimetz, lat, lon) #new for pvlib >= 0.6.1 + #sunup= pvlib.irradiance.solarposition.get_sun_rise_set_transit(datetimetz, lat, lon) #only for pvlib <0.6.1 + sunup= pvlib.irradiance.solarposition.sun_rise_set_transit_spa(datetimetz, lat, lon) #new for pvlib >= 0.6.1 sunup['minutedelta']= int(interval.seconds/2/60) # default sun angle 30 minutes before timestamp # vector update of minutedelta at sunrise @@ -2478,49 +2760,66 @@ def __init__(self, tmydata, metadata): else: minutedelta = int(interval.seconds/2/60) #datetimetz=datetimetz-pd.Timedelta(minutes = minutedelta) # This doesn't check for Sunrise or Sunset - sunup= pvlib.irradiance.solarposition.get_sun_rise_set_transit(datetimetz, lat, lon) - #sunup= pvlib.irradiance.solarposition.sun_rise_set_transit_spa(datetimetz, lat, lon) #new for pvlib >= 0.6.1 + #sunup= pvlib.irradiance.solarposition.get_sun_rise_set_transit(datetimetz, lat, lon) # deprecated in pvlib 0.6.1 + sunup= pvlib.irradiance.solarposition.sun_rise_set_transit_spa(datetimetz, lat, lon) #new for pvlib >= 0.6.1 sunup['corrected_timestamp'] = sunup.index-pd.Timedelta(minutes = minutedelta) self.solpos = pvlib.irradiance.solarposition.get_solarposition(sunup['corrected_timestamp'],lat,lon,elev) self.sunrisesetdata=sunup - def set1axis(self, cumulativesky=True, axis_azimuth=180, limit_angle=45, - angledelta=None, backtrack=True, gcr = 1.0/3.0, axis_tilt = 0): - ''' + def _set1axis(self, cumulativesky=True, axis_azimuth=180, limit_angle=45, + angledelta=None, backtrack=True, gcr = 1.0/3.0, axis_tilt = 0, + fixed_tilt_angle=None): + """ Set up geometry for 1-axis tracking cumulativesky. Solpos data - already stored in metdata.solpos. Pull in tracking angle details from + already stored in `metdata.solpos`. Pull in tracking angle details from pvlib, create multiple 8760 metdata sub-files where datetime of met data matches the tracking angle. Parameters ------------ - cumulativesky # boolean. whether individual csv files are created - # with constant tilt angle for the cumulativesky approach. - # if false, the gendaylit tracking approach must be used. - axis_azimuth # orientation axis of tracker torque tube. Default North-South (180 deg) - limit_angle # +/- limit angle of the 1-axis tracker in degrees. Default 45 - angledelta # degree of rotation increment to parse irradiance bins. - # Default 5 degrees (0.4 % error for DNI). - # Other options: 4 (.25%), 2.5 (0.1%). - # (the smaller the angledelta, the more simulations) + cumulativesky : bool + Whether individual csv files are created + with constant tilt angle for the cumulativesky approach. + if false, the gendaylit tracking approach must be used. + axis_azimuth : numerical + orientation axis of tracker torque tube. Default North-South (180 deg) + For fixed tilt simulations (angledelta=0) this is the orientation azimuth + limit_angle : numerical + +/- limit angle of the 1-axis tracker in degrees. Default 45 + For fixed tilt simulations (angledelta=0) this is the tilt angle + angledelta : numerical + Degree of rotation increment to parse irradiance bins. + Default 5 degrees (0.4 % error for DNI). + Other options: 4 (.25%), 2.5 (0.1%). + (the smaller the angledelta, the more simulations) + fixed_tilt_angle : numerical + Optional use. this changes to a fixed + tilt simulation where each hour uses fixed_tilt_angle and + axis_azimuth as the tilt and azimuth Returns ------- - trackerdict dictionary with keys for tracker tilt angles and - list of csv metfile, and datetimes at that angle - trackerdict[angle]['csvfile';'surf_azm';'surf_tilt';'UTCtime'] - Note: this output is mostly used for the cumulativesky approach. - - Internal parameters - -------- - metdata.solpos pandas dataframe with output from pvlib solar position for each timestep - metdata.sunrisesetdata pandas dataframe with sunrise, sunset and adjusted time data. - metdata.tracker_theta (list) tracker tilt angle from pvlib for each timestep - metdata.surface_tilt (list) tracker surface tilt angle from pvlib for each timestep - metdata.surface_azimuth (list) tracker surface azimuth angle from pvlib for each timestep - ''' + trackerdict : dictionary + Keys for tracker tilt angles and + list of csv metfile, and datetimes at that angle + trackerdict[angle]['csvfile';'surf_azm';'surf_tilt';'UTCtime'] + Note: this output is mostly used for the cumulativesky approach. + metdata.solpos : dataframe + Pandas dataframe with output from pvlib solar position for each timestep + metdata.sunrisesetdata : + Pandas dataframe with sunrise, sunset and adjusted time data. + metdata.tracker_theta : list + Tracker tilt angle from pvlib for each timestep + metdata.surface_tilt : list + Tracker surface tilt angle from pvlib for each timestep + metdata.surface_azimuth : list + Tracker surface azimuth angle from pvlib for each timestep + """ + # #DocumentationCheck : trackerdict Note of output still valid? I don't think so + # Also -- is that metdata.solpos and sunrisesetdata properly documented as a return of this function? + #axis_tilt = 0 # only support 0 tilt trackers for now self.cumulativesky = cumulativesky # track whether we're using cumulativesky or gendaylit @@ -2534,7 +2833,8 @@ def set1axis(self, cumulativesky=True, axis_azimuth=180, limit_angle=45, angledelta, axis_tilt = axis_tilt, backtrack = backtrack, - gcr = gcr ) + gcr = gcr, + fixed_tilt_angle=fixed_tilt_angle) # get list of unique rounded tracker angles theta_list = trackingdata.dropna()['theta_round'].unique() @@ -2565,45 +2865,66 @@ def set1axis(self, cumulativesky=True, axis_azimuth=180, limit_angle=45, def _getTrackingAngles(self, axis_azimuth=180, limit_angle=45, angledelta=None, axis_tilt=0, backtrack=True, - gcr = 1.0/3.0 ): + gcr = 1.0/3.0, fixed_tilt_angle=None): ''' Helper subroutine to return 1-axis tracker tilt and azimuth data. - Input Parameter - ------------------ + Parameters + ---------- same as pvlib.tracking.singleaxis, plus: + angledelta : degrees + Angle to round tracker_theta to. This is for + cumulativesky simulations. Other input options: None (no + rounding of tracker angle) + fixed_tilt_angle : (Optional) degrees + This changes to a fixed tilt simulation where each hour uses fixed_tilt_angle + and axis_azimuth as the tilt and azimuth - angledelta: angle in degrees to round tracker_theta to. This is for - - returns - ------------------ + Returns + ------- DataFrame with the following columns: - - * tracker_theta: The rotation angle of the tracker. - tracker_theta = 0 is horizontal, and positive rotation angles are - clockwise. - * aoi: The angle-of-incidence of direct irradiance onto the - rotated panel surface. - * surface_tilt: The angle between the panel surface and the earth - surface, accounting for panel rotation. - * surface_azimuth: The azimuth of the rotated panel, determined by - projecting the vector normal to the panel's surface to the earth's - surface. - * 'theta_round' : tracker_theta rounded to the nearest 'angledelta'. - If no angledelta is specified, it is rounded to the nearest degree. + * tracker_theta: The rotation angle of the tracker. + tracker_theta = 0 is horizontal, and positive rotation angles are + clockwise. + * aoi: The angle-of-incidence of direct irradiance onto the + rotated panel surface. + * surface_tilt: The angle between the panel surface and the earth + surface, accounting for panel rotation. + * surface_azimuth: The azimuth of the rotated panel, determined by + projecting the vector normal to the panel's surface to the earth's + surface. + * 'theta_round' : tracker_theta rounded to the nearest 'angledelta'. + If no angledelta is specified, it is rounded to the nearest degree. ''' import pvlib - import numpy as np - + #import numpy as np + #import pandas as pd + solpos = self.solpos - # get 1-axis tracker tracker_theta, surface_tilt and surface_azimuth - trackingdata = pvlib.tracking.singleaxis(solpos['zenith'], - solpos['azimuth'], - axis_tilt, - axis_azimuth, - limit_angle, - backtrack, - gcr) + + #New as of 0.3.2: pass fixed_tilt_angle and switches to FIXED TILT mode + + if fixed_tilt_angle is not None: + # fixed tilt system with tilt = fixed_tilt_angle and + # azimuth = axis_azimuth + pvsystem = pvlib.pvsystem.PVSystem(fixed_tilt_angle,axis_azimuth) + # trackingdata keys: 'tracker_theta', 'aoi', 'surface_azimuth', 'surface_tilt' + trackingdata = pd.DataFrame({'tracker_theta':limit_angle, + 'aoi':pvsystem.get_aoi( + solpos['zenith'], + solpos['azimuth']), + 'surface_azimuth':axis_azimuth, + 'surface_tilt':limit_angle}) + else: + # get 1-axis tracker tracker_theta, surface_tilt and surface_azimuth + trackingdata = pvlib.tracking.singleaxis(solpos['zenith'], + solpos['azimuth'], + axis_tilt, + axis_azimuth, + limit_angle, + backtrack, + gcr) + # save tracker tilt information to metdata.tracker_theta, # metdata.surface_tilt and metdata.surface_azimuth self.tracker_theta = np.round(trackingdata['tracker_theta'],2).tolist() @@ -2616,11 +2937,15 @@ def _getTrackingAngles(self, axis_azimuth=180, limit_angle=45, # round tracker_theta to increments of angledelta for use in cumulativesky def _roundArbitrary(x, base=angledelta): - # round to nearest 'base' value. - # mask NaN's to avoid rounding error message + # round to nearest 'base' value. + # mask NaN's to avoid rounding error message return base * (x.dropna()/float(base)).round() - if angledelta is not None: + if angledelta == 0: + raise ZeroDivisionError('Angledelta = 0. Use None instead') + elif angledelta is None: # don't round theta + trackingdata['theta_round'] = trackingdata['tracker_theta'] + else: # round theta trackingdata['theta_round'] = \ _roundArbitrary(trackingdata['tracker_theta'], angledelta) @@ -2632,32 +2957,33 @@ def _makeTrackerCSV(self, theta_list, trackingdata): rounded tracker angle. Return a dictionary with the new csv filenames and other details, Used for cumulativesky tracking - Input Parameter - ------------------ - theta_list: array of unique tracker angle values - - trackingdata: Pandas Series with hourly tracker angles from - pvlib.tracking.singleaxis + Parameters + ----------- + theta_list : array + Array of unique tracker angle values - returns - ------------------ + trackingdata : Pandas + Pandas Series with hourly tracker angles from + :pvlib.tracking.singleaxis - trackerdict [dictionary] - keys: *theta_round tracker angle (default: -45 to +45 in + Returns + -------- + trackerdict : dictionary + keys: *theta_round tracker angle (default: -45 to +45 in 5 degree increments). - sub-array keys: - *datetime: array of datetime strings in this group of angles - *count: number of datapoints in this group of angles - *surf_azm: tracker surface azimuth during this group of angles - *surf_tilt: tilt angle average during this group of angles - *csvfile: name of csv met data file saved in /EPWs/ + sub-array keys: + *datetime: array of datetime strings in this group of angles + *count: number of datapoints in this group of angles + *surf_azm: tracker surface azimuth during this group of angles + *surf_tilt: tilt angle average during this group of angles + *csvfile: name of csv met data file saved in /EPWs/ ''' - datetime = pd.to_datetime(self.datetime) + dt = pd.to_datetime(self.datetime) trackerdict = dict.fromkeys(theta_list) - for theta in list(trackerdict) : + for theta in sorted(trackerdict): trackerdict[theta] = {} csvfile = os.path.join('EPWs', '1axis_{}.csv'.format(theta)) tempdata = trackingdata[trackingdata['theta_round'] == theta] @@ -2674,7 +3000,7 @@ def _makeTrackerCSV(self, theta_list, trackingdata): ghi_temp = [] dhi_temp = [] for g, d, time in zip(self.ghi, self.dhi, - datetime.strftime('%Y-%m-%d %H:%M:%S')): + dt.strftime('%Y-%m-%d %H:%M:%S')): # is this time included in a particular theta_round angle? if time in datetimetemp: @@ -2702,15 +3028,20 @@ def _makeTrackerCSV(self, theta_list, trackingdata): class AnalysisObj: - ''' - Analysis class for plotting and reporting - ''' + """ + Analysis class for performing raytrace to obtain irradiance measurements + at the array, as well plotting and reporting results + """ + def __init__(self, octfile=None, name=None): self.octfile = octfile self.name = name def makeImage(self, viewfile, octfile=None, name=None, hpc=False): - 'make visible image of octfile, viewfile' + """ + Makes a visible image (rendering) of octfile, viewfile + """ + import time if octfile is None: @@ -2738,12 +3069,16 @@ def makeImage(self, viewfile, octfile=None, name=None, hpc=False): " > images/"+name+viewfile[:-3] +".hdr") def makeFalseColor(self, viewfile, octfile=None, name=None): - '''make false-color plot of octfile, viewfile - Note: for Windows requires installation of falsecolor.exe, - which is part of radwinexe-5.0.a.8-win64.zip found at - http://www.jaloxa.eu/resources/radiance/radwinexe.shtml - TODO: error checking for installation of falsecolor.exe with download suggestion - ''' + """ + Makes a false-color plot of octfile, viewfile + + .. note:: + For Windows requires installation of falsecolor.exe, + which is part of radwinexe-5.0.a.8-win64.zip found at + http://www.jaloxa.eu/resources/radiance/radwinexe.shtml + """ + #TODO: error checking for installation of falsecolor.exe with download suggestion + if octfile is None: octfile = self.octfile if name is None: @@ -2777,14 +3112,42 @@ def makeFalseColor(self, viewfile, octfile=None, name=None): print('possible solution: install radwinexe binary package from ' 'http://www.jaloxa.eu/resources/radiance/radwinexe.shtml') - def linePtsMakeDict(self, linePtsDict): + def _linePtsArray(self, linePtsDict): + """ + Helper function to just print the x y and z values in an array format, + just like they will show in the .csv result files. + + """ + xstart = linePtsDict['xstart'] + ystart = linePtsDict['ystart'] + zstart = linePtsDict['zstart'] + xinc = linePtsDict['xinc'] + yinc = linePtsDict['yinc'] + zinc = linePtsDict['zinc'] + Nx = int(linePtsDict['Nx']) + Ny = int(linePtsDict['Ny']) + Nz = int(linePtsDict['Nz']) + + x = [] + y = [] + z = [] + + for iz in range(0,Nz): + for iy in range(0,Ny): + x . append(xstart+iy*xinc) + y . append(ystart+iy*yinc) + z . append(zstart+iy*zinc) + + return x, y, z + + def _linePtsMakeDict(self, linePtsDict): a = linePtsDict - linepts = self.linePtsMake3D(a['xstart'],a['ystart'],a['zstart'], + linepts = self._linePtsMake3D(a['xstart'],a['ystart'],a['zstart'], a['xinc'], a['yinc'], a['zinc'], a['Nx'],a['Ny'],a['Nz'],a['orient']) return linepts - def linePtsMake3D(self, xstart, ystart, zstart, xinc, yinc, zinc, + def _linePtsMake3D(self, xstart, ystart, zstart, xinc, yinc, zinc, Nx, Ny, Nz, orient): #linePtsMake(xpos,ypos,zstart,zend,Nx,Ny,Nz,dir) #create linepts text input with variable x,y,z. @@ -2799,7 +3162,6 @@ def linePtsMake3D(self, xstart, ystart, zstart, xinc, yinc, zinc, for iz in range(0,Nz): - zpos = zstart+iz*zinc for iy in range(0,Ny): ypos = ystart+iy*yinc xpos = xstart+iy*xinc @@ -2808,10 +3170,10 @@ def linePtsMake3D(self, xstart, ystart, zstart, xinc, yinc, zinc, ' '+str(zpos) + ' ' + orient + " \r" return(linepts) - def irrPlotNew(self, octfile, linepts, mytitle=None, plotflag=None, + def _irrPlot(self, octfile, linepts, mytitle=None, plotflag=None, accuracy='low', hpc=False): - ''' - (plotdict) = irrPlotNew(linepts,title,time,plotflag, accuracy) + """ + (plotdict) = _irrPlot(linepts,title,time,plotflag, accuracy) irradiance plotting using rtrace pass in the linepts structure of the view along with a title string for the plots. note that the plots appear in a blocking way unless @@ -2819,23 +3181,30 @@ def irrPlotNew(self, octfile, linepts, mytitle=None, plotflag=None, Parameters ------------ - octfile - filename and extension of .oct file - linepts - output from linePtsMake3D - mytitle - title to append to results files - plotflag - true or false - include plot of resulting irradiance - accuracy - either 'low' (default - faster) or 'high' - (better for low light) - hpc - boolean, default False. Waits for octfile for a - longer time if parallel processing. + octfile : string + Filename and extension of .oct file + linepts : + Output from :py:class:`bifacial_radiance.AnalysisObj._linePtsMake3D` + mytitle : string + Title to append to results files + plotflag : Boolean + Include plot of resulting irradiance + accuracy : string + Either 'low' (default - faster) or 'high' + (better for low light) + hpc : boolean, default False. Waits for octfile for a + Longer time if parallel processing. Returns ------- - out.x,y,z - coordinates of point - .r,g,b - r,g,b values in Wm-2 - .Wm2 - equal-weight irradiance - .mattype - material intersected - .title - title passed in - ''' + out : dictionary + out.x,y,z - coordinates of point + .r,g,b - r,g,b values in Wm-2 + .Wm2 - equal-weight irradiance + .mattype - material intersected + .title - title passed in + """ + if mytitle is None: mytitle = octfile[:-4] @@ -2873,7 +3242,7 @@ def irrPlotNew(self, octfile, linepts, mytitle=None, plotflag=None, #rtrace ambient values set for 'very accurate': cmd = "rtrace -i -ab 5 -aa .08 -ar 512 -ad 2048 -as 512 -h -oovs "+ octfile else: - print('irrPlotNew accuracy options: "low" or "high"') + print('_irrPlot accuracy options: "low" or "high"') return({}) @@ -2900,6 +3269,7 @@ def irrPlotNew(self, octfile, linepts, mytitle=None, plotflag=None, if plotflag is True: + import matplotlib.pyplot as plt plt.figure() plt.plot(out['Wm2']) plt.ylabel('Wm2 irradiance') @@ -2911,13 +3281,16 @@ def irrPlotNew(self, octfile, linepts, mytitle=None, plotflag=None, return(out) - def saveResults(self, data, reardata=None, savefile=None): - ''' - saveResults - function to save output from irrPlotNew + def _saveResults(self, data, reardata=None, savefile=None): + """ + Function to save output from _irrPlot If rearvals is passed in, back ratio is saved - Returns: savefile - ''' + Returns + -------- + savefile : str + If set to None, will write to default .csv filename in results folder. + """ if savefile is None: savefile = data['title'] + '.csv' @@ -2950,36 +3323,86 @@ def saveResults(self, data, reardata=None, savefile=None): else: df = pd.DataFrame.from_dict(data_sub) df.to_csv(os.path.join("results", savefile), sep = ',', - columns = ['x','y','z','mattype','Wm2'], index = False) + columns = ['x','y','z', 'mattype','Wm2'], index = False) print('Saved: %s'%(os.path.join("results", savefile))) return os.path.join("results", savefile) + def _saveResultsCumulative(self, data, reardata=None, savefile=None): + """ + TEMPORARY FUNCTION -- this is a fix to save ONE cumulative results csv + in the main working folder for when doing multiple entries in a + tracker dict. + + Returns + -------- + savefile : str + If set to None, will write to default .csv filename in results folder. + """ + + if savefile is None: + savefile = data['title'] + '.csv' + # make dataframe from results + data_sub = {key:data[key] for key in ['x', 'y', 'z', 'Wm2', 'mattype']} + self.x = data['x'] + self.y = data['y'] + self.z = data['z'] + self.mattype = data['mattype'] + #TODO: data_sub front values don't seem to be saved to self. + if reardata is not None: + self.rearX = reardata['x'] + self.rearY = reardata['y'] + self.rearMat = reardata['mattype'] + data_sub['rearMat'] = self.rearMat + self.rearZ = reardata['z'] + data_sub['rearZ'] = self.rearZ + self.Wm2Front = data_sub.pop('Wm2') + data_sub['Wm2Front'] = self.Wm2Front + self.Wm2Back = reardata['Wm2'] + data_sub['Wm2Back'] = self.Wm2Back + self.backRatio = [x/(y+.001) for x,y in zip(reardata['Wm2'],data['Wm2'])] # add 1mW/m2 to avoid dividebyzero + data_sub['Back/FrontRatio'] = self.backRatio + df = pd.DataFrame.from_dict(data_sub) + df.to_csv(savefile, sep = ',', + columns = ['x','y','z','rearZ','mattype','rearMat', + 'Wm2Front','Wm2Back','Back/FrontRatio'], + index = False) # new in 0.2.3 + + else: + df = pd.DataFrame.from_dict(data_sub) + df.to_csv(savefile, sep = ',', + columns = ['x','y','z', 'mattype','Wm2'], index = False) + + print('Saved: %s'%(savefile)) + return (savefile) def moduleAnalysis(self, scene, modWanted=None, rowWanted=None, sensorsy=9.0, debug=False): - ''' - (frontscan, backscan) = moduleAnalysis(scene, modWanted, rowWanted, sensorsy) - - Definition of the Radiance scan points used in rtrace. + """ + This function defines the scan points to be used in the :py:class:`~bifacial_radiance.AnalysisObj.analysis` function, + to perform the raytrace through Radiance function `rtrace` Parameters ------------ - scene - SceneObj generated with makeScene. - These details are used to identify scan points. - modWanted - output from linePtsMake3D - rowWanted - title to append to results files - sensorsy - number of - debug - boolean + scene : ``SceneObj`` + Generated with :py:class:`~bifacial_radiance.RadianceObj.makeScene`. + modWanted : int + Module wanted to sample. If none, defaults to center module (rounding down) + rowWanted : int + Row wanted to sample. If none, defaults to center row (rounding down) + sensorsy : int + Number of 'sensors' or scanning points along the collector width (CW) of the module(s)/ + debug : bool + Activates various print statemetns for debugging this function. Returns ------- - (frontscan, backscan) - tuple of scanDict for front and backside scan - that is passed into `analysis` function - + frontscan : dictionary + Scan dictionary for module's front side. Used to pass into :py:class:`~bifacial_radiance.AnalysisObj.analysis` function + backscan : dictionary + Scan dictionary for module's back side. Used to pass into :py:class:`~bifacial_radiance.AnalysisObj.analysis` function - - ''' + """ # Height: clearance height for fixed tilt systems, or torque tube # height for single-axis tracked systems. @@ -3147,54 +3570,65 @@ def moduleAnalysis(self, scene, modWanted=None, rowWanted=None, return frontscan, backscan def analysis(self, octfile, name, frontscan, backscan, plotflag=False, accuracy='low'): - ''' - analysis(octfile,name,frontscan,backscan,plotflag, accuracy) - general analysis where linescan is passed in - - pass in the linepts structure of the view along with a title string for the plots - note that the plots appear in a blocking way unless you call pylab magic in the beginning. + """ + General analysis function, where linepts are passed in for calling the + raytrace routine :py:class:`~bifacial_radiance.AnalysisObj._irrPlot` + and saved into results with + :py:class:`~bifacial_radiance.AnalysisObj._saveResults`. + + This function can also pass in the linepts structure of the view + along with a title string for the plots note that the plots appear in + a blocking way unless you call pylab magic in the beginning Parameters ------------ - octfile - filename and extension of .oct file - name - string name to append to output files - frontscan - scene.frontscan object - backscan - scene.backscan object - plotflag - true or false - include plot of resulting irradiance - accuracy - either 'low' (default - faster) or 'high' (better for low light) + name : string + Name to append to output files + octfile : string + Filename and extension of .oct file + frontscan : scene.frontscan object + Object with the sensor location information for the front of the module + backscan : scene.backscan object + Object with the sensor location information for the rear side of the module + plotflag : boolean + Include plot of resulting irradiance + accuracy : string + Either 'low' (default - faster) or 'high' (better for low light) Returns ------- - None. file saved in \results\irr_name.csv - ''' - # + File saved in `\\results\\irr_name.csv` + + """ + if octfile is None: print('Analysis aborted - no octfile \n') return None, None - linepts = self.linePtsMakeDict(frontscan) - frontDict = self.irrPlotNew(octfile, linepts, name+'_Front', + linepts = self._linePtsMakeDict(frontscan) + frontDict = self._irrPlot(octfile, linepts, name+'_Front', plotflag=plotflag, accuracy=accuracy) #bottom view. - linepts = self.linePtsMakeDict(backscan) - backDict = self.irrPlotNew(octfile, linepts, name+'_Back', + linepts = self._linePtsMakeDict(backscan) + backDict = self._irrPlot(octfile, linepts, name+'_Back', plotflag=plotflag, accuracy=accuracy) - # don't save if irrPlotNew returns an empty file. + # don't save if _irrPlot returns an empty file. if frontDict is not None: - self.saveResults(frontDict, backDict,'irr_%s.csv'%(name) ) + self._saveResults(frontDict, backDict,'irr_%s.csv'%(name) ) return frontDict, backDict def runJob(daydate): - ''' - runjob(daydate) - runJob routine for the HPC, assigns each daydate to a different node and performs all the + """ + Routine for the HPC, assigns each daydate to a different node and performs all the bifacial radiance tasks. Parameters ------------ - daydate - string 'MM_dd' corresponding to month_day i.e. '02_17' February 17th. - ''' + daydate : string + 'MM_dd' corresponding to month_day i.e. '02_17' February 17th. + + """ try: slurm_nnodes = int(os.environ['SLURM_NNODES']) @@ -3229,57 +3663,56 @@ def runJob(daydate): sensorsy=sensorsy) def hpcExample(): - ''' Example of HPC Job call + """ + Example of HPC Job call This allocates the day_dates generated to the different codes in as many nodes are available. Works inside and outside of slurm for testing (but set FullYear to False so it only does two days) Full year takes 1 min in 11 Nodes. - -->> Variables stored in input_bf.py SO: - #Modify this on top: - if __name__ == "__main__": #in case this is run as a script not a module. - from readepw import readepw - from load import loadTrackerDict - from input_bf import * - - else: # module imported or loaded normally - from bifacial_radiance.readepw import readepw # epw file reader from pvlib development forums #module load format - from bifacial_radiance.load import loadTrackerDict - from bifacial_radiance.input_bf import * - - Procedure for a Full Year Run (~1 min in 11 nodes of 36 cores each > 365 days): - -connect to Eagle - - $ cd bifacial_radiance/bifacial_radiance - - $ srun -A pvsoiling -t 5 -N 11 --pty bash - - $ module load conda - - $ . activate py3 - - $ srun bifacial_radiance2.py + Variables stored in input_bf.py. First configure this on top: + + .. code-block :: python + if __name__ == "__main__": #in case this is run as a script not a module. + from readepw import readepw + from load import loadTrackerDict + from input_bf import * + + else: # module imported or loaded normally + from bifacial_radiance.readepw import readepw # epw file reader from pvlib development forums #module load format + from bifacial_radiance.load import loadTrackerDict + from bifacial_radiance.input_bf import * + + Procedure for a Full Year Run (~1 min in 11 nodes of 36 cores each > 365 days): + + .. code-block :: python + + Connect to Eagle + - $ cd bifacial_radiance/bifacial_radiance + - $ srun -A pvsoiling -t 5 -N 11 --pty bash + - $ module load conda + - $ . activate py3 + - $ srun bifacial_radiance2.py Procedure for testing before joining SLURM: - - $ cd bifacial_radiance/bifacial_radiance - - $ module load conda - - $ . activate py3 - - $ nano bifacial_radiance.py - change fullYear to False. - - $ python bifacial_radiance2.py + .. code-block :: python + + change fullYear to False. + - $ cd bifacial_radiance/bifacial_radiance + - $ module load conda + - $ . activate py3 + - $ nano bifacial_radiance.py + - $ python bifacial_radiance2.py - Other important random notes: - Do not load conda twice nor activate .py3 twice. - (following above) Either activate conda or .py3 in the login node - or on the slurm - - TO DO: - # Test as a function (I usually replace the main section's content - with this function's content. - # Figure why loading conda twice crashes - # Do a batch file to run this maybe? - # More elegant way to read values from .py than importing - (only works on declarations at the beginning) - + .. warning:: + Do not load conda twice nor activate .py3 twice. + (following above) Either activate conda or .py3 in the login node + or on the slurm + + """ - ''' import multiprocessing as mp daylist = [] @@ -3299,7 +3732,6 @@ def hpcExample(): for date in date_generated: daylist.append(date.strftime("%m_%d")) - # print("This is daydate %s" % (daydate)) demo = RadianceObj(simulationname,path=testfolder) demo.setGround(albedo) @@ -3339,7 +3771,6 @@ def hpcExample(): def quickExample(): """ - Example of how to run a Radiance routine for a simple rooftop bifacial system """ @@ -3391,7 +3822,7 @@ def _interactive_directory(title=None): analysis = bifacial_radiance.AnalysisObj(octfile, demo.name) frontscan, backscan = analysis.moduleAnalysis(scene, sensorsy=9) analysis.analysis(octfile, demo.name, frontscan, backscan) - + # bifacial ratio should be 12.8% - 12.9% ! print('Annual bifacial ratio average: %0.3f' %( sum(analysis.Wm2Back) / sum(analysis.Wm2Front) ) ) diff --git a/bifacial_radiance/mismatch.py b/bifacial_radiance/mismatch.py new file mode 100644 index 00000000..21863526 --- /dev/null +++ b/bifacial_radiance/mismatch.py @@ -0,0 +1,335 @@ +# -*- coding: utf-8 -*- +""" +Created on Tue Mar 26 20:16:47 2019 + +@author: sayala +""" + +#from load import * + + + +def _sensorupsampletocellsbyInterpolation(df, cellsy): + ''' + + Function for when sensorsy in the results are less than cellsy desired. + Interpolates the dataframe. + + #2DO: improve interpolation with pandas. right onw it's row by row. + + _sensorupsampletocellsbyInterpolation(df, cellsy) + ''' + + import pandas as pd + import numpy as np + + sensorsy = len(df) + + #2DO: Update this section to match bifacialvf + cellCenterPVM=[] + for i in range (0, cellsy): + cellCenterPVM.append((i*sensorsy/cellsy+(i+1)*sensorsy/cellsy)/2) + + df2 = pd.DataFrame() + for j in range (0, len(df.keys())): + A = list(df[df.keys()[j]]) + B= np.interp(cellCenterPVM, list(range(0,sensorsy)), A) + df2[df.keys()[j]]=B + + return df2 + +def _sensorsdownsampletocellsbyAverage(df, cellsy): + ''' + df = dataframe with rows indexed by number (i.e. 0 to sensorsy) where sensorsy > cellsy + cellsy = int. usually 8 or 12. + + example: + F_centeraverages = _sensorsdownsampletocellsbyAverage(F, cellsy) + ''' + import numpy as np + import pandas as pd + + edges=len(df)-np.floor(len(df)/(cellsy))*(cellsy) + edge1=int(np.floor(edges/2)) + edge2=int(edges-edge1) + A = list(range(df.index[0]+edge1, df.index[-1]-edge2+2, int(np.floor(len(df)/(cellsy))))) + B = range(0,len(A)-1,1) + C = [df.iloc[A[x]:A[x+1]].mean(axis=0) for x in B] + df_centeraverages=pd.DataFrame(C) + + return df_centeraverages + +def _sensorsdownsampletocellbyCenter(df, cellsy): + ''' + df = dataframe with rows indexed by number (i.e. 0 to sensorsy) where sensorsy > cellsy + cellsy = int. usually 8 or 12. + + example: + F_centervalues = _sensorsdownsampletocellbyCenter(F, cellsy) + ''' + + import numpy as np + + + edges=len(df)-np.floor(len(df)/(cellsy))*(cellsy) + edge1=int(np.floor(edges/2)) + edge2=int(edges-edge1) + A = list(range(df.index[0]+edge1, df.index[-1]-edge2+2, int(np.floor(len(df)/(cellsy))))) + A = [int(x+(A[1]-A[0])*0.5) for x in A] + A = A[:-1] + df_centervalues=df.loc[A] + df_centervalues=df_centervalues.reset_index(drop=True) + + return df_centervalues + +def _setupforPVMismatch(portraitorlandscape, sensorsy, numcells=72): + r''' Sets values for calling PVMismatch, for ladscape or portrait modes and + + Example: + stdpl, cellsx, cellsy = _setupforPVMismatch(portraitorlandscape='portrait', sensorsy=100): + ''' + + import numpy as np + + # cell placement for 'portrait'. + if numcells == 72: + stdpl=np.array([[0, 23, 24, 47, 48, 71], + [1, 22, 25, 46, 49, 70], + [2, 21, 26, 45, 50, 69], + [3, 20, 27, 44, 51, 68], + [4, 19, 28, 43, 52, 67], + [5, 18, 29, 42, 53, 66], + [6, 17, 30, 41, 54, 65], + [7, 16, 31, 40, 55, 64], + [8, 15, 32, 39, 56, 63], + [9, 14, 33, 38, 57, 62], + [10, 13, 34, 37, 58, 61], + [11, 12, 35, 36, 59, 60]]) + + elif numcells == 96: + stdpl=np.array([[0, 23, 24, 47, 48, 71, 72, 95], + [1, 22, 25, 46, 49, 70, 73, 94], + [2, 21, 26, 45, 50, 69, 74, 93], + [3, 20, 27, 44, 51, 68, 75, 92], + [4, 19, 28, 43, 52, 67, 76, 91], + [5, 18, 29, 42, 53, 66, 77, 90], + [6, 17, 30, 41, 54, 65, 78, 89], + [7, 16, 31, 40, 55, 64, 79, 88], + [8, 15, 32, 39, 56, 63, 80, 87], + [9, 14, 33, 38, 57, 62, 81, 86], + [10, 13, 34, 37, 58, 61, 82, 85], + [11, 12, 35, 36, 59, 60, 83, 84]]) + else: + print("Error. Only 72 and 96 cells modules supported at the moment. Change numcells to either of this options!") + return + + if portraitorlandscape == 'landscape': + stdpl = stdpl.transpose() + elif portraitorlandscape != 'portrait': + print("Error. portraitorlandscape variable must either be 'landscape' or 'portrait'") + return + + cellsx = len(stdpl[1]); cellsy = len(stdpl) + + return stdpl, cellsx, cellsy + + +def calculatePVMismatch(stdpl, cellsx, cellsy, Gpoat): + r''' calls PVMismatch with all the pre-generated values on bifacial_radiance + + Example: + PowerAveraged, PowerDetailed = def calculatePVMismatch(stdpl, cellsx, cellsy, Gpoat) + + ''' + + import pvmismatch # this imports everything we need + import numpy as np + + if np.mean(Gpoat) < 0.001: + PowerAveraged = 0 + PowerDetailed = 0 + else: + + if cellsx*cellsy == 72: + cell_pos = pvmismatch.pvmismatch_lib.pvmodule.STD72 + elif cellsx*cellsy == 96: + cell_pos = pvmismatch.pvmismatch_lib.pvmodule.STD96 + else: + print("Error. Only 72 and 96 cells modules supported at the moment. Change numcells to either of this options!") + return + + pvmod=pvmismatch.pvmismatch_lib.pvmodule.PVmodule(cell_pos=cell_pos) + # makes the system # 1 module, in portrait mode. + pvsys = pvmismatch.pvsystem.PVsystem(numberStrs=1, numberMods=1, pvmods=pvmod) + + G=np.array([Gpoat]).transpose() + H = np.ones([1,cellsx]) + array_det = np.dot(G,H) + array_avg = np.ones([cellsy,cellsx])*np.mean(Gpoat) + + # ACtually do calculations + pvsys.setSuns({0: {0: [array_avg, stdpl]}}) + PowerAveraged=pvsys.Pmp + + pvsys.setSuns({0: {0: [array_det, stdpl]}}) + PowerDetailed=pvsys.Pmp + + return PowerAveraged, PowerDetailed + +def mad_fn(data): + # EUPVSEC 2019 Chris Version + # return MAD / Average for a 1D array + import numpy as np + + return (np.abs(np.subtract.outer(data,data)).sum()/float(data.__len__())**2 / np.mean(data))*100 + + + +def analysisIrradianceandPowerMismatch(testfolder, writefiletitle, portraitorlandscape, bififactor, numcells=72, downsamplingmethod='byCenter'): + r''' + Use this when sensorsy calculated with bifacial_radiance > cellsy + + Reads and calculates power output and mismatch for each file in the + testfolder where all the bifacial_radiance irradiance results .csv are saved. + First load each file, cleans it and resamples it to the numsensors set in this function, + and then calculates irradiance mismatch and PVMismatch power output for averaged, minimum, + or detailed irradiances on each cell for the cases of A) only 12 or 8 downsmaples values are + considered (at the center of each cell), and B) 12 or 8 values are obtained from averaging + all the irradiances falling in the area of the cell (No edges or inter-cell spacing are considered + at this moment). Then it saves all the A and B irradiances, as well as the cleaned/resampled + front and rear irradiances. + + Ideally sensorsy in the read data is >> 12 to give results for the irradiance mismatch in the cell. + + Also ideally n + + Parameters + ---------- + testfolder: folder containing output .csv files for bifacial_radiance + writefiletitle: .csv title where the output results will be saved. + portraitorlandscape: 'portrait' or 'landscape', for PVMismatch input + which defines the electrical interconnects inside the module. + bififactor: bifaciality factor of the module. Max 1.0. ALL Rear irradiance values saved include the bifi-factor. + downsampling method: 1 - 'byCenter' - 2 - 'byAverage' + + Example: + + # User information. + import bifacial_radiance + testfolder=r'C:\Users\sayala\Documents\HPC_Scratch\EUPVSEC\HPC Tracking Results\RICHMOND\Bifacial_Radiance Results\PVPMC_0\results' + writefiletitle= r'C:\Users\sayala\Documents\HPC_Scratch\EUPVSEC\HPC Tracking Results\RICHMOND\Bifacial_Radiance Results\PVPMC_0\test_df.csv' + sensorsy=100 + portraitorlandscape = 'portrait' + analysis.analysisIrradianceandPowerMismatch(testfolder, writefiletitle, portraitorlandscape, bififactor=1.0, numcells=72) + + ''' + from bifacial_radiance import load + import os, glob + import pandas as pd + + # Default variables + numpanels=1 # 1 at the moment, necessary for the cleaning routine. + automatic=True + + #loadandclean + # testfolder = r'C:\Users\sayala\Documents\HPC_Scratch\EUPVSEC\PinPV_Bifacial_Radiance_Runs\HPCResults\df4_FixedTilt\FixedTilt_Cairo_C_0.15\results' + filelist = sorted(os.listdir(testfolder)) + #filelist = sorted(glob.glob(os.path.join('testfolder','*.csv'))) + print('{} files in the directory'.format(filelist.__len__())) + + # Check number of sensors on data. + temp = load.read1Result(os.path.join(testfolder,filelist[0])) + sensorsy = len(temp) + + # Setup PVMismatch parameters + stdpl, cellsx, cellsy = _setupforPVMismatch(portraitorlandscape=portraitorlandscape, sensorsy=sensorsy, numcells=numcells) + + F=pd.DataFrame() + B=pd.DataFrame() + for z in range(0, filelist.__len__()): + data=load.read1Result(os.path.join(testfolder,filelist[z])) + [frontres, backres] = load.deepcleanResult(data, sensorsy=sensorsy, numpanels=numpanels, automatic=automatic) + F[filelist[z]]=frontres + B[filelist[z]]=backres + + B = B*bififactor + # Downsample routines: + if sensorsy > cellsy: + if downsamplingmethod == 'byCenter': + print("Sensors y > cellsy; Downsampling data by finding CellCenter method") + F = _sensorsdownsampletocellbyCenter(F, cellsy) + B = _sensorsdownsampletocellbyCenter(B, cellsy) + elif downsamplingmethod == 'byAverage': + print("Sensors y > cellsy; Downsampling data by Averaging data into Cells method") + F = _sensorsdownsampletocellsbyAverage(F, cellsy) + B = _sensorsdownsampletocellsbyAverage(B, cellsy) + else: + print ("Sensors y > cellsy for your module. Select a proper downsampling method ('byCenter', or 'byAverage')") + return + elif sensorsy < cellsy: + print("Sensors y < cellsy; Upsampling data by Interpolation") + F = _sensorupsampletocellsbyInterpolation(F, cellsy) + B = _sensorupsampletocellsbyInterpolation(B, cellsy) + elif sensorsy == cellsy: + print ("Same number of sensorsy and cellsy for your module.") + F = F + B = B + + # Calculate POATs + Poat = F+B + + # Define arrays to fill in: + Pavg_all=[]; Pdet_all=[] + Pavg_front_all=[]; Pdet_front_all=[] + colkeys = F.keys() + + # Calculate powers for each hour: + for i in range(0,len(colkeys)): + Pavg, Pdet = calculatePVMismatch(stdpl=stdpl, cellsx=cellsx, cellsy=cellsy, Gpoat=list(Poat[colkeys[i]]/1000)) + Pavg_front, Pdet_front = calculatePVMismatch(stdpl, cellsx, cellsy, Gpoat= list(F[colkeys[i]]/1000)) + Pavg_all.append(Pavg) + Pdet_all.append(Pdet) + Pavg_front_all.append(Pavg_front) + Pdet_front_all.append(Pdet_front) + + ## Rename Rows and save dataframe and outputs. + F.index='FrontIrradiance_cell_'+F.index.astype(str) + B.index='BackIrradiance_cell_'+B.index.astype(str) + Poat.index='POAT_Irradiance_cell_'+Poat.index.astype(str) + + ## Transpose + F = F.T + B = B.T + Poat = Poat.T + + # Statistics Calculatoins + dfst=pd.DataFrame() + dfst['MAD/G_Total'] = Poat.apply(mad_fn,axis=1) + dfst['Front_MAD/G_Total'] = F.apply(mad_fn,axis=1) + dfst['MAD/G_Total**2'] = dfst['MAD/G_Total']**2 + dfst['Front_MAD/G_Total**2'] = dfst['Front_MAD/G_Total']**2 + dfst['poat'] = Poat.mean(axis=1) + dfst['gfront'] = F.mean(axis=1) + dfst['grear'] = B.mean(axis=1) + dfst['bifi_ratio'] = dfst['grear']/dfst['gfront'] + dfst['stdev'] = Poat.std(axis=1)/ dfst['poat'] + dfst.index=Poat.index.astype(str) + + # Power Calculations/Saving + Pout=pd.DataFrame() + Pout['Pavg']=Pavg_all + Pout['Pdet']=Pdet_all + Pout['Front_Pavg']=Pavg_front_all + Pout['Front_Pdet']=Pdet_front_all + Pout['Mismatch_rel'] = 100-(Pout['Pdet']*100/Pout['Pavg']) + Pout['Front_Mismatch_rel'] = 100-(Pout['Front_Pdet']*100/Pout['Front_Pavg']) + Pout.index=Poat.index.astype(str) + + ## Save CSV + df_all = pd.concat([Pout,dfst,Poat,F,B],axis=1) + df_all.to_csv(writefiletitle) + print("Saved Results to ", writefiletitle) + + + + diff --git a/bifacial_radiance/modelchain.py b/bifacial_radiance/modelchain.py index d0fda2a1..6241a8ba 100644 --- a/bifacial_radiance/modelchain.py +++ b/bifacial_radiance/modelchain.py @@ -5,9 +5,9 @@ @author: sayala """ -import bifacial_radiance +#import bifacial_radiance #from bifacial_radiance.config import * -import os +#import os # DATA_PATH = bifacial_radiance.main.DATA_PATH # directory with module.json etc. @@ -34,36 +34,76 @@ def _append_dicts(x, y): return z # create start/end string and list for the 1-axis tracking hourly workflow -def _returnTimeVals(t, trackerdict=None): +def _returnTimeVals(t=None, trackerdict=None): """ input: timeControlParamsDict, trackerdict (optional) - return startday (string), endday (string) in MM_DD format - return timelist (list) in MM_DD_HH format only if trackerdict passed in + + return timelist (list) in MM_DD_HH format. + startday (string), endday (string) are timelist[0] and [-1] + If timeControlParamsDict is None, default to full year """ + if t is None: # full year behavior by default + t = {'MonthStart':1,'MonthEnd':12,'DayStart':1,'DayEnd':31, + 'HourStart':1,'HourEnd':23} import datetime as dt - start = dt.datetime(2000,t['MonthStart'], - t['DayStart'],t['HourStart']) - end = dt.datetime(2000,t['MonthEnd'], - t['DayEnd'],t['HourEnd']) - startday = start.strftime("%m_%d") - endday = end.strftime("%m_%d") + try: + start = dt.datetime(2000,t['MonthStart'], + t['DayStart'],t['HourStart']) + end = dt.datetime(2000,t['MonthEnd'], + t['DayEnd'],t['HourEnd']) + except KeyError: # catch missing hour parameters + start = dt.datetime(2000,t['MonthStart'], + t['DayStart'],1) + end = dt.datetime(2000,t['MonthEnd'], + t['DayEnd'],23) + + startday = start.strftime("%m_%d_%H") + endday = end.strftime("%m_%d_%H") if trackerdict is None: - timelist = [] + timelist = [startday, endday] else: - dd = [(start + dt.timedelta(days=x/24)).strftime("%m_%d_%H") for x in range((end-start).days*24 + 1)] + #dd = [(start + dt.timedelta(days=x/24)).strftime("%m_%d_%H") \ + # for x in range(((end-start).days + 1)*24)] + dd = [(start + dt.timedelta(seconds=x*3600)).strftime("%m_%d_%H") \ + for x in range(int((end-start).total_seconds()/3600) +1)] timelist = (set(dd) & set(trackerdict.keys())) - return startday, endday, timelist - - -def runModelChain(simulationParamsDict, sceneParamsDict, timeControlParamsDict=None, moduleParamsDict=None, trackingParamsDict=None, torquetubeParamsDict=None, analysisParamsDict=None, cellLevelModuleParamsDict=None): - ''' - + return timelist +''' +def _returnTimeParams(simulationParamsDict, timeControlParamsDict): + simDict = simulationParamsDict + timeDict = timeControlParamsDict + + if simulationParamsDict['daydateSimulation']: # Start / end passed + starttime = (str(timeControlParamsDict['MonthStart'])+'_'+ + str(timeControlParamsDict['DayStart'])+'_'+ + str(timeControlParamsDict['HourStart']) ) + endtime = (str(timeControlParamsDict['MonthEnd'])+'_'+ + str(timeControlParamsDict['DayEnd'])+'_'+ + str(timeControlParamsDict['HourEnd']) ) + else: + starttime = None; endtime=None + + if simulationParamsDict['] +''' +def runModelChain(simulationParamsDict, sceneParamsDict, timeControlParamsDict=None, + moduleParamsDict=None, trackingParamsDict=None, torquetubeParamsDict=None, + analysisParamsDict=None, cellLevelModuleParamsDict=None): + """ This calls config.py values, which are arranged into dictionaries, - and runs all the respective processes based on the varaibles in the config.py. - - Still under testing! - ''' - + and runs all the respective processes based on the variables in the config.py. + + To import the variables from a .ini file, use:: + + (simulationParamsDict, sceneParamsDict, timeControlParamsDict, moduleParamsDict, + trackingParamsDict,torquetubeParamsDict,analysisParamsDict,cellLevelModuleParamsDict) = + bifacial_radiance.load.readconfigurationinputfile(inifile) + + """ + + import bifacial_radiance + import os + import numpy as np + if 'testfolder' not in simulationParamsDict: simulationParamsDict['testfolder'] = bifacial_radiance.main._interactive_directory( title='Select or create an empty directory for the Radiance tree') @@ -77,18 +117,35 @@ def runModelChain(simulationParamsDict, sceneParamsDict, timeControlParamsDict=N simulationParamsDict['testfolder'], 'simulation.ini') bifacial_radiance.load.savedictionariestoConfigurationIniFile(simulationParamsDict, sceneParamsDict, timeControlParamsDict, moduleParamsDict, trackingParamsDict, torquetubeParamsDict, analysisParamsDict, cellLevelModuleParamsDict, inifilename) + + # re-load configuration file to make sure all booleans are converted + (simulationParamsDict, sceneParamsDict, timeControlParamsDict, + moduleParamsDict, trackingParamsDict,torquetubeParamsDict, + analysisParamsDict,cellLevelModuleParamsDict) = \ + bifacial_radiance.load.readconfigurationinputfile(inifilename) + + # Load weatherfile - # All options for loading data: - if simulationParamsDict['weatherFile'][-3:] == 'epw': - if simulationParamsDict['getEPW']: - simulationParamsDict['weatherFile'] = demo.getEPW( - simulationParamsDict['latitude'], simulationParamsDict['longitude']) # pull TMY data for any global lat/lon + if simulationParamsDict['getEPW']: + simulationParamsDict['weatherFile'] = demo.getEPW( + simulationParamsDict['latitude'], simulationParamsDict['longitude']) # pull EPW data for any global lat/lon # If file is none, select a EPW file using graphical picker - metdata = demo.readEPW(simulationParamsDict['weatherFile']) - else: + #metdata = demo.readEPW(simulationParamsDict['weatherFile']) + #else: # If file is none, select a TMY file using graphical picker - metdata = demo.readTMY(simulationParamsDict['weatherFile']) - + #metdata = demo.readTMY(simulationParamsDict['weatherFile']) + # load in weatherfile. Check if start/end time + + if simulationParamsDict['daydateSimulation']: + timelist = _returnTimeVals(timeControlParamsDict) + starttime=timelist[0]; endtime=timelist[-1] + else: # read in full TMY file + starttime = None; endtime=None + + print('Reading weather file {}'.format(simulationParamsDict['weatherFile'])) + metdata = demo.readWeatherFile(simulationParamsDict['weatherFile'], + starttime=starttime, endtime=endtime) + # input albedo number or material name like 'concrete'. To see options, run this without any input. demo.setGround(sceneParamsDict['albedo']) analysis = None # initialize default analysis return value to none. @@ -126,6 +183,10 @@ def runModelChain(simulationParamsDict, sceneParamsDict, timeControlParamsDict=N cellLevelModuleParams=cellLevelModuleParams, **kwargs) + # TODO: Refactor as a state machine to run specific routines based on + # input flags. That might clean things up here a bit... + + if simulationParamsDict['tracking'] is False: # Fixed Routine # makeScene creates a .rad file with 20 modules per row, 7 rows. @@ -133,7 +194,7 @@ def runModelChain(simulationParamsDict, sceneParamsDict, timeControlParamsDict=N moduletype=simulationParamsDict['moduletype'], sceneDict=sceneParamsDict, hpc=simulationParamsDict['hpc']) if simulationParamsDict["cumulativeSky"]: - if simulationParamsDict['timestampRangeSimulation']: + if simulationParamsDict['daydateSimulation']: # was timestampRangeSimulation import datetime startdate = datetime.datetime(2001, timeControlParamsDict['MonthStart'], timeControlParamsDict['DayStart'], @@ -154,10 +215,19 @@ def runModelChain(simulationParamsDict, sceneParamsDict, timeControlParamsDict=N analysisParamsDict['sensorsy']) analysis.analysis(octfile, demo.name, frontscan, backscan) print('Bifacial ratio yearly average: %0.3f' % - (sum(analysis.Wm2Back) / sum(analysis.Wm2Front))) - - else: - if simulationParamsDict["timestampRangeSimulation"]: + (np.sum(analysis.Wm2Back) / np.sum(analysis.Wm2Front))) + + else: # Hourly simulation fixed tilt. Use new modified 1-axis tracking workflow + # Largely copies the existing 1-axis workflow from below, but + # forces trackerdict tilt and azimuth to be fixed. + + # + print('\n***Starting Fixed-tilt hourly simulation ***\n') + + + ## All the rest here is copied from below... + # Timestamp range selection + if simulationParamsDict["timestampRangeSimulation"]: # fixed tilt timestamp range for timeindex in range(timeControlParamsDict['timeindexstart'], timeControlParamsDict['timeindexend']): demo.gendaylit(metdata, timeindex) # Noon, June 17th # makeOct combines all of the ground, sky and object files into a .oct file. @@ -170,24 +240,59 @@ def runModelChain(simulationParamsDict, sceneParamsDict, timeControlParamsDict=N analysisParamsDict['sensorsy']) analysis.analysis(octfile, demo.name, frontscan, backscan) print('Bifacial ratio for %s average: %0.3f' % ( - metdata.datetime[timeindex], sum(analysis.Wm2Back) / sum(analysis.Wm2Front))) - else: # Run whole year - for timeindex in range(0, 8760): - demo.gendaylit(metdata, timeindex) # Noon, June 17th - # makeOct combines all of the ground, sky and object files into a .oct file. - octfile = demo.makeOct(demo.getfilelist()) - # return an analysis object including the scan dimensions for back irradiance - analysis = bifacial_radiance.AnalysisObj( - octfile, demo.name) - frontscan, backscan = analysis.moduleAnalysis(scene, analysisParamsDict['modWanted'], - analysisParamsDict['rowWanted'], - analysisParamsDict['sensorsy']) - analysis.analysis(octfile, demo.name, frontscan, backscan) - print('Bifacial ratio for %s average: %0.3f' % ( - metdata.datetime[timeindex], sum(analysis.Wm2Back) / sum(analysis.Wm2Front))) + metdata.datetime[timeindex], np.sum(analysis.Wm2Back) / np.sum(analysis.Wm2Front))) + else: # both daydateSimulation and full year uses this branch.. + #TODO: pytest for this section + trackerdict = demo.set1axis(cumulativesky=False, + fixed_tilt_angle=sceneParamsDict['tilt'], + axis_azimuth=sceneParamsDict['azimuth']) + # fixed_tilt_angle switches to constant fixed tilt mode. + + if not(simulationParamsDict['daydateSimulation']): # full year. + # use default behavior of _returnTimeVals to run + # full year simulation if you pass timeDict as none + timeControlParamsDict = None + print('\n***Full - year hourly simulation ***\n') + # _returnTimeVals returns proper string formatted start and end days. + timelist = _returnTimeVals(timeControlParamsDict) + startday=timelist[0]; endday=timelist[-1] + # optional parameters 'startdate', 'enddate' inputs = string 'MM/DD' or 'MM_DD' + trackerdict = demo.gendaylit1axis(startdate=startday, enddate=endday) + # remove times when GHI < 0 by comparing with trackerdict + timelist = _returnTimeVals(timeControlParamsDict, trackerdict) + print("\n***Timerange from %s to %s. ***\n" % (sorted(timelist)[0], + sorted(timelist)[-1])) + def _addRadfile(trackerdict): + # need to add trackerdict[time]['radfile'] = radfile and + # trackerdict[time]['scene'] = scene since we don't do makeScene1axis + for i in trackerdict: + trackerdict[i]['scene'] = scene + trackerdict[i]['radfile'] = scene.radfiles + return trackerdict + + trackerdict = _addRadfile(trackerdict) # instead of makeScene1axis + + footime=0 + for time in sorted(timelist): + footime = footime+1 + trackerdict = demo.makeOct1axis(trackerdict, singleindex=time, + hpc=simulationParamsDict['hpc']) + trackerdict = demo.analysis1axis(trackerdict, singleindex=time, + modWanted=analysisParamsDict['modWanted'], + rowWanted=analysisParamsDict['rowWanted'], + sensorsy=analysisParamsDict['sensorsy']) + analysis = trackerdict[time]['AnalysisObj'] # save and return the last run + print('Bifacial ratio average for %d out of %d datapoints: %0.3f' % ( footime, + timelist.__len__(), + np.sum(demo.Wm2Back) / np.sum(demo.Wm2Front))) else: # Tracking print('\n***Starting 1-axis tracking simulation***\n') + + if simulationParamsDict['timestampRangeSimulation']: + raise Exception('timestampRangeSimulations not currently '+ + 'supported for tracking') + if 'gcr' not in sceneParamsDict: # didn't get gcr passed - need to calculate it sceneParamsDict['gcr'] = moduleDict['sceney'] / \ sceneParamsDict['pitch'] @@ -199,9 +304,8 @@ def runModelChain(simulationParamsDict, sceneParamsDict, timeControlParamsDict=N cumulativesky=simulationParamsDict["cumulativeSky"]) if simulationParamsDict["cumulativeSky"]: # cumulative sky routine - - # This option doesn't work currently.! - if simulationParamsDict['timestampRangeSimulation']: + + if simulationParamsDict['daydateSimulation']: # Start / end passed import datetime startdate = datetime.datetime(2001, timeControlParamsDict['MonthStart'], timeControlParamsDict['DayStart'], @@ -212,7 +316,7 @@ def runModelChain(simulationParamsDict, sceneParamsDict, timeControlParamsDict=N trackerdict = demo.genCumSky1axis( trackerdict, startdt=startdate, enddt=enddate) else: - trackerdict = demo.genCumSky1axis(trackerdict) + trackerdict = demo.genCumSky1axis(trackerdict) # full year trackerdict = demo.makeScene1axis(trackerdict=trackerdict, moduletype=simulationParamsDict['moduletype'], @@ -227,29 +331,33 @@ def runModelChain(simulationParamsDict, sceneParamsDict, timeControlParamsDict=N rowWanted=analysisParamsDict['rowWanted'], sensorsy=analysisParamsDict['sensorsy']) print('Annual RADIANCE bifacial ratio for 1-axis tracking: %0.3f' % - (sum(demo.Wm2Back)/sum(demo.Wm2Front))) + (np.sum(demo.Wm2Back)/np.sum(demo.Wm2Front))) - else: + else: # Hourly tracking # Timestamp range selection - if simulationParamsDict['timestampRangeSimulation']: - # _returnTimeVals returns proper string formatted start and end days. - startday, endday,_= _returnTimeVals(timeControlParamsDict) - - - # optional parameters 'startdate', 'enddate' inputs = string 'MM/DD' or 'MM_DD' - trackerdict = demo.gendaylit1axis(startdate=startday, enddate=endday) - _,_,timelist = _returnTimeVals(timeControlParamsDict, trackerdict) - else: - # optional parameters 'startdate', 'enddate' inputs = string 'MM/DD' or 'MM_DD' - trackerdict = demo.gendaylit1axis() - - # Tracker dict should go here becuase sky routine reduces the size of trackerdict. + #workflow currently identical for timestampRangeSimulation and daydateSimulation + # TODO: update _returnTimeVals to allow timestartindex and timeEndIndex as well. + + if not(simulationParamsDict['daydateSimulation']): # full year. + # use default behavior of _returnTimeVals to run + # full year simulation if you pass timeDict as none + timeControlParamsDict = None + print('\n***Full - year hourly simulation ***\n') + timelist = _returnTimeVals(timeControlParamsDict) + startday = timelist[0]; endday = timelist[-1] + trackerdict = demo.gendaylit1axis(startdate=startday, enddate=endday) + # reduce trackerdict to only hours in timeControlParamsDict + timelist = _returnTimeVals(timeControlParamsDict, trackerdict) + trackerdict = {t: trackerdict[t] for t in timelist} + + # Tracker dict should go here because sky routine reduces the size of trackerdict. trackerdict = demo.makeScene1axis(trackerdict=trackerdict, moduletype=simulationParamsDict['moduletype'], sceneDict=sceneParamsDict, cumulativesky=simulationParamsDict['cumulativeSky'], hpc=simulationParamsDict['hpc']) + ''' if simulationParamsDict['timestampRangeSimulation']: for time in timelist: @@ -259,11 +367,17 @@ def runModelChain(simulationParamsDict, sceneParamsDict, timeControlParamsDict=N modWanted=analysisParamsDict['modWanted'], rowWanted=analysisParamsDict['rowWanted'], sensorsy=analysisParamsDict['sensorsy']) - - else: - trackerdict = demo.makeOct1axis( - trackerdict, hpc=simulationParamsDict['hpc']) - trackerdict = demo.analysis1axis(trackerdict, modWanted=analysisParamsDict['modWanted'], - rowWanted=analysisParamsDict['rowWanted'], - sensorsy=analysisParamsDict['sensorsy']) + + #else: #daydateSimulation. Not sure this is much different from the above... + ''' + trackerdict = demo.makeOct1axis( + trackerdict, hpc=simulationParamsDict['hpc']) + trackerdict = demo.analysis1axis(trackerdict, modWanted=analysisParamsDict['modWanted'], + rowWanted=analysisParamsDict['rowWanted'], + sensorsy=analysisParamsDict['sensorsy']) + # end else statement + print('Bifacial Tracking simulation complete. Preliminary '+ + 'Bifi ratio average: %0.3f' % ( + np.sum(demo.Wm2Back) / np.sum(demo.Wm2Front)) + + ' but final results need cleaning') return demo, analysis diff --git a/bifacial_radiance/readepw.py b/bifacial_radiance/readepw.py deleted file mode 100644 index 14bb3aa8..00000000 --- a/bifacial_radiance/readepw.py +++ /dev/null @@ -1,125 +0,0 @@ -# -*- coding: utf-8 -*- - - -def readepw(filename=None): - ''' - Reads an EPW file into a pandas dataframe. - - Function tested with EnergyPlus weather data files: - https://energyplus.net/weather - - Parameters - ---------- - filename : None or string - If None, attempts to use a Tkinter file browser. A string can be - a relative file path, absolute file path, or url. - - Returns - ------- - Tuple of the form (data, metadata). - - data : DataFrame - A pandas dataframe with the columns described in the table - below. - - metadata : dict - The site metadata available in the file. - - Notes - ----- - - The returned structures have the following fields. - - ======================================================================= - Data field - ======================================================================= - Datetime data - Dry bulb temperature in Celsius at indicated time - Dew point temperature in Celsius at indicated time - Relative humidity in percent at indicated time - Atmospheric station pressure in Pa at indicated time - Extraterrestrial horizontal radiation in Wh/m2 - Extraterrestrial direct normal radiation in Wh/m2 - Horizontal infrared radiation intensity in Wh/m2 - Global horizontal radiation in Wh/m2 - Direct normal radiation in Wh/m2 - Diffuse horizontal radiation in Wh/m2 - Averaged global horizontal illuminance in lux during minutes preceding the indicated time - Direct normal illuminance in lux during minutes preceding the indicated time - Diffuse horizontal illuminance in lux during minutes preceding the indicated time - Zenith luminance in Cd/m2 during minutes preceding the indicated time - Wind direction at indicated time. N=0, E=90, S=180, W=270 - Wind speed in m/s at indicated time - Total sky cover at indicated time - Opaque sky cover at indicated time - Visibility in km at indicated time - Ceiling height in m - Present weather observation - Present weather codes - Precipitable water in mm - Aerosol optical depth - Snow depth in cm - Days since last snowfall - Albedo - Liquid precipitation depth in mm at indicated time - Liquid precipitation quantity - ======================================================================= - - =============== ====== =================== - key format description - =============== ====== =================== - altitude Float site elevation - latitude Float site latitudeitude - longitude Float site longitudeitude - Name String site name - State String state - TZ Float UTC offset - USAF Int USAF identifier - =============== ====== =================== - - S. Quoilin, October 2017 - Downloaded from PVLib issue tracker on 3/16/18 - https://github.com/pvlib/pvlib-python/issues/261 - ''' - import pandas as pd - def _interactive_load(): - import Tkinter - from tkFileDialog import askopenfilename - Tkinter.Tk().withdraw() #Start interactive file input - return askopenfilename() - - if filename is None: - try: - filename = _interactive_load() - except: - raise Exception('Interactive load failed. Tkinter not supported on this system. Try installing X-Quartz and reloading') - - head = ['dummy0', 'Name', 'dummy1', 'State', 'dummy2', 'USAF', 'latitude', 'longitude', 'TZ', 'altitude'] - - csvdata = open(filename, 'r') - - # read in file metadata - temp = dict(zip(head, csvdata.readline().rstrip('\n').split(","))) - - # convert metadata strings to numeric types - meta = {} - meta['Name'] = temp['Name'] - meta['State'] = temp['State'] - meta['altitude'] = float(temp['altitude']) - meta['latitude'] = float(temp['latitude']) - meta['longitude'] = float(temp['longitude']) - meta['TZ'] = float(temp['TZ']) - try: - meta['USAF'] = int(temp['USAF']) - except: - meta['USAF'] = None - - headers = ["year","month","day","hour","min","Dry bulb temperature in C","Dew point temperature in C","Relative humidity in percent","Atmospheric pressure in Pa","Extraterrestrial horizontal radiation in Wh/m2","Extraterrestrial direct normal radiation in Wh/m2","Horizontal infrared radiation intensity in Wh/m2","Global horizontal radiation in Wh/m2","Direct normal radiation in Wh/m2","Diffuse horizontal radiation in Wh/m2","Averaged global horizontal illuminance in lux during minutes preceding the indicated time","Direct normal illuminance in lux during minutes preceding the indicated time","Diffuse horizontal illuminance in lux during minutes preceding the indicated time","Zenith luminance in Cd/m2 during minutes preceding the indicated time","Wind direction. N=0, E=90, S=180, W=270","Wind speed in m/s","Total sky cover","Opaque sky cover","Visibility in km","Ceiling height in m","Present weather observation","Present weather codes","Precipitable water in mm","Aerosol optical depth","Snow depth in cm","Days since last snowfall","Albedo","Liquid precipitation depth in mm","Liquid precipitation quantity"] - Data = pd.read_csv(filename, skiprows=8,header=None) - del Data[5] - Data.columns = headers - Data.index = pd.to_datetime(Data[["year","month","day","hour"]]) - - Data = Data.tz_localize(int(meta['TZ']*3600)) - - return Data, meta \ No newline at end of file diff --git a/docs/1Axis_Radiance_VF_comparison.py b/docs/1Axis_Radiance_VF_comparison.py deleted file mode 100644 index 67d8a7cb..00000000 --- a/docs/1Axis_Radiance_VF_comparison.py +++ /dev/null @@ -1,166 +0,0 @@ -''' - ## 1-Axis tracker example including bifacialVF - - Example demonstrating Radiance gencumulativesky for 1-axis tracking and bifacialvf. - - #### Prerequisites (Step 0): - bifacial_radiance requires the previous installation of RADIANCE from https://github.com/NREL/Radiance/releases. - - During installation, select "add radiance to the system PATH" so Python can interact with the radiance program - - - #### STEP 1: Install and import bifacial_radiance - - - clone the github.com/cdeline/bifacial_radiance repo to your local directory - - navigate to the /bifacial_radiance/ directory which contains setup.py - - run `pip install -e . ` ( the period . is required, the -e flag is optional and installs in development mode where changes to the bifacial_radiance.py files are immediately incorporated into the module if you re-start the python kernel) - - #### STEP 2: Move gencumulativesky.exe - Copy gencumulativesky.exe from the repo's `/bifacial_radiance/data/` directory and copy into your Radiance install directory. - This is typically found in `/program files/radiance/bin/`. - - #### STEP 3: Create a local Radiance directory for storing the scene files created - Keep scene geometry files separate from the bifacial_radiance directory. Create a local directory somewhere that will be referenced in the next step. - - #### STEP 4: Install bifacialvf - - clone the github.com/cdeline/bifacialvf repo to your local directory - - navigate to the /bifacialvf directory which contains setup.py - - run `pip install -e . ` ( the period . is required, the -e flag is optional and installs in development mode where changes to the bifacialvf.py files are immediately incorporated into the module if you re-start the python kernel) - - - #### STEP 5: Reboot the computer - This makes sure the PATH is updated - -''' - - -## User custom variables (update this) - -testfolder = r'C:\Users\cdeline\Documents\Python Scripts\TestFolder' #point to an empty directory or existing Radiance directory - -# tracker geometry options: -module_height = 1.7 # module portrait dimension in meters -gcr = 0.25 # ground cover ratio, = module_height / pitch -albedo = 0.2 # ground albedo -hub_height = 2 # tracker height at 0 tilt in meters (hub height) -limit_angle = 45 # tracker rotation limit angle -# Import modules - -import numpy as np -import datetime -try: - from bifacial_radiance import RadianceObj -except ImportError: - raise RuntimeError('bifacial_radiance is required. download distribution') - - -print('starting simulation: {}'.format(datetime.datetime.now())) -# Example 1-axis tracking system using Radiance. This takes 5-10 minutes to complete, depending on computer. - -demo = RadianceObj(path = testfolder) # Create a RadianceObj 'object' named 'demo' - -demo.setGround(albedo) # input albedo number or material name like 'concrete'. To see options, run this without any input. - -epwfile = demo.getEPW(37.5,-77.6) #Pull TMY weather data for any global lat/lon. In this case, Richmond, VA - -metdata = demo.readEPW(epwfile) # read in the weather data - -# create separate metdata files for each 1-axis tracker angle (5 degree resolution). -trackerdict = demo.set1axis(metdata, limit_angle = limit_angle, backtrack = True, gcr = gcr) - -# create cumulativesky functions for each tracker angle: demo.genCumSky1axis -trackerdict = demo.genCumSky1axis(trackerdict) -# Create a new moduletype: Prism Solar Bi60. width = .984m height = 1.695m. Bifaciality = 0.90 -demo.makeModule(name='Prism Solar Bi60',x=0.984,y=module_height,bifi = 0.90) -# print available module types -demo.printModules() - -# create a 1-axis scene using panels in portrait, 2m hub height, 0.33 GCR. NOTE: clearance needs to be calculated at each step. hub height is constant -sceneDict = {'pitch': module_height / gcr,'height':hub_height} #'orientation':'portrait' - deprecated in v0.2.4 -module_type = 'Prism Solar Bi60' -trackerdict = demo.makeScene1axis(trackerdict,module_type,sceneDict, nMods = 20, nRows = 7) #makeScene creates a .rad file with 20 modules per row, 7 rows. - -trackerdict = demo.makeOct1axis(trackerdict) -# Now we need to run analysis and combine the results into an annual total. This can be done by calling scene.frontscan and scene.backscan -trackerdict = demo.analysis1axis(trackerdict) - -# the frontscan and backscan include a linescan along a chord of the module, both on the front and back. -# Return the minimum of the irradiance ratio, and the average of the irradiance ratio along a chord of the module. -print('Annual RADIANCE bifacial ratio for 1-axis tracking: %0.3f - %0.3f' %(min(demo.backRatio), np.mean(demo.backRatio)) ) - - -''' -Now run the analysis using bifacialVF ! - - - - -''' - -print('starting VF simulation: {}'.format(datetime.datetime.now())) - -import bifacialvf # github.com/cdeline/bifacialvf -import os -# change directory to \bifacialvf\ root -os.chdir(os.path.dirname(bifacialvf.__file__)) - - -#2. Set the Values of your test -# Remember all values are normalized to your panel length (slope, which will equal 1). -# If your slope is different than 1 m, desired C and D (or Cv and rtr in tracking case) will need to be -# divided by the slope length. -# i.e.: panel 1mx1.59m, in portrait mode means slope = 1.59. For a height C of 1m, C = 1/1.59. -# For a rtr of 1.5m, D=0.51519/1.59 if beta tilt angle = 10 - -# comparison with bifacial_radiance 1-axis tracking. 0.2 albedo, richmond VA location, 1.7m 1-up portrait, 2m height, 5.1m rtr -# normalized to 1 m panel length: rtr = 3; C (hub height) = 1.176 - -# Set mandatory variables * SCALED BY MODULE_HEIGHT * - -C = hub_height / module_height # GroundClearance(panel slope lengths) -rtr = 1.0 / gcr # normalized to panel length -#D = 0.51519 # DistanceBetweenRows(panel slope lengths) this is NOT row to row spacing -TMYtoread = "data/724010TYA.csv" # VA Richmond -writefiletitle = "data/Output/1Axis.csv" -sazm = 180 # azimuth of system. For trackers, this is the tracking axis orientation -beta = 0 # tilt of the system. For trackers, this can be anything - -# Set optional variables. These are the default values -rowType = "interior" # RowType(first interior last single) -transFactor = 0 # TransmissionFactor(open area fraction) -cellRows = 6 # CellRows(# hor rows in panel) This is the number of irradiance values returned along module chord -PVfrontSurface = "glass" # PVfrontSurface(glass or AR glass) -PVbackSurface = "glass" # PVbackSurface(glass or AR glass) - -# 1-axis tracking instructions (optional) -max_angle = 45 # tracker rotation limit angle -backtrack=True # backtracking optimization as defined in pvlib -#Cv = 0.05 # GroundClearance when panel is in vertical position for tracking simulations (panel slope lengths) - - - - -#3. Call the function. - -bifacialvf.simulate(TMYtoread, writefiletitle, beta, sazm, - C=C, rowType=rowType, transFactor=transFactor, cellRows=cellRows, - PVfrontSurface=PVfrontSurface, PVbackSurface=PVbackSurface, albedo=albedo, - tracking=True, backtrack=backtrack, rtr=rtr, max_angle = max_angle) - - - - -#4. Load the results from the resultfile -(data, metadata) = bifacialvf.loadVFresults(writefiletitle) -#print data.keys() -# calculate average front and back global tilted irradiance across the module chord -data['GTIFrontavg'] = data[['No_1_RowFrontGTI', 'No_2_RowFrontGTI','No_3_RowFrontGTI','No_4_RowFrontGTI','No_5_RowFrontGTI','No_6_RowFrontGTI']].mean(axis=1) -data['GTIBackavg'] = data[['No_1_RowBackGTI', 'No_2_RowBackGTI','No_3_RowBackGTI','No_4_RowBackGTI','No_5_RowBackGTI','No_6_RowBackGTI']].mean(axis=1) - -# Print the annual bifacial ratio -frontIrrSum = data['GTIFrontavg'].sum() -backIrrSum = data['GTIBackavg'].sum() -print('Done! {}'.format(datetime.datetime.now())) -print('The VF bifacial ratio for ground clearance {} and row to row {} is: {:.1f}%'.format(C,rtr,backIrrSum/frontIrrSum*100)) - - diff --git a/docs/images_wiki/AdvancedJournals/Lrear_solving.PNG b/docs/images_wiki/AdvancedJournals/Lrear_solving.PNG new file mode 100644 index 00000000..9fb61978 Binary files /dev/null and b/docs/images_wiki/AdvancedJournals/Lrear_solving.PNG differ diff --git a/docs/images_wiki/AdvancedJournals/Mismatch_Definition_Example.PNG b/docs/images_wiki/AdvancedJournals/Mismatch_Definition_Example.PNG new file mode 100644 index 00000000..cc9a4b2b Binary files /dev/null and b/docs/images_wiki/AdvancedJournals/Mismatch_Definition_Example.PNG differ diff --git a/docs/images_wiki/AdvancedJournals/MultipleSceneObject_AnalysingSceneObj2_Row1_Module4.PNG b/docs/images_wiki/AdvancedJournals/MultipleSceneObject_AnalysingSceneObj2_Row1_Module4.PNG new file mode 100644 index 00000000..a18d60e8 Binary files /dev/null and b/docs/images_wiki/AdvancedJournals/MultipleSceneObject_AnalysingSceneObj2_Row1_Module4.PNG differ diff --git a/docs/images_wiki/JPV_Ayala_Fig13.PNG b/docs/images_wiki/JPV_Ayala_Fig13.PNG new file mode 100644 index 00000000..992f9497 Binary files /dev/null and b/docs/images_wiki/JPV_Ayala_Fig13.PNG differ diff --git a/docs/images_wiki/Journal1Pics/BGG_Formula.PNG b/docs/images_wiki/Journal1Pics/BGG_Formula.PNG new file mode 100644 index 00000000..dd7de4be Binary files /dev/null and b/docs/images_wiki/Journal1Pics/BGG_Formula.PNG differ diff --git a/docs/images_wiki/Journal1Pics/cumulativesky.png b/docs/images_wiki/Journal1Pics/cumulativesky.png new file mode 100644 index 00000000..ca87a2b5 Binary files /dev/null and b/docs/images_wiki/Journal1Pics/cumulativesky.png differ diff --git a/docs/images_wiki/Journal1Pics/folderStructure.PNG b/docs/images_wiki/Journal1Pics/folderStructure.PNG new file mode 100644 index 00000000..ff5a57aa Binary files /dev/null and b/docs/images_wiki/Journal1Pics/folderStructure.PNG differ diff --git a/docs/images_wiki/frontscan_backscan.png b/docs/images_wiki/Journal1Pics/frontscan_backscan.png similarity index 100% rename from docs/images_wiki/frontscan_backscan.png rename to docs/images_wiki/Journal1Pics/frontscan_backscan.png diff --git a/docs/images_wiki/Journal1Pics/openhdr_FalseColorExample.PNG b/docs/images_wiki/Journal1Pics/openhdr_FalseColorExample.PNG new file mode 100644 index 00000000..62eaa6f6 Binary files /dev/null and b/docs/images_wiki/Journal1Pics/openhdr_FalseColorExample.PNG differ diff --git a/docs/images_wiki/Journal2Pics/gencumsky1axis_example_file_structure_and_contents.PNG b/docs/images_wiki/Journal2Pics/gencumsky1axis_example_file_structure_and_contents.PNG new file mode 100644 index 00000000..94153b32 Binary files /dev/null and b/docs/images_wiki/Journal2Pics/gencumsky1axis_example_file_structure_and_contents.PNG differ diff --git a/docs/images_wiki/Journal2Pics/spaced_sensors.PNG b/docs/images_wiki/Journal2Pics/spaced_sensors.PNG new file mode 100644 index 00000000..654b0d6c Binary files /dev/null and b/docs/images_wiki/Journal2Pics/spaced_sensors.PNG differ diff --git a/docs/images_wiki/Journal2Pics/tracking_cumulativesky.PNG b/docs/images_wiki/Journal2Pics/tracking_cumulativesky.PNG new file mode 100644 index 00000000..c3554849 Binary files /dev/null and b/docs/images_wiki/Journal2Pics/tracking_cumulativesky.PNG differ diff --git a/docs/images_wiki/Journal3Pics/Equation_CW.PNG b/docs/images_wiki/Journal3Pics/Equation_CW.PNG new file mode 100644 index 00000000..207079fb Binary files /dev/null and b/docs/images_wiki/Journal3Pics/Equation_CW.PNG differ diff --git a/docs/images_wiki/Journal3Pics/Equation_GCR.PNG b/docs/images_wiki/Journal3Pics/Equation_GCR.PNG new file mode 100644 index 00000000..5adc8521 Binary files /dev/null and b/docs/images_wiki/Journal3Pics/Equation_GCR.PNG differ diff --git a/docs/images_wiki/bifacial_radiance_GUI.png b/docs/images_wiki/bifacial_radiance_GUI.png new file mode 100644 index 00000000..19edd918 Binary files /dev/null and b/docs/images_wiki/bifacial_radiance_GUI.png differ diff --git a/docs/sphinx/Makefile b/docs/sphinx/Makefile new file mode 100644 index 00000000..d0c3cbf1 --- /dev/null +++ b/docs/sphinx/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = source +BUILDDIR = build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/sphinx/make.bat b/docs/sphinx/make.bat new file mode 100644 index 00000000..9534b018 --- /dev/null +++ b/docs/sphinx/make.bat @@ -0,0 +1,35 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=source +set BUILDDIR=build + +if "%1" == "" goto help + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.http://sphinx-doc.org/ + exit /b 1 +) + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% + +:end +popd diff --git a/bifacial_radiance/TEMP/temp.txt b/docs/sphinx/source/_static/.gitignore similarity index 100% rename from bifacial_radiance/TEMP/temp.txt rename to docs/sphinx/source/_static/.gitignore diff --git a/docs/sphinx/source/_static/no_scrollbars.css b/docs/sphinx/source/_static/no_scrollbars.css new file mode 100644 index 00000000..5177a66a --- /dev/null +++ b/docs/sphinx/source/_static/no_scrollbars.css @@ -0,0 +1,11 @@ +/* override table width restrictions */ +/* as described in https://github.com/snide/sphinx_rtd_theme/issues/117 */ +.wy-table-responsive table td, .wy-table-responsive table th { + /* !important prevents the common CSS stylesheets from + overriding this as on RTD they are loaded after this stylesheet */ + white-space: normal !important; +} + +.wy-table-responsive { + overflow: visible !important; +} diff --git a/docs/sphinx/source/conf.py b/docs/sphinx/source/conf.py new file mode 100644 index 00000000..5b495dda --- /dev/null +++ b/docs/sphinx/source/conf.py @@ -0,0 +1,248 @@ +# -*- coding: utf-8 -*- +# +# bifacial_radiance documentation build configuration file, created by +# sphinx-quickstart on Tuesday Sep 24 18:48:33 2019. +# +# Configuration file for the Sphinx documentation builder. +# +# This file only contains a selection of the most common options. For a full +# list see the documentation: +# http://www.sphinx-doc.org/en/master/config + +# -- Path setup -------------------------------------------------------------- + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# +# import os +# import sys +# sys.path.insert(0, os.path.abspath('.')) + + +# -- Project information ----------------------------------------------------- + +import sys +import os + +""" +# Mock modules so RTD works +try: + from mock import Mock as MagicMock +except ImportError: + from unittest.mock import MagicMock + +class Mock(MagicMock): + @classmethod + def __getattr__(cls, name): + return Mock() + +MOCK_MODULES = [] +sys.modules.update((mod_name, Mock()) for mod_name in MOCK_MODULES) +""" +import pandas as pd +pd.show_versions() + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +sys.path.insert(0, os.path.abspath('../sphinxext')) +sys.path.insert(0, os.path.abspath('../../../')) + + + + +# -- General configuration --------------------------------------------------- + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ + 'sphinx.ext.autodoc', + 'sphinx.ext.mathjax', + 'sphinx.ext.viewcode', + 'sphinx.ext.intersphinx', + 'sphinx.ext.autodoc', + 'sphinx.ext.extlinks', + 'sphinx.ext.napoleon', + 'sphinx.ext.autosummary', + 'IPython.sphinxext.ipython_directive', + 'IPython.sphinxext.ipython_console_highlighting', + 'sphinx.ext.doctest', + #'autoapi.extension', + 'sphinx.ext.todo' +] + + + +# Document Python Code +#autodoc_mock_imports = ['bs4', 'requests'] +#autoapi_type = 'python' +#autoapi_dirs = '../../../bifacial_radiance' + +napoleon_use_rtype = False # group rtype on same line together with return + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix of source filenames. +source_suffix = '.rst' + +# The encoding of source files. +#source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'bifacial_radiance' +copyright = u'2019, NREL' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. + +import bifacial_radiance + +# The short X.Y version. +version = '%s' % (bifacial_radiance.__version__) +# The full version, including alpha/beta/rc tags. +release = version + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = ['whatsnew/*', '**.ipynb_checkpoints'] + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +autosummary_generate = True + + +# -- Options for HTML output ------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# on_rtd is whether we are on readthedocs.org + +on_rtd = os.environ.get('READTHEDOCS', None) == 'True' + +if not on_rtd: # only import and set the theme if we're building docs locally + import sphinx_rtd_theme + html_theme = 'sphinx_rtd_theme' + html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] +else: + html_theme = 'default' + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +html_show_copyright = True + +# Output file base name for HTML help builder. +htmlhelp_basename = 'bifacial_radiancedoc' + +# A workaround for the responsive tables always having annoying scrollbars. +def setup(app): + app.add_stylesheet("no_scrollbars.css") + +""" +# -- Options for LaTeX output --------------------------------------------- + +latex_elements = { +# The paper size ('letterpaper' or 'a4paper'). +#'papersize': 'letterpaper', + +# The font size ('10pt', '11pt' or '12pt'). +#'pointsize': '10pt', + +# Additional stuff for the LaTeX preamble. +#'preamble': '', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, +# author, documentclass [howto, manual, or own class]). +latex_documents = [ + ('index', 'bifacial_radiance.tex', u'bifacial_radiance\\_Python Documentation', + u'NREL, github contributors', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +#latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# If true, show page references after internal links. +#latex_show_pagerefs = False + +# If true, show URL addresses after external links. +#latex_show_urls = False + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_domain_indices = True +""" +# extlinks alias +extlinks = {'issue': ('https://github.com/NREL/bifacial_radiance/issues/%s', + 'GH'), + 'wiki': ('https://github.com/NREL/bifacial_radiance/wiki/%s', + 'wiki '), + 'doi': ('http://dx.doi.org/%s', 'DOI: '), + 'ghuser': ('https://github.com/%s', '@')} +""" +# -- Options for manual page output --------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + ('index', 'bifacial_radiance', u'bifacial_radiance Documentation', + [u'NREL, github contributors'], 1) +] + +# If true, show URL addresses after external links. +#man_show_urls = False + + +# -- Options for Texinfo output ------------------------------------------- + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + ('index', 'bifacial_radiance', u'bifacial_radiance Documentation', + u'NREL, github contributors', 'bifacial_radiance', 'One line description of project.', + 'Miscellaneous'), +] + +# Documents to append as an appendix to all manuals. +#texinfo_appendices = [] + +# If false, no module index is generated. +#texinfo_domain_indices = True + +# How to display URL addresses: 'footnote', 'no', or 'inline'. +#texinfo_show_urls = 'footnote' + +# If true, do not generate a @detailmenu in the "Top" node's menu. +#texinfo_no_detailmenu = False + +# Example configuration for intersphinx: refer to the Python standard library. +intersphinx_mapping = { + 'python': ('https://docs.python.org/3.7/', None), + 'pandas': ('https://pandas.pydata.org/pandas-docs/stable/', None), + 'numpy': ('https://docs.scipy.org/doc/numpy/', None), +} + +nbsphinx_allow_errors = True + +ipython_warning_is_error = False +""" \ No newline at end of file diff --git a/docs/sphinx/source/contributing.rst b/docs/sphinx/source/contributing.rst new file mode 100644 index 00000000..6c27575b --- /dev/null +++ b/docs/sphinx/source/contributing.rst @@ -0,0 +1,281 @@ +.. _contributing: + +Contributing +============ + +.. note:: + This contributing document is heavily based on pvlib-python + contribution guidelines. This is still a work in progress + +Encouraging more people to help develop bifacial_radiance is essential to our +success. Therefore, we want to make it easy and rewarding for you to +contribute. + +There is a lot of material in this section, aimed at a variety of +contributors from novice to expert. Don't worry if you don't (yet) +understand parts of it. + + +Easy ways to contribute +~~~~~~~~~~~~~~~~~~~~~~~ + +Here are a few ideas for how you can contribute, even if you are new to +bifacial_radiance, git, or Python: + +* Ask and answer `bifacial_radiance questions on StackOverflow `_ + and participate in discussions in the `bifacial_radiance google group `_. +* Make `GitHub issues `_ + and contribute to the conversations about how to resolve them. +* Read issues and pull requests that other people created and + contribute to the conversation about how to resolve them. +* Improve the documentation and the unit tests. +* Improve the IPython/Jupyter Notebook tutorials or write new ones that + demonstrate how to use bifacial_radiance in your area of expertise. +* Tell your friends and colleagues about bifacial_radiance +* Add your project to our + `Projects and publications that use bifacial_radiance wiki + `_. + + +How to contribute new code +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The basics +---------- + +Contributors to bifacial_radiance use GitHub's pull requests to add/modify +its source code. The GitHub pull request process can be intimidating for +new users, but you'll find that it becomes straightforward once you use +it a few times. Please let us know if you get stuck at any point in the +process. Here's an outline of the process: + +#. Create a GitHub issue and get initial feedback from users and + maintainers. If the issue is a bug report, please include the + code needed to reproduce the problem. +#. Obtain the latest version of bifacial_radiance: Fork the bifacail_radiance + project to your GitHub account, ``git clone`` your fork to your computer. +#. Make some or all of your changes/additions and ``git commit`` them to + your local repository. +#. Share your changes with us via a pull request: ``git push`` your + local changes to your GitHub fork, then go to GitHub make a pull + request. + +The Pandas project maintains an excellent `contributing page +`_ that goes +into detail on each of these steps. Also see GitHub's `Set Up Git +`_ and `Using Pull +Requests `_. + +We strongly recommend using virtual environments for development. +Virtual environments make it trivial to switch between different +versions of software. This `astropy guide +`_ is a good reference for virtual environments. If +this is your first pull request, don't worry about using a virtual +environment. + +You must include documentation and unit tests for any new or improved +code. We can provide help and advice on this after you start the pull +request. See the Testing section below. + + +.. _pull-request-scope: + +Pull request scope +------------------ + +This section can be summed up as "less is more". + +A pull request can quickly become unmanageable if too many lines are +added or changed. "Too many" is hard to define, but as a rule of thumb, +we encourage contributions that contain less than 50 lines of primary code. +50 lines of primary code will typically need at least 250 lines +of documentation and testing. This is about the limit of what the +maintainers can review on a regular basis. + +A pull request can also quickly become unmanageable if it proposes +changes to the API in order to implement another feature. Consider +clearly and concisely documenting all proposed API changes before +implementing any code. + +Questions about related issues frequently come up in the process of +addressing implementing code for a pull request. Please try to avoid +expanding the scope of your pull request (this also applies to +reviewers!). We'd rather see small, well-documented additions to the +project's technical debt than see a pull request languish because its +scope expanded beyond what the reviewer community is capable of +processing. + +Of course, sometimes it is necessary to make a large pull request. We +only ask that you take a few minutes to consider how to break it into +smaller chunks before proceeding. + +bifacial_radiance contains 3 layers of code: +functions, analysis, and ModelChain. We recommend that +contributors focus their work on only one or two of those layers in a +single pull request. + + +When should I submit a pull request? +------------------------------------ + +The short answer: anytime. + +The long answer: it depends. If in doubt, go ahead and submit. You do +not need to make all of your changes before creating a pull request. +Your pull requests will automatically be updated when you commit new +changes and push them to GitHub. + +There are pros and cons to submitting incomplete pull-requests. On the +plus side, it gives everybody an easy way to comment on the code and can +make the process more efficient. On the minus side, it's easy for an +incomplete pull request to grow into a multi-month saga that leaves +everyone unhappy. If you submit an incomplete pull request, please be +very clear about what you would like feedback on and what we should +ignore. Alternatives to incomplete pull requests include creating a +`gist `_ or experimental branch and linking to +it in the corresponding issue. + +The best way to ensure that a pull request will be reviewed and merged in +a timely manner is to: + +#. Start by creating an issue. The issue should be well-defined and + actionable. +#. Ask the maintainers to tag the issue with the appropriate milestone. +#. Tag bifacial_radiance community members or ``@bifacial_radiance/maintainer`` when the pull + request is ready for review. (see :ref:`pull-request-reviews`) + + +.. _pull-request-reviews: + +Pull request reviews +-------------------- + +The bifacial_radiance community and maintainers will review your pull request in a +timely fashion. Please "ping" ``@bifacial_radinace/maintainer`` if it seems that +your pull request has been forgotten at any point in the pull request +process. + +Keep in mind that the PV modeling community is diverse and each bifacial_radiance +community member brings a different perspective when reviewing code. +Some reviewers bring years of expertise in the sub-field that your code +contributes to and will focus on the details of the algorithm. Other +reviewers will be more focused on integrating your code with the rest of +bifacial_radiance, ensuring that it is feasible to maintain, that it meets the +:ref:`code style ` guidelines, and that it is +:ref:`comprehensively tested `. Limiting the scope of the pull +request makes it much more likely that all of these reviews can be +conducted and any issues can be resolved in a timely fashion. + +Sometimes it's hard for reviewers to be immediately available, so the +right amount of patience is to be expected. That said, interested +reviewers should do their best to not wait until the last minute to put +in their two cents. + + +.. _code-style: + +Code style +~~~~~~~~~~ + +bifacial_radiance python generally follows the `PEP 8 -- Style Guide for Python Code +`_. Maximum line length for code +is 79 characters. + +Code must be compatible with Python 3.5 and above. + +bifacial_radiance uses a mix of full and abbreviated variable names. See +:ref:`variables_style_rules`. We could be better about consistency. +Prefer full names for new contributions. This is especially important +for the API. Abbreviations can be used within a function to improve the +readability of formulae. + +Set your editor to strip extra whitespace from line endings. This +prevents the git commit history from becoming cluttered with whitespace +changes. + +Please see :ref:`Documentation` for information specific to documentation +style. + +Remove any ``logging`` calls and ``print`` statements that you added +during development. ``warning`` is ok. + +We typically use GitHub's +"`squash and merge` _" +feature to merge your pull request into bifacial_radiance. GitHub will condense the +commit history of your branch into a single commit when merging into +bifacial_radiance/master (the commit history on your branch remains +unchanged). Therefore, you are free to make commits that are as big or +small as you'd like while developing your pull request. + + +.. _documentation: + +Documentation +~~~~~~~~~~~~~ + +Documentation must be written in +`numpydoc format `_ format which is rendered +using the `Sphinx Napoleon extension +`_. + +The numpydoc format includes a specification for the allowable input +types. Python's `duck typing `_ +allows for multiple input types to work for many parameters. bifacial_radiance uses +the following generic descriptors as short-hand to indicate which +specific types may be used: + +* dict-like : dict, OrderedDict, pd.Series +* numeric : scalar, np.array, pd.Series. Typically int or float dtype. +* array-like : np.array, pd.Series. Typically int or float dtype. + +Parameters that specify a specific type require that specific input type. + +A relatively easy way to test your documentation is to build it on +`readthedocs.org ` by following their +`Import Your Docs `_ +instructions and enabling your branch on the readthedocs +`versions admin page `_. + +.. _testing: + +Testing +~~~~~~~ + +bifacial_radiance's unit tests can easily be run by executing ``pytest`` on the +bifacial_radiance directory: + +``pytest bifacial_radiance`` + +or, for a single module: + +``pytest bifacial_radiance/test/modelchain.py`` + +or, for a single test: + +``pytest bifacial_radiance/test/modelchain.py::runModelChain`` + +We suggest using pytest's ``--pdb`` flag to debug test failures rather +than using ``print`` or ``logging`` calls. For example: + +``pytest bifacial_radiance/test/modelchain.py --pdb`` + +will drop you into the +`pdb debugger `_ at the +location of a test failure. As described in :ref:`code-style`, bifacial_radiance +code does not use ``print`` or ``logging`` calls, and this also applies +to the test suite (with rare exceptions). + +New unit test code should be placed in the corresponding test module in +the bifacial_radiance/test directory. + +Developers **must** include comprehensive tests for any additions or +modifications to bifacial_radiance. + +This documentation +~~~~~~~~~~~~~~~~~~ + +If this documentation is unclear, help us improve it! Consider looking +at the `pandas +documentation `_ for inspiration. diff --git a/docs/sphinx/source/index.rst b/docs/sphinx/source/index.rst new file mode 100644 index 00000000..dc7b60d8 --- /dev/null +++ b/docs/sphinx/source/index.rst @@ -0,0 +1,67 @@ +.. image:: ../../images_wiki/bifacial_radiance.png + :width: 600 + +Welcome to bifacial_radiance's documentation! +============================================= + +bifacial_radiance is a NREL supported tool that provides a set of functions and classes for simulating the performance of bifacial PV systems. More information on bifacial_radiance can be found at the Wiki page. + +The source code for bifacial_radiance is hosted on `github +`_. + +Please see the :ref:`installation` page for installation help. + +For examples on how to use bifacial_radiance, please see :ref:`package_overview` and our `Jupyter Notebook tutorials +`_. The documentation assumes general familiarity with Python, NumPy, and Pandas. Google searches will yield many excellent tutorials for these packages. + +The bifacial_radiance GitHub wiki has a `Projects and publications that use bifacial_radiance `_ page for inspiration and listing of your application. + +There is a :ref:`variable naming convention ` to +ensure consistency throughout the library + +Citing bifacial_radiance +======================== + +Many of the contributors to bifacial_radiance work in institutions where +citation metrics are used in performance or career evaluations. If you +use bifacial_radiance in a published work, please cite: + + Deline, Chris, Marion, William, and Ayala Pelaez, Silvana. Bifacial_Radiance. Computer Software. https://github.com/NREL/bifacial_radiance. California Energy Commission. 17 Dec. 2017. Web. doi:10.11578/dc.20180530.16. + https://www.osti.gov/doecode/biblio/6869 + +Please also cite the DOI corresponding to the specific version of +bifacial_radiance that you used. bifacial_radiance DOIs are listed at +`Zenodo.org` + +Additional bifacial_radiance publications with validation of the software include: + +* Ayala Pelaez S, Deline C, Greenberg P, Stein JS, Kostuk RK. Model and validation of single-axis tracking with bifacial PV. IEEE J Photovoltaics. 2019;9(3):715-721. https://ieeexplore.ieee.org/document/8644027 and https://www.nrel.gov/docs/fy19osti/72039.pdf (pre-print, conference version) + +* Ayala Pelaez, Deline C, MacAlpine M, Marion B, Stein J, Kostuk K. Comparison of Bifacial Solar Irradiance Model Predictions with Field Validation. IEEE J Photovoltaics. 2019; 9(1):82-87. https://ieeexplore.ieee.org/document/8534404 + + + + +Contents +======== + +.. toctree:: + :maxdepth: 3 + + package_overview + introexamples + whatsnew + installation + contributing + modelchain + manualapi + validation + variables_style_rules + + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` diff --git a/docs/sphinx/source/installation.rst b/docs/sphinx/source/installation.rst new file mode 100644 index 00000000..4c267568 --- /dev/null +++ b/docs/sphinx/source/installation.rst @@ -0,0 +1,61 @@ +.. _installation: + +Installation +============ + +Video Instructions +~~~~~~~~~~~~~~~~~~ + +`https://youtu.be/4A9GocfHKyM `_ This video shows how to install the bifacial_radiance software and all associated softwares needed. More info on the Wiki. Instructions are also shown below. + + +PREREQUISITES (Step 0) +~~~~~~~~~~~~~~~~~~~~~~~ +This software requires the previous installation of ``RADIANCE`` from https://github.com/NREL/Radiance/releases. + +Make sure you add radiance to the system PATH so Python can interact with the radiance program + +If you are on a PC you should also copy the `Jaloxa radwinexe-5.0.a.8-win64.zip `_ executables into ``program files/radiance/bin`` + +**Note: bifacial_radiance is not endorsed by or officially connected with the Radiance software package or its development team.** + +STEP 1 +~~~~~~ +Install and Import bifacial_radiance + +* clone the bifacial_radiance repo to your local directory or download and unzip the .zip file +* navigate to the \bifacial_radiance directory using anaconda command line +* run:: + + pip install -e . + +The period ``.`` is required, the ``-e`` flag is optional and installs in development mode where changes to the `bifacial_radiance.py` files are immediately incorporated into the module if you re-start the python kernel) + +For best compatibility, deploy in an `Anaconda 2.7` environment, or run:: + + pip install -r requirements.txt + + +STEP 2 +~~~~~~ +Move gencumulativesky.exe + +* Copy gencumulativesky.exe from the repo's ``/bifacial_radiance/data/`` directory and copy into your Radiance install directory. + This is typically found in ``/program files/radiance/bin/``. + +.. note:: + GenCumulativeSky is detailed in the publication "Robinson, D., Stone, A., Irradiation modeling made simple: the cumulative sky approach and its applications, Proc. PLEA 2004, Eindhoven 2004." + +The source is `available from the authors here `_ + + +STEP 3 +~~~~~~ +Create a local Radiance directory for storing the scene files created + +Keep scene geometry files separate from the bifacial_radiance directory. Create a local directory somewhere to be used for storing scene files. + +STEP 4 +~~~~~~ +Reboot the computer +This makes sure the ``PATH`` is updated diff --git a/docs/sphinx/source/introexamples.rst b/docs/sphinx/source/introexamples.rst new file mode 100644 index 00000000..826be5c4 --- /dev/null +++ b/docs/sphinx/source/introexamples.rst @@ -0,0 +1,181 @@ +.. _introexamples: + +Intro Examples +============== + +Usage +~~~~~ + +This page contains introductory examples of bifacial_radiance usage. We recommend to look at the Jupyter Notebook tutorials for more updated and better documented examples. + +.. code-block:: python + + from bifacial_radiance import RadianceObj # the main container for working with radiance + +Now that the module is loaded, let's use it. + + +.. code-block:: python + + demo = RadianceObj(name = 'Testrun', path = 'myfolder') #create a new demo run. Files will have the Testrun prefix, and be saved to 'myfolder' + + demo.setGround(0.3) # input albedo number or material name like 'concrete'. To see options, run this without any input. + + # Now download an EPW climate file for any global lat/lon value : + epwfile = demo.getEPW(37.5,-77.6) # pull EPW data for any global lat/lon + + # let's load this epw file into our MetObj container class. + metdata = demo.readEPW(epwfile) # read in the EPW weather data as metdata object. Run this with no input parameters to load a graphical picker + # if you'd rather use a TMY3 file, select one that you've already downloaded: + metdata = demo.readTMY() # select an existing TMY3 climate file. return metdata object. + +Now that we have ground albedo and a climate file loaded, we need to start designing the PV system. +Fixed tilt systems can have hourly simulations with gendaylit, or annual simulations with gencumulativesky + + +.. code-block:: python + + # create cumulativesky skyfiles and save it to the \skies\ directory, along with a .cal file in root + demo.genCumSky(demo.epwfile) + +--- optionally ---- + + +.. code-block:: python + + demo.gendaylit(metdata,4020) # pass in the metdata object, plus the integer number of the hour in the year you want to run (0 to 8759) + # note that for genCumSky, you pass the *name* of the EPW file. for gendaylit you pass the metdata object. + + +The nice thing about the RadianceObject is that it keeps track of where all of your skyfiles and calfiles are being saved. +Next let's put a PV system together. The details are saved in a dictionary and passed into makeScene. Let's start with a PV module: + + +.. code-block:: python + + # Create a new moduletype: Prism Solar Bi60. width = .984m height = 1.695m. + demo.makeModule(name='Prism Solar Bi60',x=0.984,y=1.695) #x is assumed module width, y is height. + + # Let's print the available module types + demo.printModules() + +the module details are stored in a module.json file in the bifacial_radiance\data directory so you can re-use module parameters. +Each unit module generates a corresponding .RAD file in \objects\ which is referenced in our array scene. + +There are some nifty module generation options including stacking them (e.g. 2-up or 3-up but any number) with a gap, and torque tube down the middle of the string. See the :py:func:`~bifacial_radiance.RadianceObj.makeModule` documentation for all the options, or the Jupyter Notebook tutorials for examples and visualizations of what is possible. + +To define the orientation it has to be done in the makeModule step, assigning the correct values to the x and y of the module. x is the size of the module along the row, therefore for a landscape module x > y. + +.. code-block:: python + + # make a 72-cell module 2m x 1m arranged 2-up in portrait with a 10cm torque tube behind. + # a 5cm offset between panels and the tube, along with a 5cm array gap between the modules: + + demo.makeModule(name = '1axis_2up', x = 1.995, y = 0.995, torquetube = True, tubetype = 'round', + diameter = 0.1, zgap = 0.05, ygap = 0.05, numpanels = 2) + +Now we make a sceneDict with details of our PV array. We'll make a rooftop array of Prism Solar modules in landscape +at 10 degrees tilt. + + +.. code-block:: python + + module_name = 'Prism Solar Bi60' + sceneDict = {'tilt':10,'pitch':1.5,'clearance_height':0.2,'azimuth':180, 'nMods': 20, 'nRows': 7} + # this is passed into makeScene to generate the RADIANCE .rad file + scene = demo.makeScene(module_name,sceneDict) #makeScene creates a .rad file with 20 modules per row, 7 rows. + +OK, we're almost done. RADIANCE has to combine the skyfiles, groundfiles, material (\*.mtl) files, and scene geometry (.rad) files +into an OCT file using makeOct. Instead of having to remember where all these files are, the RadianceObj keeps track. Or call .getfilelist() + + +.. code-block:: python + + octfile = demo.makeOct(demo.getfilelist()) # the input parameter is optional - maybe you have a custom file list you want to use + +The final step is to query the front and rear irradiance of our array. The default is a 9-point scan through the center module of the center row of the array. The actual scan values are set up by .makeScene and returned in your sceneObj (sceneObj.frontscan, sceneObj.backscan). To do this we use an AnalysisObj. + + +.. code-block:: python + + analysis = AnalysisObj(octfile, demo.name) # return an analysis object including the scan dimensions for back irradiance + analysis.analysis(octfile, demo.name, scene.frontscan, scene.backscan) # compare the back vs front irradiance + print('Annual bifacial ratio average: %0.3f' %( sum(analysis.Wm2Back) / sum(analysis.Wm2Front) ) ) + +We can also query specific scans along the array: + + +.. code-block:: python + + # Do a 4-point scan along the 5th module in the 2nd row of the array. + scene = demo.makeScene(module_name,sceneDict) + octfile = demo.makeOct() + analysis = AnalysisObj(octfile, demo.name) + frontscan, backscan = analysis.moduleAnalysis(scene, sensorsy = 4, modWanted = 5, rowWanted = 2) + frontresults,backresults = analysis.analysis(octfile, demo.name, scene.frontscan, scene.backscan) + print('Annual bifacial ratio on 5th Module average: %0.3f' %( sum(analysis.Wm2Back) / sum(analysis.Wm2Front) ) ) + + # And you can run the scanning for another module. + frontscan, backscan = analysis.moduleAnalysis(scene, sensorsy = 4, modWanted = 1, rowWanted = 2) + frontresults,backresults = analysis.analysis(octfile, demo.name, scene.frontscan, scene.backscan) + print('Annual bifacial ratio average on 1st Module: %0.3f' %( sum(analysis.Wm2Back) / sum(analysis.Wm2Front) ) ) + + +For more usage examples including 1-axis tracking examples, carport examples, and examples of scenes with multiple sceneObjects (different trackers/modules/etc) see the Jupyter notebooks in \docs\ + + +Functions +~~~~~~~~~ + +`RadianceObj(basename,path)`: This is the basic container for radiance projects. +Pass in a `basename` string to name your radiance scene and append to various +result and image files. `path` points to an existing or empty Radiance directory. +If the directory is empty it will be populated with appropriate ground.rad and view +files. +Default behavior: basename defaults to current date/time, and path defaults to current directory + +`RadianceObj.getfilelist()` : return list of material, sky and rad files for the scene + +`RadianceObj.returnOctFiles()` : return files in the root directory with .oct extension + +`RadianceObj.setGround(material_or_albedo, material_file)`: set the ground to either +a material type (e.g. 'litesoil') or albedo value e.g. 0.25. 'material_file' is a +filename for a specific material RAD file to load with your material description + +`RadianceObj.getEPW(lat,lon)` : download the closest EnergyPlus EPW file for a give lat / lon value. +return: filename of downloaded file + +`RadianceObj.readWeatherFile(weatherFile)` : call readEPW or readTMY functions to read in a epw or tmy file. Return: metdata + +`RadianceObj.readEPW(epwfilename)` : use pyepw to read in a epw file. Return: metdata + +`RadianceObj.readTMY(tmyfilename)` : use pvlib to read in a tmy3 file. Return: metdata + +`RadianceObj.gendaylit(metdata,timeindex)` : pass in data read from a EPW file. +Select a single time slice of the annual timeseries to conduct gendaylit Perez model +for that given time + +`RadianceObj.gencumsky(epwfilename, startdt, enddt)` : use gencumulativesky.exe to do an entire year simulation. +If no epwfilename is passed, the most recent EPW file read by `readEPW` will be used. startdt and enddt are optional +start and endtimes for the gencumulativesky. NOTE: if you don't have gencumulativesky.exe loaded, +look in bifacial_radiance/data/ for a copy + +`RadianceObj.makeOct(filelist, octname)`: create a .oct file from the scene .RAD files. By default +this will use RadianceObj.getfilelist() to build the .oct file, and use RadianceObj.basename as the filename. + +`RadianceObj.makeScene(moduletype, sceneDict)` : create a PV array scene with nMods modules per row and nRows number of rows. moduletype specifies the type of module which be one of the options saved in module.JSON (makeModule adds a customModule to the Json file). Pre-loaded module options are 'simple_panel', which generates a simple 0.95m x 1.59m module, or 'monopanel' which looks for 'objects/monopanel_1.rad'. sceneDict is a dictionary containing the following keys: 'tilt','pitch','clearance_height','azimuth', 'nMods', 'nRows'. + Return: SceneObj which includes details about the PV scene including frontscan and backscan details + +`RadianceObj.getTrackingGeometryTimeIndex(metdata, timeindex, angledelta, roundTrackerAngleBool, backtrack, gcr, hubheight, sceney)`: returns tracker tilt and clearance height for a specific point in time. + Return: tracker_theta, tracker_height, tracker_azimuth_ang + +`AnalysisObj(octfile,basename)` : Object for conducting analysis on a .OCT file. + +`AnalysisObj.makeImage(viewfile,octfile, basename)` : create visual render of scene 'octfile' from view 'views/viewfile' + +`AnalysisObj.makeFalseColor(viewfile,octfile, basename)` : create false color Wm-2 +render of scene 'octfile' from view 'views/viewfile' + +`AnalysisObj.analysis(octfile, basename, frontscan, backscan)` : conduct a general front / back ratio +analysis of a .oct file. frontscan, backscan: dictionary input for linePtsMakeDict that +is passed from AnalysisObj.makeScene. diff --git a/docs/sphinx/source/manualapi.rst b/docs/sphinx/source/manualapi.rst new file mode 100644 index 00000000..7cad9c59 --- /dev/null +++ b/docs/sphinx/source/manualapi.rst @@ -0,0 +1,164 @@ +.. currentmodule:: bifacial_radiance + +############# +API reference +############# + + +Classes +======= + +This is a collectio nof classes used by bifacial_radiance for users that prefer object-oriented programming. + +.. autosummary:: + :toctree: generated/ + + MetObj + RadianceObj + GroundObj + SceneObj + AnalysisObj + load.Params + + +Sky +==== + +Functions and methods for dealing with weather, calculating solar position and generating the skies for the raytrace simulatoins. + +Weather +------- + +.. autosummary:: + :toctree: generated/ + + RadianceObj.getEPW + RadianceObj.getEPW_all + RadianceObj.readWeatherFile + RadianceObj.readEPW + RadianceObj.readTMY + +Sky Dome +-------- +Functions and methods for establishing the sources or sky domes for the simulation + +.. autosummary:: + :toctree: generated/ + + RadianceObj.genCumSky + RadianceObj.genCumSky1axis + RadianceObj.gendaylit + RadianceObj.gendaylit2manual + RadianceObj.gendaylit1axis + +Geometry +======== + +Modules +------- +Functions and methods to generate modules + +.. autosummary:: + :toctree: generated/ + + RadianceObj.makeModule + SceneObj.readModule + SceneObj.showModule + RadianceObj.returnMaterialFiles + +Scene +----- +Functions and methods to generate the scene. + +.. autosummary:: + :toctree: generated/ + + RadianceObj.setGround + RadianceObj.set1axis + RadianceObj.makeScene + RadianceObj.makeScene1axis + RadianceObj.makeOct + RadianceObj.makeOct1axis + +Support methods for scene + +.. autosummary:: + :toctree: generated/ + + SceneObj.showScene + RadianceObj.makeCustomObject + RadianceObj.appendtoScene + +Analysis +========== + +Methods for irradiance calculations +----------------------------------- + +.. autosummary:: + :toctree: generated/ + + AnalysisObj.moduleAnalysis + AnalysisObj.analysis + RadianceObj.analysis1axis + +Mismatch +-------- + +.. autosummary:: + :toctree: generated/ + + mismatch.analysisIrradianceandPowerMismatch + +Support +======= + +Input / Output +-------------- + +.. autosummary:: + :toctree: generated/ + + load + load.load_inputvariablesfile + load.loadRadianceObj + load.loadTrackerDict + RadianceObj.loadtrackerdict + load.read1Result + load.cleanResult + load.deepcleanResult + RadianceObj.exportTrackerDict + RadianceObj.save + +Visualization +------------- + +Functions for visualizing iirradiance results + +.. autosummary:: + :toctree: generated/ + + AnalysisObj.makeFalseColor + AnalysisObj.makeImage + SceneObj.showScene + +Others +------ + +.. autosummary:: + :toctree: generated/ + + RadianceObj.getfilelist + RadianceObj.getSingleTimestampTrackerAngle + RadianceObj.returnOctFiles + +ModelChain +---------- + +.. autosummary:: + :toctree: generated/ + + load.load_inputvariablesfile + load.readconfigurationinputfile + modelchain.runModelChain + load.savedictionariestoConfigurationIniFile diff --git a/docs/sphinx/source/modelchain.rst b/docs/sphinx/source/modelchain.rst new file mode 100644 index 00000000..9eac384e --- /dev/null +++ b/docs/sphinx/source/modelchain.rst @@ -0,0 +1,65 @@ + +ModelChain +========== + +The :py:class:`~bifacial_radiance.modelchain.runModelChain` class provides a high-level +interface for standardized bifacial PV modeling. The class aims to automate much +of the modeling process while providing user-control and remaining +extensible. This guide aims to build users' understanding of the +ModelChain class. It assumes some familiarity with object-oriented +code in Python, but most information should be understandable even +without a solid understanding of classes. + +Modeling with a :py:class:`~bifacial_radiance.modelchain.runModelChain` typically involves 2 steps: + +1. Establishing all your desired values on an .ini file or in the form of dictionaries. +2. Runnin the modelchain. + +The use of the GUI is based on an internal ModelChain. In future releases, we hope to +improve the functionality of ModelChain for easiness of use in the python console. + +Defining a ModelChain +--------------------- + +ModelChain uses the keyword arguments passed to it to determine the +models for the simulation. The documentation describes the allowed +values for each keyword argument. If a keyword argument is not supplied, +ModelChain will attempt to infer the correct set of models by inspecting +the Location and PVSystem attributes. + +Below, we show some examples of how to define a ModelChain. + +Let’s make the most basic simulation for a fixed tilt scenario + +.. code-block:: python + + # CREATE AN EXMAPLE HEREe + +Some of the choices can also lead to failure when executing +:py:meth:`~bifacial_radiance.modelchain.runModelChain` if your system objects +do not contain the required parameters for running the model. + +We are improving these methods, so if you find a bug or case that does not work please consider +contributing (see :ref:`contributing` page.) + +Demystifying ModelChain internals +--------------------------------- + +The ModelChain class has a lot going in inside it in order to make +users' code as simple as possible. + +The key parts of ModelChain are: + + 2. A set of methods that wrap and call the PVSystem methods. + 3. A set of methods that inspect user-supplied objects to determine + the appropriate default models. + +run_model +~~~~~~~~~ + +Most users will only interact with the +:py:meth:`~bifacial_radiance.modelchain.runModelChain` method and ``gencumsky`` for methods and functions that can help fully define +the irradiance inputs. + +The methods called by :py:meth:`~bifacial_radiance.modelchain.runModelChain` +store their results in a series of ModelChain Dictionary attributes: ``settingParamsSimul``, etc. diff --git a/docs/sphinx/source/package_overview.rst b/docs/sphinx/source/package_overview.rst new file mode 100644 index 00000000..f21c06cc --- /dev/null +++ b/docs/sphinx/source/package_overview.rst @@ -0,0 +1,6 @@ +.. _package_overview: + +Package Overview +================ + +This is also still under construction. ! :D diff --git a/docs/sphinx/source/validation.rst b/docs/sphinx/source/validation.rst new file mode 100644 index 00000000..63a9450c --- /dev/null +++ b/docs/sphinx/source/validation.rst @@ -0,0 +1,6 @@ +.. _validation: + +Validation +=========== + +This is under construction ! :D diff --git a/docs/sphinx/source/variables_style_rules.rst b/docs/sphinx/source/variables_style_rules.rst new file mode 100644 index 00000000..238b092c --- /dev/null +++ b/docs/sphinx/source/variables_style_rules.rst @@ -0,0 +1,7 @@ +.. _variables_style_rules: + +Variables and Symbols +===================== + +Here is a convention on consistent variable names throughout the library: + diff --git a/docs/sphinx/source/whatsnew.rst b/docs/sphinx/source/whatsnew.rst new file mode 100644 index 00000000..bf700852 --- /dev/null +++ b/docs/sphinx/source/whatsnew.rst @@ -0,0 +1,23 @@ +.. _whatsnew: + +********** +What's New +********** + +These are new features and improvements of note in each release. + +.. include:: whatsnew/v0.3.2.rst +.. include:: whatsnew/v0.3.0.rst +.. include:: whatsnew/v0.2.4.rst +.. include:: whatsnew/v0.2.3.rst +.. include:: whatsnew/v0.2.2.rst +.. include:: whatsnew/v0.2.1.rst +.. include:: whatsnew/v0.2.0.rst +.. include:: whatsnew/v0.1.1.rst +.. include:: whatsnew/v0.1.0.rst +.. include:: whatsnew/v0.0.5.rst +.. include:: whatsnew/v0.0.4.rst +.. include:: whatsnew/v0.0.3.rst +.. include:: whatsnew/v0.0.2.rst +.. include:: whatsnew/v0.0.1.rst + diff --git a/docs/sphinx/source/whatsnew/v0.0.1.rst b/docs/sphinx/source/whatsnew/v0.0.1.rst new file mode 100644 index 00000000..10f8038e --- /dev/null +++ b/docs/sphinx/source/whatsnew/v0.0.1.rst @@ -0,0 +1,6 @@ +.. _whatsnew_0001: + +v0.0.1 (?? / ?? / 2018) +------------------------ + +Initial stable release. diff --git a/docs/sphinx/source/whatsnew/v0.0.2.rst b/docs/sphinx/source/whatsnew/v0.0.2.rst new file mode 100644 index 00000000..fee1ca48 --- /dev/null +++ b/docs/sphinx/source/whatsnew/v0.0.2.rst @@ -0,0 +1,6 @@ +.. _whatsnew_0002: + +v0.0.2 (?? / ?? / 2018) +------------------------ + +Adjustable ``azimuth`` angle other than 180. diff --git a/docs/sphinx/source/whatsnew/v0.0.3.rst b/docs/sphinx/source/whatsnew/v0.0.3.rst new file mode 100644 index 00000000..75f945d8 --- /dev/null +++ b/docs/sphinx/source/whatsnew/v0.0.3.rst @@ -0,0 +1,6 @@ +.. _whatsnew_0003: + +v0.0.3 (?? / ?? / 2018) +------------------------ + +Arbitrary NxR number of modules and rows for :py:class:`~bifacial_radiance.SceneObj` diff --git a/docs/sphinx/source/whatsnew/v0.0.4.rst b/docs/sphinx/source/whatsnew/v0.0.4.rst new file mode 100644 index 00000000..0e329a99 --- /dev/null +++ b/docs/sphinx/source/whatsnew/v0.0.4.rst @@ -0,0 +1,6 @@ +.. _whatsnew_0004: + +v0.0.4 (?? / ?? / 2018) +------------------------ + +Include configuration file module.json and custom module configuration. diff --git a/docs/sphinx/source/whatsnew/v0.0.5.rst b/docs/sphinx/source/whatsnew/v0.0.5.rst new file mode 100644 index 00000000..8fef7144 --- /dev/null +++ b/docs/sphinx/source/whatsnew/v0.0.5.rst @@ -0,0 +1,9 @@ +.. _whatsnew_0005: + +v0.0.5 (?? / ?? / 2018) +------------------------ + +* 1-axis tracking draft. +* :py:func:`~bifacial_radiance.RadianceObj.gencumsky` now allows ``-G`` filetype optoin for support of 1-axis tracking. + + diff --git a/docs/sphinx/source/whatsnew/v0.1.0.rst b/docs/sphinx/source/whatsnew/v0.1.0.rst new file mode 100644 index 00000000..55a22226 --- /dev/null +++ b/docs/sphinx/source/whatsnew/v0.1.0.rst @@ -0,0 +1,8 @@ +.. _whatsnew_0100: + +v0.1.0 (?? / ?? / 2018) +------------------------ + +1-axis bug fix and validation vs PVSyst and ViewFactor model. + + diff --git a/docs/sphinx/source/whatsnew/v0.1.1.rst b/docs/sphinx/source/whatsnew/v0.1.1.rst new file mode 100644 index 00000000..1ce43f8f --- /dev/null +++ b/docs/sphinx/source/whatsnew/v0.1.1.rst @@ -0,0 +1,8 @@ +.. _whatsnew_0101: + +v0.1.1 (?? / ?? / 2019) +------------------------ + +Allow southern latitudes. + + diff --git a/docs/sphinx/source/whatsnew/v0.2.0.rst b/docs/sphinx/source/whatsnew/v0.2.0.rst new file mode 100644 index 00000000..32c1518f --- /dev/null +++ b/docs/sphinx/source/whatsnew/v0.2.0.rst @@ -0,0 +1,6 @@ +.. _whatsnew_0200: + +v0.2.0 (?? / ?? / 2019) +------------------------ + +Critical 1-axis tracking update to fix geometry issues that were over-predicting 1-axis results diff --git a/docs/sphinx/source/whatsnew/v0.2.1.rst b/docs/sphinx/source/whatsnew/v0.2.1.rst new file mode 100644 index 00000000..d7de64b7 --- /dev/null +++ b/docs/sphinx/source/whatsnew/v0.2.1.rst @@ -0,0 +1,9 @@ +.. _whatsnew_0201: + +v0.2.1 (?? / ?? / 2019) +------------------------ + +* Allow TMY3 input files +* Using a different EPW file reader. + + diff --git a/docs/sphinx/source/whatsnew/v0.2.2.rst b/docs/sphinx/source/whatsnew/v0.2.2.rst new file mode 100644 index 00000000..d5d9f240 --- /dev/null +++ b/docs/sphinx/source/whatsnew/v0.2.2.rst @@ -0,0 +1,7 @@ +.. _whatsnew_0202: + +v0.2.2 (?? / ?? / 2019) +------------------------ + +Negative 1 hour offset to TMY file inputs + diff --git a/docs/sphinx/source/whatsnew/v0.2.3.rst b/docs/sphinx/source/whatsnew/v0.2.3.rst new file mode 100644 index 00000000..9f78833d --- /dev/null +++ b/docs/sphinx/source/whatsnew/v0.2.3.rst @@ -0,0 +1,21 @@ +.. _whatsnew_0203: + +v0.2.3 (?? / ?? / 2019) +------------------------ + +This release introduces the option to module torque tubes now added as part of makeModule. + +Enhancements +~~~~~~~~~~~~ +* Arbitrary length and position of module scans in makeScene. +* Torquetube option to makeModule :py:func:`~bifacial_radiance.RadianceObj.makeModule` +* Option to add module gaps ``xgap``, ``ygap``, and ``zgap`` in :py:func:`~bifacial_radiance.RadianceObj.makeModule` +* New functions :py:func:`bifacial_radiance.RadianceObj.gendaylit1axis`, + :py:func:`bifacial_radiance.RadianceObj.makeOct1axis` and + :py:func:`bifacial_radiance.RadianceObj.analysis1axis` +* ``trackerdict`` already mapped to ``self.trackerdict``, so no longer mapping on :py:func:`~bifacial_radiance.RadianceObj.analysis1axis` + +Contributors +~~~~~~~~~~~~ +* Chris Deline (:ghuser:`cdeline`) +* Silvana Ayala (:ghuser:`shirubana`) diff --git a/docs/sphinx/source/whatsnew/v0.2.4.rst b/docs/sphinx/source/whatsnew/v0.2.4.rst new file mode 100644 index 00000000..d7adf0a9 --- /dev/null +++ b/docs/sphinx/source/whatsnew/v0.2.4.rst @@ -0,0 +1,40 @@ +.. _whatsnew_0204: + +v0.2.4 (?? / ?? / 2019) +------------------------ + +Various improvements across the bifacial_radiance software. + +API Changes +~~~~~~~~~~~ +* Module orientation deprecated. Now module is defined by ``x`` (dimension along rack) and ``y`` (dimensio nin slant direction). +* Module gap variables renamed to ``xgap``, ``ygap``, and ``zgap``. +* ``scenex`` and ``sceney`` variables introduced which include torquetube and gap dimensions. +* Py36 and cross-platform code compliance implemented +* Modified :py:func:`~bifacial_radiance.RadianceObj.gendaylit` to be based on sun positions by default +* :py:func:`~bifacial_radiance.RadianceObj.gendaylit` now uses PVLib for calculating sun position angles instea dof using Radiance internal sun positoin calculations. +* Updated :py:func:`bifacial_radiance.AnalysisObj.makeFalseColor` routine for visualizing in falsecolor the octfile. +* Moved existing and new load routines into :py:class:`~bifacial_radiance.load` + + +Enhancements +~~~~~~~~~~~~ +* More torquetube options added (round, square, hexagonal and octagonal profiles) +* custom spacing between modules in a row added, +* Added accuracy input option for 1-axis scans +* Update to sensor position on 1axistracking. + + +Bugs +~~~~ +* Module-select and module scan bug fixed + +Testing +~~~~~~~ +* Updates to pytests. + + +Contributors +~~~~~~~~~~~~ +* Chris Deline (:ghuser:`cdeline`) +* Silvana Ayala (:ghuser:`shirubana`) diff --git a/docs/sphinx/source/whatsnew/v0.3.0.rst b/docs/sphinx/source/whatsnew/v0.3.0.rst new file mode 100644 index 00000000..814a50c8 --- /dev/null +++ b/docs/sphinx/source/whatsnew/v0.3.0.rst @@ -0,0 +1,31 @@ +.. _whatsnew_0300: + +v0.3.0 (?? / ?? / 2019) +------------------------ + +This is a major release that includes the release of a new Visual Interface (GUI). We recommend all users to upgrade to this release as many internal geometry handling has been updated and made more accurate. + +API Changes +~~~~~~~~~~~~~ +* Dictionaries implemented as inputs to most functions. +* Height deprecated as input, now functions expect `clerance_height` or `hub_height`. + +Enhancements +~~~~~~~~~~~~ +* New GUI. +* Modelchains implemented. +* New internal Geometry handling. +* Modules get centered on X and Y. +* Geometry of scenes gets constructed around (X,Y) (0,0), placing the center of the center module of the center row in that position +* New/improved sensor locations. +* Multiple Scene object capability for fixed scenes. +* HPC friendly code. +* More module creation options +* cell Level model capability with dictionary input. +* Axis of rotation around torque tube possible with `axisofrotation` parameter. + + +Contributors +~~~~~~~~~~~~ +* Chris Deline (:ghuser:`cdeline`) +* Silvana Ayala (:ghuser:`shirubana`) diff --git a/docs/sphinx/source/whatsnew/v0.3.2.rst b/docs/sphinx/source/whatsnew/v0.3.2.rst new file mode 100644 index 00000000..889f0159 --- /dev/null +++ b/docs/sphinx/source/whatsnew/v0.3.2.rst @@ -0,0 +1,64 @@ +.. _whatsnew_0302: + +v0.3.2 (10 / 14 / 2019) +------------------------ + +This is a major release that adds many power mismatch analysis features, GUI updates as well as documentation. + +API Changes +~~~~~~~~~~~ +* :py:class:`~bifacial_radiance.RadianceObj.readWeatherFile` set as main function to read TMY and EPW files. +* Added ``startdate`` and ``enddate`` inputs to py:class:`~bifacial_radiance.RadianceObj.readWeatherFile`, in `str` format ``MM_DD_HH``. +* Deprecated :py:class:`~bifacial_radiance.RadianceObj.gencumsky` startdate and enddate options. +* Fixed tilt simulations can now use the *tracking workflow* by passing a ``fixed_tilt_angle`` variable into :py:class:`~bifacial_radiance.RadianceObj.set1axis`. This has been implemented as the default for hourly simulations in the ModelChain and GUI. +* Renamed many functions with a dash, to differentiate from functions called by user and helper-functions that are subfunctions or called by the main functions. (:issue:`155`) +* Update internal paths to TESTDIR rather than os.cwd +* :py:class:`~bifacial_radiance.RadianceObj.gendaylit`: update to use the sunposition automatically stored with ``metdata`` +* Corrected print statements for full year hourly simulations to reflect the exact number of datapoints considered in that ratio average. +* Changed functionality of ModelChain for tracking: if ``simulationParamsDict[dayDateSimulation]`` is `False`, default to full-year simulation even if ``timeParamsDict`` is passed. +* Added print statement for fixed tilt and tracking modelchain results. +* Raise error if tracking modelchain is used with ``timestampRangeSimulation`` +* Updated modelchain ``daydate`` functionality: when starthour = endhour gives that hour of analysis. +* Add filenotfound error checking for loading .ini file +* Added analysis.py to modules loaded by bifacial_radiance as default. +* Trackerdict now keeps track of hub_height (:issue:`168`) +* Updated requirements for PVLib > 0.6.1 to avoid deprecation warnings (:issue:`158`) + + +Enhancements +~~~~~~~~~~~~ +* Improved cross-platform support with linux (:issue:`128`), (:issue:`130`), (:issue:`127`), (:issue:`126`). +* Trackerdict functions now analyze/do their stuff in an ordered manner. (makeOct1axis, set1axis, makeScene1axis). +* Power Mismatch calculation function as well as irradiance statistics in module ``mismatch.py``. + + +Bug fixes +~~~~~~~~~ +* Fix accuracy setting for :py:func:`~bifacial_radiance.RadianceObj.analysis1axis` on (:issue:`150`) +* GUI: GCR or Pitch independently read based on selection, so no need for values to match (:issue:`143`) +* Fixed tracking daydate hourly (:issue:`125`) +* Fix to avoid divide-by-zero errors in :py:class:`~bifacial_radiance.RadianceObj.gendaylit` +* Fix GUI tracking hourly by day, which was still reading enddate (:issue:`136`) +* GUI: fixing automatically downloading getEPW when readEPW was selected. + + +Testing +~~~~~~~ +* Increase in pytesting, particularly for secondary modules in bifacial_radiance (other than main.py) +* Tracking pytesting with `coveralls `_ +* Rename test .ini files to avoid name confusion with test.py files +* Add test for bad RadianceObj path + + +Documentation +~~~~~~~~~~~~~~ +* readtheDocs now connected. Moved all documentation to sphinx format. +* Revamping of journals, with more explanations, images, and sorted by level of difficulty. + + +Contributors +~~~~~~~~~~~~ +* Chris Deline (:ghuser:`cdeline`) +* Silvana Ayala (:ghuser:`shirubana`) +* Jason Alderman (:ghuser:`jalderman9`) +* Mark Mikofski (:ghuser:`mikofski`) diff --git a/docs/tutorials/1 - Introductory Example - Fixed Tilt simple setup.ipynb b/docs/tutorials/1 - Introductory Example - Fixed Tilt simple setup.ipynb new file mode 100644 index 00000000..70be619f --- /dev/null +++ b/docs/tutorials/1 - Introductory Example - Fixed Tilt simple setup.ipynb @@ -0,0 +1,726 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# 1 - Introductory Example: Fixed-Tilt simple setup\n", + "\n", + "This jupyter journal will walk us through the creation of the most basic fixed-tilt simulation possible with bifacial_radiance.\n", + "We will simulate a 1-up landscape system over a white rooftop.\n", + "\n", + "Steps include:\n", + "\n", + "
    \n", + "
  1. Create a folder for your simulation, and Load bifacial_radiance
  2. \n", + "
  3. Create a Radiance Object
  4. \n", + "
  5. Set the Albedo
  6. \n", + "
  7. Download Weather Files
  8. \n", + "
  9. Generate the Sky
  10. \n", + "
  11. Define a Module type
  12. \n", + "
  13. Create the scene
  14. \n", + "
  15. Combine Ground, Sky and Scene Objects
  16. \n", + "
  17. Analyze and get results
  18. \n", + "
  19. Visualize scene options
  20. \n", + "
\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "## 1. Create a folder for your simulation, and load bifacial_radiance \n", + "\n", + "First let's set the folder where the simulation will be saved. By default, this is the TEMP folder in the bifacial_radiance distribution.\n", + "\n", + "The lines below find the location of the folder relative to this Jupyter Journa. You can alternatively point to an empty directory (it will open a load GUI Visual Interface) or specify any other directory in your computer, for example:\n", + "\n", + "#### testfolder = r'C:\\Users\\sayala\\Documents\\RadianceScenes\\Tutorials\\Journal1'\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Your simulation will be stored in C:\\Users\\sayala\\Documents\\GitHub\\bifacial_radiance\\bifacial_radiance\\TEMP\n" + ] + } + ], + "source": [ + "import os\n", + "testfolder = os.path.abspath(r'..\\..\\bifacial_radiance\\TEMP') \n", + "\n", + "print (\"Your simulation will be stored in %s\" % testfolder)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This will load bifacial_radiance and other libraries from python that will be useful for this Jupyter Journal:" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " from bifacial_radiance import *\n", + "except ImportError:\n", + " raise RuntimeError('bifacial_radiance is required. download distribution')\n", + "\n", + "import numpy as np" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 2. Create a Radiance Object" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "path = C:\\Users\\sayala\\Documents\\GitHub\\bifacial_radiance\\bifacial_radiance\\TEMP\n" + ] + } + ], + "source": [ + "# Create a RadianceObj 'object' named bifacial_example. no whitespace allowed\n", + "demo = RadianceObj('bifacial_example',testfolder) " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This will create all the folder structure of the bifacial_radiance Scene in the designated testfolder in your computer, and it should look like this:\n", + "\n", + "\n", + "![Folder Structure](../images_wiki/Journal1Pics/folderStructure.png)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 3. Set the Albedo" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To see more options of ground materials available (located on ground.rad), run this function without any input. " + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Input albedo 0-1, or ground material names:['black', 'litesoil', 'concrete', 'white_EPDM', 'beigeroof', 'beigeroof_lite', 'beigeroof_heavy', 'asphalt', 'greyroof', 'Metal_Aluminum_Anodized', 'Color_I11', 'M_0010_Snow', 'Metal_Grey']\n" + ] + } + ], + "source": [ + "# Input albedo number or material name like 'concrete'. \n", + "demo.setGround() # This prints available materials." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "If a number between 0 and 1 is passed, it assumes it's an albedo value. For this example, we want a high-reflectivity rooftop albedo surface, so we will set the albedo to 0.62" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "albedo = 0.62\n", + "demo.setGround(albedo)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 4. Download and Load Weather Files\n", + "\n", + "There are various options provided in bifacial_radiance to load weatherfiles. getEPW is useful because you just set the latitude and longitude of the location and it donwloads the meteorologicla data for any location. " + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Getting weather file: USA_VA_Richmond.Intl.AP.724010_TMY.epw\n", + " ... OK!\n" + ] + } + ], + "source": [ + "# Pull in meteorological data using pyEPW for any global lat/lon\n", + "epwfile = demo.getEPW(lat = 37.5, lon = -77.6) # This location corresponds to Richmond, VA." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The downloaded EPW will be in the EPWs folder.\n", + "\n", + "To load the data, use readWeatherFile. This reads EPWs, TMY meterological data, or even your own data as long as it follows TMY data format (With any time resoultion)." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "# Read in the weather data pulled in above. \n", + "metdata = demo.readWeatherFile(epwfile) " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 5. Generate the Sky.\n", + "\n", + "Sky definitions can either be for a single time point with gendaylit function,\n", + "or using gencumulativesky to generate a cumulativesky for the entire year.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "message: There were 4255 sun up hours in this climate file\r\n", + "Total Ibh/Lbh: 0.000000\n" + ] + } + ], + "source": [ + "fullYear = True\n", + "if fullYear:\n", + " demo.genCumSky(demo.epwfile) # entire year.\n", + "else:\n", + " demo.gendaylit(metdata,4020) # Noon, June 17th (timepoint # 4020)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The method gencumSky calculates the hourly radiance of the sky hemisphere by dividing it into 145 patches. Then it adds those hourly values to generate one single cumulative sky. Here is a visualization of this patched hemisphere for Richmond, VA, US. Can you deduce from the radiance values of each patch which way is North?\n", + "\n", + "![Example of the hemisphere cumulative sky](../images_wiki/Journal1Pics/cumulativesky.png)\n", + "\n", + "Answer: Since Richmond is in the Northern Hemisphere, the modules face the south, which is where most of the radiation from the sun is coming. The north in this picture is the darker blue areas." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 6. DEFINE a Module type\n", + "\n", + "You can create a custom PV module type. In this case we are defining a module named \"Prism Solar Bi60\", in landscape. The x value defines the size of the module along the row, so for landscape modules x > y. This module measures y = 0.984 x = 1.695. \n", + "\n", + "\n", + "
\n", + "Modules in this example are 100% opaque. For drawing each cell, makeModule needs more inputs with cellLevelModule = True. You can also specify a lot more variables in makeModule like multiple modules, torque tubes, spacing between modules, etc. Reffer to the Module Documentation and read the following jupyter journals to explore all your options.\n", + "
\n" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Module Name: Prism_Solar_Bi60_landscape\n", + "REWRITING pre-existing module file. \n", + "Module Prism Solar Bi60 landscape successfully created\n" + ] + }, + { + "data": { + "text/plain": [ + "{'x': 1.695,\n", + " 'y': 0.984,\n", + " 'scenex': 1.705,\n", + " 'sceney': 0.984,\n", + " 'scenez': 0.15,\n", + " 'numpanels': 1,\n", + " 'bifi': 1,\n", + " 'text': '! genbox black Prism_Solar_Bi60_landscape 1.695 0.984 0.02 | xform -t -0.8475 -0.492 0 -a 1 -t 0 0.984 0',\n", + " 'modulefile': 'objects\\\\Prism_Solar_Bi60_landscape.rad',\n", + " 'offsetfromaxis': 0,\n", + " 'xgap': 0.01,\n", + " 'ygap': 0.0,\n", + " 'zgap': 0.1,\n", + " 'cellModule': None,\n", + " 'torquetube': {'bool': False,\n", + " 'diameter': 0.1,\n", + " 'tubetype': 'Round',\n", + " 'material': 'Metal_Grey'}}" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "module_type = 'Prism Solar Bi60 landscape' \n", + "demo.makeModule(name=module_type,x=1.695, y=0.984)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In case you want to use a pre-defined module or a module you've created previously, they are stored in a JSON format in data/module.json, and the options available can be called with printModules:" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Usage: SceneObj(moduletype)\n", + "No module type selected. Available module types: dict_keys(['Prism Solar Bi60', '2upTracker', 'test', 'Prism Solar Bi60 landscape', 'cellModule', 'Custom Tracker Module'])\n", + "Available module names: ['Prism Solar Bi60', '2upTracker', 'test', 'Prism Solar Bi60 landscape', 'cellModule', 'Custom Tracker Module']\n" + ] + } + ], + "source": [ + "availableModules = demo.printModules()\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 7. Make the Scene\n", + "\n", + " The sceneDicitonary specifies the information of the scene, such as number of rows, number of modules per row, azimuth, tilt, clearance_height (distance between the ground and lowest point of the module) and any other parameter. \n", + " \n", + " Azimuth gets measured from N = 0, so for South facing modules azimuth should equal 180 degrees\n" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ + "sceneDict = {'tilt':10,'pitch':3,'clearance_height':0.2,'azimuth':180, 'nMods': 20, 'nRows': 7} \n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To make the scene we have to create a Scene Object through the method makeScene. This method will create a .rad file in the objects folder, with the parameters specified in sceneDict and the module created above." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [], + "source": [ + "scene = demo.makeScene(module_type,sceneDict)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 8. COMBINE the Ground, Sky, and the Scene Objects\n", + "\n", + "Radiance requires an \"Oct\" file that combines the ground, sky and the scene object into it. \n", + "The method makeOct does this for us." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Created bifacial_example.oct\n" + ] + } + ], + "source": [ + "octfile = demo.makeOct(demo.getfilelist()) " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To see what files got merged into the octfile, you can use the helper method getfilelist. This is useful for advanced simulations too, specially when you want to have different Scene objects in the same simulation, or if you want to add other custom elements to your scene (like a building, for example)" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['materials\\\\ground.rad',\n", + " 'skies\\\\cumulative.rad',\n", + " 'objects\\\\Prism_Solar_Bi60_landscape_0.2_3_10_20x7_origin0,0.rad']" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "demo.getfilelist()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 9. ANALYZE and get Results\n", + "\n", + "Once the octfile tying the scene, ground and sky has been created, we create an Analysis Object. We first have to create an Analysis object, and then we have to specify where the sensors will be located with moduleAnalysis. \n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "First let's create the Analysis Object" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [], + "source": [ + "analysis = AnalysisObj(octfile, demo.basename)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Then let's specify the sensor location. If no parameters are passed to moduleAnalysis, it will scan the center module of the center row:" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "frontscan, backscan = analysis.moduleAnalysis(scene)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The frontscan and backscan include a linescan along a chord of the module, both on the front and back. \n", + "\n", + "![Simple example for south facing module](../images_wiki/Journal1Pics/frontscan_backscan.png)\n", + "Analysis saves the measured irradiances in the front and in the back on the results folder. Prints out the ratio of the average of the rear and front irradiance values along a chord of the module." + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Linescan in process: bifacial_example_Front\n", + "Linescan in process: bifacial_example_Back\n", + "Saved: results\\irr_bifacial_example.csv\n" + ] + } + ], + "source": [ + "results = analysis.analysis(octfile, demo.basename, frontscan, backscan) \n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The results are also automatically saved in the results folder. Some of our input/output functions can be used to read the results and work with them, for example:" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": {}, + "outputs": [ + { + "ename": "FileNotFoundError", + "evalue": "[Errno 2] File b'results\\\\irr_bifacial_example.csv' does not exist: b'results\\\\irr_bifacial_example.csv'", + "output_type": "error", + "traceback": [ + "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[1;31mFileNotFoundError\u001b[0m Traceback (most recent call last)", + "\u001b[1;32m\u001b[0m in \u001b[0;36m\u001b[1;34m\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[0mload\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mread1Result\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m'results\\irr_bifacial_example.csv'\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[1;32mc:\\users\\sayala\\documents\\github\\bifacial_radiance\\bifacial_radiance\\load.py\u001b[0m in \u001b[0;36mread1Result\u001b[1;34m(selectfile)\u001b[0m\n\u001b[0;32m 163\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 164\u001b[0m \u001b[1;31m#resultsDict = pd.read_csv(os.path.join('results',selectfile))\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 165\u001b[1;33m \u001b[0mresultsDF\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mpd\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mread_csv\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mselectfile\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 166\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 167\u001b[0m \u001b[1;31m#return(np.array(temp['Wm2Front']), np.array(temp['Wm2Back']))\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", + "\u001b[1;32mC:\\ProgramData\\Anaconda3\\lib\\site-packages\\pandas\\io\\parsers.py\u001b[0m in \u001b[0;36mparser_f\u001b[1;34m(filepath_or_buffer, sep, delimiter, header, names, index_col, usecols, squeeze, prefix, mangle_dupe_cols, dtype, engine, converters, true_values, false_values, skipinitialspace, skiprows, skipfooter, nrows, na_values, keep_default_na, na_filter, verbose, skip_blank_lines, parse_dates, infer_datetime_format, keep_date_col, date_parser, dayfirst, iterator, chunksize, compression, thousands, decimal, lineterminator, quotechar, quoting, doublequote, escapechar, comment, encoding, dialect, tupleize_cols, error_bad_lines, warn_bad_lines, delim_whitespace, low_memory, memory_map, float_precision)\u001b[0m\n\u001b[0;32m 700\u001b[0m skip_blank_lines=skip_blank_lines)\n\u001b[0;32m 701\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 702\u001b[1;33m \u001b[1;32mreturn\u001b[0m \u001b[0m_read\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mfilepath_or_buffer\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mkwds\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 703\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 704\u001b[0m \u001b[0mparser_f\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m__name__\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mname\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", + "\u001b[1;32mC:\\ProgramData\\Anaconda3\\lib\\site-packages\\pandas\\io\\parsers.py\u001b[0m in \u001b[0;36m_read\u001b[1;34m(filepath_or_buffer, kwds)\u001b[0m\n\u001b[0;32m 427\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 428\u001b[0m \u001b[1;31m# Create the parser.\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 429\u001b[1;33m \u001b[0mparser\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mTextFileReader\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mfilepath_or_buffer\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;33m**\u001b[0m\u001b[0mkwds\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 430\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 431\u001b[0m \u001b[1;32mif\u001b[0m \u001b[0mchunksize\u001b[0m \u001b[1;32mor\u001b[0m \u001b[0miterator\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", + "\u001b[1;32mC:\\ProgramData\\Anaconda3\\lib\\site-packages\\pandas\\io\\parsers.py\u001b[0m in \u001b[0;36m__init__\u001b[1;34m(self, f, engine, **kwds)\u001b[0m\n\u001b[0;32m 893\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0moptions\u001b[0m\u001b[1;33m[\u001b[0m\u001b[1;34m'has_index_names'\u001b[0m\u001b[1;33m]\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mkwds\u001b[0m\u001b[1;33m[\u001b[0m\u001b[1;34m'has_index_names'\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 894\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 895\u001b[1;33m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m_make_engine\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mengine\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 896\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 897\u001b[0m \u001b[1;32mdef\u001b[0m \u001b[0mclose\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", + "\u001b[1;32mC:\\ProgramData\\Anaconda3\\lib\\site-packages\\pandas\\io\\parsers.py\u001b[0m in \u001b[0;36m_make_engine\u001b[1;34m(self, engine)\u001b[0m\n\u001b[0;32m 1120\u001b[0m \u001b[1;32mdef\u001b[0m \u001b[0m_make_engine\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mengine\u001b[0m\u001b[1;33m=\u001b[0m\u001b[1;34m'c'\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 1121\u001b[0m \u001b[1;32mif\u001b[0m \u001b[0mengine\u001b[0m \u001b[1;33m==\u001b[0m \u001b[1;34m'c'\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m-> 1122\u001b[1;33m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m_engine\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mCParserWrapper\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mf\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;33m**\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0moptions\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 1123\u001b[0m \u001b[1;32melse\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 1124\u001b[0m \u001b[1;32mif\u001b[0m \u001b[0mengine\u001b[0m \u001b[1;33m==\u001b[0m \u001b[1;34m'python'\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", + "\u001b[1;32mC:\\ProgramData\\Anaconda3\\lib\\site-packages\\pandas\\io\\parsers.py\u001b[0m in \u001b[0;36m__init__\u001b[1;34m(self, src, **kwds)\u001b[0m\n\u001b[0;32m 1851\u001b[0m \u001b[0mkwds\u001b[0m\u001b[1;33m[\u001b[0m\u001b[1;34m'usecols'\u001b[0m\u001b[1;33m]\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0musecols\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 1852\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m-> 1853\u001b[1;33m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m_reader\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mparsers\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mTextReader\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0msrc\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;33m**\u001b[0m\u001b[0mkwds\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 1854\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0munnamed_cols\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m_reader\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0munnamed_cols\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 1855\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n", + "\u001b[1;32mpandas/_libs/parsers.pyx\u001b[0m in \u001b[0;36mpandas._libs.parsers.TextReader.__cinit__\u001b[1;34m()\u001b[0m\n", + "\u001b[1;32mpandas/_libs/parsers.pyx\u001b[0m in \u001b[0;36mpandas._libs.parsers.TextReader._setup_parser_source\u001b[1;34m()\u001b[0m\n", + "\u001b[1;31mFileNotFoundError\u001b[0m: [Errno 2] File b'results\\\\irr_bifacial_example.csv' does not exist: b'results\\\\irr_bifacial_example.csv'" + ] + } + ], + "source": [ + "load.read1Result('results\\irr_bifacial_example.csv')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As can be seen in the results for the *Wm2Front* and *WM2Back*, the irradiance values are quite high. This is because a cumulative sky simulation was performed on step 5 , so this is the total irradiance over all the hours of the year that the module at each sampling point will receive. Dividing the back irradiance average by the front irradiance average will give us the bifacial gain for the year:\n", + "\n", + "![Bifacial Gain in Irradiance Formula](../images_wiki/Journal1Pics/BGG_Formula.png)\n", + "\n", + "Assuming that our module from Prism Solar has a bifaciality factor (rear to front performance) of 90%, our bifacial gain is of:" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Annual bifacial ratio: 0.15 \n" + ] + } + ], + "source": [ + "bifacialityfactor = 0.9\n", + "print('Annual bifacial ratio: %0.2f ' %( np.mean(analysis.Wm2Back) * bifacialityfactor / np.mean(analysis.Wm2Front)) )\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 10. View / Render the Scene\n", + "\n", + "If you used gencumsky or gendaylit, you can view the Scene by navigating on a command line to the folder and typing:\n", + "\n", + "##### objview materials\\ground.rad objects\\Prism_Solar_Bi60_landscape_0.2_3_10_20x7_origin0,0.rad \n", + "\n", + "This objview has 3 different light sources of its own, so the shading is not representative.\n", + "\n", + "ONLY If you used gendaylit , you can view the scene correctly illuminated with the sky you generated after generating the oct file, with \n", + "\n", + "##### rvu -vf views\\front.vp -e .01 bifacial_example.oct\n", + "\n", + "The rvu manual can be found here: manual page here: http://radsite.lbl.gov/radiance/rvu.1.html\n", + "\n", + "Or you can also use the code below from bifacial_radiance to generate an HDR rendered image of the scene. You can choose from front view or side view in the views folder:" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Generating visible render of scene\n", + "Generating scene in WM-2. This may take some time.\n", + "Saving scene in false color\n" + ] + } + ], + "source": [ + "# Make a color render and falsecolor image of the scene.\n", + "analysis.makeImage('side.vp')\n", + "analysis.makeFalseColor('side.vp')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This is how the False Color image stored in images folder should look like:\n", + "\n", + "![OpenHDR image example of False color](../images_wiki/Journal1Pics/openhdr_FalseColorExample.png)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Files are saved as .hdr (high definition render) files. Try LuminanceHDR viewer (free) to view them, or https://viewer.openhdr.org/ \n", + "\n" + ] + } + ], + "metadata": { + "anaconda-cloud": {}, + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.3" + } + }, + "nbformat": 4, + "nbformat_minor": 1 +} diff --git a/docs/tutorials/1 - Introductory Example - Fixed Tilt simple setup.py b/docs/tutorials/1 - Introductory Example - Fixed Tilt simple setup.py new file mode 100644 index 00000000..8a64d723 --- /dev/null +++ b/docs/tutorials/1 - Introductory Example - Fixed Tilt simple setup.py @@ -0,0 +1,305 @@ +#!/usr/bin/env python +# coding: utf-8 + +# # 1 - Introductory Example: Fixed-Tilt simple setup +# +# This jupyter journal will walk us through the creation of the most basic fixed-tilt simulation possible with bifacial_radiance. +# We will simulate a 1-up landscape system over a white rooftop. +# +# Steps include: +# +#
    +#
  1. Create a folder for your simulation, and Load bifacial_radiance
  2. +#
  3. Create a Radiance Object
  4. +#
  5. Set the Albedo
  6. +#
  7. Download Weather Files
  8. +#
  9. Generate the Sky
  10. +#
  11. Define a Module type
  12. +#
  13. Create the scene
  14. +#
  15. Combine Ground, Sky and Scene Objects
  16. +#
  17. Analyze and get results
  18. +#
  19. Visualize scene options
  20. +#
+# + +# + +# +# ## 1. Create a folder for your simulation, and load bifacial_radiance +# +# First let's set the folder where the simulation will be saved. By default, this is the TEMP folder in the bifacial_radiance distribution. +# +# The lines below find the location of the folder relative to this Jupyter Journa. You can alternatively point to an empty directory (it will open a load GUI Visual Interface) or specify any other directory in your computer, for example: +# +# #### testfolder = r'C:\Users\sayala\Documents\RadianceScenes\Tutorials\Journal1' +# +# + +# In[1]: + + +import os +testfolder = os.path.abspath(r'..\..\bifacial_radiance\TEMP') + +print ("Your simulation will be stored in %s" % testfolder) + + +# This will load bifacial_radiance and other libraries from python that will be useful for this Jupyter Journal: + +# In[2]: + + +try: + from bifacial_radiance import * +except ImportError: + raise RuntimeError('bifacial_radiance is required. download distribution') + +import numpy as np + + +# + +# ## 2. Create a Radiance Object + +# In[3]: + + +# Create a RadianceObj 'object' named bifacial_example. no whitespace allowed +demo = RadianceObj('bifacial_example',testfolder) + + +# This will create all the folder structure of the bifacial_radiance Scene in the designated testfolder in your computer, and it should look like this: +# +# +# ![Folder Structure](../images_wiki/Journal1Pics/folderStructure.png) + +# + +# ## 3. Set the Albedo + +# To see more options of ground materials available (located on ground.rad), run this function without any input. + +# In[4]: + + +# Input albedo number or material name like 'concrete'. +demo.setGround() # This prints available materials. + + +# If a number between 0 and 1 is passed, it assumes it's an albedo value. For this example, we want a high-reflectivity rooftop albedo surface, so we will set the albedo to 0.62 + +# In[5]: + + +albedo = 0.62 +demo.setGround(albedo) + + +# + +# ## 4. Download and Load Weather Files +# +# There are various options provided in bifacial_radiance to load weatherfiles. getEPW is useful because you just set the latitude and longitude of the location and it donwloads the meteorologicla data for any location. + +# In[6]: + + +# Pull in meteorological data using pyEPW for any global lat/lon +epwfile = demo.getEPW(lat = 37.5, lon = -77.6) # This location corresponds to Richmond, VA. + + +# The downloaded EPW will be in the EPWs folder. +# +# To load the data, use readWeatherFile. This reads EPWs, TMY meterological data, or even your own data as long as it follows TMY data format (With any time resoultion). + +# In[7]: + + +# Read in the weather data pulled in above. +metdata = demo.readWeatherFile(epwfile) + + +# + +# ## 5. Generate the Sky. +# +# Sky definitions can either be for a single time point with gendaylit function, +# or using gencumulativesky to generate a cumulativesky for the entire year. +# + +# In[8]: + + +fullYear = True +if fullYear: + demo.genCumSky(demo.epwfile) # entire year. +else: + demo.gendaylit(metdata,4020) # Noon, June 17th (timepoint # 4020) + + +# The method gencumSky calculates the hourly radiance of the sky hemisphere by dividing it into 145 patches. Then it adds those hourly values to generate one single cumulative sky. Here is a visualization of this patched hemisphere for Richmond, VA, US. Can you deduce from the radiance values of each patch which way is North? +# +# ![Example of the hemisphere cumulative sky](../images_wiki/Journal1Pics/cumulativesky.png) +# +# Answer: Since Richmond is in the Northern Hemisphere, the modules face the south, which is where most of the radiation from the sun is coming. The north in this picture is the darker blue areas. + +# + +# ## 6. DEFINE a Module type +# +# You can create a custom PV module type. In this case we are defining a module named "Prism Solar Bi60", in landscape. The x value defines the size of the module along the row, so for landscape modules x > y. This module measures y = 0.984 x = 1.695. +# +# +#
+# Modules in this example are 100% opaque. For drawing each cell, makeModule needs more inputs with cellLevelModule = True. You can also specify a lot more variables in makeModule like multiple modules, torque tubes, spacing between modules, etc. Reffer to the Module Documentation and read the following jupyter journals to explore all your options. +#
+# + +# In[9]: + + +module_type = 'Prism Solar Bi60 landscape' +demo.makeModule(name=module_type,x=1.695, y=0.984) + + +# In case you want to use a pre-defined module or a module you've created previously, they are stored in a JSON format in data/module.json, and the options available can be called with printModules: + +# In[10]: + + +availableModules = demo.printModules() + + +# + +# ## 7. Make the Scene +# +# The sceneDicitonary specifies the information of the scene, such as number of rows, number of modules per row, azimuth, tilt, clearance_height (distance between the ground and lowest point of the module) and any other parameter. +# +# Azimuth gets measured from N = 0, so for South facing modules azimuth should equal 180 degrees +# + +# In[11]: + + +sceneDict = {'tilt':10,'pitch':3,'clearance_height':0.2,'azimuth':180, 'nMods': 20, 'nRows': 7} + + +# To make the scene we have to create a Scene Object through the method makeScene. This method will create a .rad file in the objects folder, with the parameters specified in sceneDict and the module created above. + +# In[12]: + + +scene = demo.makeScene(module_type,sceneDict) + + +# + +# ## 8. COMBINE the Ground, Sky, and the Scene Objects +# +# Radiance requires an "Oct" file that combines the ground, sky and the scene object into it. +# The method makeOct does this for us. + +# In[13]: + + +octfile = demo.makeOct(demo.getfilelist()) + + +# To see what files got merged into the octfile, you can use the helper method getfilelist. This is useful for advanced simulations too, specially when you want to have different Scene objects in the same simulation, or if you want to add other custom elements to your scene (like a building, for example) + +# In[14]: + + +demo.getfilelist() + + +# + +# ## 9. ANALYZE and get Results +# +# Once the octfile tying the scene, ground and sky has been created, we create an Analysis Object. We first have to create an Analysis object, and then we have to specify where the sensors will be located with moduleAnalysis. +# + +# First let's create the Analysis Object + +# In[15]: + + +analysis = AnalysisObj(octfile, demo.basename) + + +# Then let's specify the sensor location. If no parameters are passed to moduleAnalysis, it will scan the center module of the center row: + +# In[16]: + + + +frontscan, backscan = analysis.moduleAnalysis(scene) + + +# The frontscan and backscan include a linescan along a chord of the module, both on the front and back. +# +# ![Simple example for south facing module](../images_wiki/Journal1Pics/frontscan_backscan.png) +# Analysis saves the measured irradiances in the front and in the back on the results folder. Prints out the ratio of the average of the rear and front irradiance values along a chord of the module. + +# In[21]: + + +results = analysis.analysis(octfile, demo.basename, frontscan, backscan) + + +# The results are also automatically saved in the results folder. Some of our input/output functions can be used to read the results and work with them, for example: + +# In[24]: + + +load.read1Result('results\irr_bifacial_example.csv') + + +# As can be seen in the results for the *Wm2Front* and *WM2Back*, the irradiance values are quite high. This is because a cumulative sky simulation was performed on step 5 , so this is the total irradiance over all the hours of the year that the module at each sampling point will receive. Dividing the back irradiance average by the front irradiance average will give us the bifacial gain for the year: +# +# ![Bifacial Gain in Irradiance Formula](../images_wiki/Journal1Pics/BGG_Formula.png) +# +# Assuming that our module from Prism Solar has a bifaciality factor (rear to front performance) of 90%, our bifacial gain is of: + +# In[19]: + + +bifacialityfactor = 0.9 +print('Annual bifacial ratio: %0.2f ' %( np.mean(analysis.Wm2Back) * bifacialityfactor / np.mean(analysis.Wm2Front)) ) + + +# + +# ## 10. View / Render the Scene +# +# If you used gencumsky or gendaylit, you can view the Scene by navigating on a command line to the folder and typing: +# +# ##### objview materials\ground.rad objects\Prism_Solar_Bi60_landscape_0.2_3_10_20x7_origin0,0.rad +# +# This objview has 3 different light sources of its own, so the shading is not representative. +# +# ONLY If you used gendaylit , you can view the scene correctly illuminated with the sky you generated after generating the oct file, with +# +# ##### rvu -vf views\front.vp -e .01 bifacial_example.oct +# +# The rvu manual can be found here: manual page here: http://radsite.lbl.gov/radiance/rvu.1.html +# +# Or you can also use the code below from bifacial_radiance to generate an HDR rendered image of the scene. You can choose from front view or side view in the views folder: + +# In[20]: + + +# Make a color render and falsecolor image of the scene. +analysis.makeImage('side.vp') +analysis.makeFalseColor('side.vp') + + +# This is how the False Color image stored in images folder should look like: +# +# ![OpenHDR image example of False color](../images_wiki/Journal1Pics/openhdr_FalseColorExample.png) + +# Files are saved as .hdr (high definition render) files. Try LuminanceHDR viewer (free) to view them, or https://viewer.openhdr.org/ +# +# diff --git a/docs/tutorials/2 - Introductory Example - Single Axis Tracking with cumulative Sky.ipynb b/docs/tutorials/2 - Introductory Example - Single Axis Tracking with cumulative Sky.ipynb new file mode 100644 index 00000000..5fa52935 --- /dev/null +++ b/docs/tutorials/2 - Introductory Example - Single Axis Tracking with cumulative Sky.ipynb @@ -0,0 +1,2645 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 2 - Introductory Example - 1-Axis tracker with cumulative sky\n", + "\n", + "Method Gencumsky has been modified to divide the yearly-cumulative sky into various skies, each one representing the cumulative irradiance for the hours at which the tracker is at a certain angle. For faster running, for a tracker that moves between 45 and -45 degrees limit angle, if only positions every 5 degrees are considered (45, 40, 35 .... -4-, -45), then only 18 skies (and 18 simulations) will be run for the whole year.\n", + "\n", + "![Example of the hemisphere cumulative sky](../images_wiki/Journal2Pics/tracking_cumulativesky.png)\n", + "\n", + "\n", + "This procedure was presented in:\n", + "\n", + "Ayala Pelaez S, Deline C, Greenberg P, Stein JS, Kostuk RK. Model and validation of single-axis tracking with bifacial PV. IEEE J Photovoltaics. 2019;9(3):715–21. https://ieeexplore.ieee.org/document/8644027 and https://www.nrel.gov/docs/fy19osti/72039.pdf (pre-print, conference version)\n", + "\n", + "\n", + "### Steps:\n", + "
    \n", + "
  1. Create a folder for your simulation, and load bifacial_radiance
  2. \n", + "
  3. Create a Radiance Object
  4. \n", + "
  5. Set the Albedo
  6. \n", + "
  7. Download Weather Files
  8. \n", + "
      (VERY SIMILAR TO FIXED TILT EXAMPLE UNTIL HERE)
    \n", + "
  9. Set Tracking Angles
  10. \n", + "
  11. Generate the Sky
  12. \n", + "
  13. Define a Module type
  14. \n", + "
  15. Create the scene
  16. \n", + "
  17. Combine Ground, Sky and Scene Objects
  18. \n", + "
  19. Analyze and get results
  20. \n", + "
  21. Clean Results
  22. \n", + " \n", + "
\n", + "\n", + "And finally: " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "## 1. Create a folder for your simulation, and load bifacial_radiance \n", + "\n", + "First let's set the folder where the simulation will be saved. By default, this is the TEMP folder in the bifacial_radiance distribution.\n", + "\n", + "The lines below find the location of the folder relative to this Jupyter Journa. You can alternatively point to an empty directory (it will open a load GUI Visual Interface) or specify any other directory in your computer, for example:\n", + "\n", + "#### testfolder = r'C:\\Users\\sayala\\Documents\\RadianceScenes\\Tutorials\\Journal2'\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Your simulation will be stored in C:\\Users\\sayala\\Documents\\GitHub\\bifacial_radiance\\bifacial_radiance\\TEMP\n" + ] + } + ], + "source": [ + "import os\n", + "testfolder = os.path.abspath(r'..\\..\\bifacial_radiance\\TEMP') \n", + "\n", + "print (\"Your simulation will be stored in %s\" % testfolder)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This will load bifacial_radiance and other libraries from python that will be useful for this Jupyter Journal:" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "from bifacial_radiance import *\n", + "import numpy as np" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 2. Create a Radiance Object" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "path = C:\\Users\\sayala\\Documents\\GitHub\\bifacial_radiance\\bifacial_radiance\\TEMP\n" + ] + } + ], + "source": [ + "# Create a RadianceObj 'object' named bifacial_example. no whitespace allowed\n", + "demo = RadianceObj('bifacial_tracking_example', path = testfolder) " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This will create all the folder structure of the bifacial_radiance Scene in the designated testfolder in your computer, and it should look like this:\n", + "\n", + "\n", + "![Folder Structure](../images_wiki/Journal1Pics/folderStructure.png)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 3. Set the Albedo" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To see more options of ground materials available (located on ground.rad), run this function without any input. " + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Input albedo 0-1, or ground material names:['black', 'litesoil', 'concrete', 'white_EPDM', 'beigeroof', 'beigeroof_lite', 'beigeroof_heavy', 'asphalt', 'greyroof', 'Metal_Aluminum_Anodized', 'Color_I11', 'M_0010_Snow', 'Metal_Grey']\n" + ] + } + ], + "source": [ + "# Input albedo number or material name like 'concrete'. \n", + "demo.setGround() # This prints available materials." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "If a number between 0 and 1 is passed, it assumes it's an albedo value. For this example, we want a natural-ground albedo value, so we'll use 0.25" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "albedo = 0.25\n", + "demo.setGround(albedo)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 4. Download and Load Weather Files\n", + "\n", + "There are various options provided in bifacial_radiance to load weatherfiles. getEPW is useful because you just set the latitude and longitude of the location and it donwloads the meteorologicla data for any location. " + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "scrolled": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Getting weather file: USA_VA_Richmond.Intl.AP.724010_TMY.epw\n", + " ... OK!\n" + ] + } + ], + "source": [ + "# Pull in meteorological data using pyEPW for any global lat/lon\n", + "epwfile = demo.getEPW(lat = 37.5, lon = -77.6) # This location corresponds to Richmond, VA." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The downloaded EPW will be in the EPWs folder.\n", + "\n", + "To load the data, use readWeatherFile. This reads EPWs, TMY meterological data, or even your own data as long as it follows TMY data format (With any time resoultion)." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "# Read in the weather data pulled in above. \n", + "metdata = demo.readWeatherFile(weatherFile = epwfile) " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## TRACKING Workflow" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Until now, all the steps looked the same from Tutorial 1. The following section follows similar steps, but the functions are specific for working with single axis tracking.\n", + "\n", + "## 5. Set Tracking Angles\n", + "\n", + "This function will read the weather file, and based on the sun position it will calculate the angle the tracker should be at for each hour. It will create metdata files for each of the tracker angles considered." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "C:\\ProgramData\\Anaconda3\\lib\\site-packages\\pvlib\\tracking.py:449: RuntimeWarning: invalid value encountered in arccos\n", + " wc = np.degrees(np.arccos(temp))\n", + "C:\\ProgramData\\Anaconda3\\lib\\site-packages\\pvlib\\tracking.py:473: RuntimeWarning: invalid value encountered in arccos\n", + " aoi = np.degrees(np.arccos(np.abs(np.sum(sun_vec*panel_norm, axis=0))))\n", + "C:\\ProgramData\\Anaconda3\\lib\\site-packages\\pvlib\\tracking.py:568: RuntimeWarning: invalid value encountered in remainder\n", + " surface_azimuth = surface_azimuth % 360\n", + "C:\\ProgramData\\Anaconda3\\lib\\site-packages\\pvlib\\tracking.py:572: RuntimeWarning: invalid value encountered in arccos\n", + " surface_tilt = 90 - np.degrees(np.arccos(dotproduct))\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Saving file EPWs\\1axis_-5.0.csv, # points: 2215\n", + "Saving file EPWs\\1axis_0.0.csv, # points: 81\n", + "Saving file EPWs\\1axis_5.0.csv, # points: 2087\n" + ] + } + ], + "source": [ + "limit_angle = 5 # tracker rotation limit angle. Setting it ridiculously small so this runs faster.\n", + "angledelta = 5 # sampling between the limit angles. \n", + "backtrack = True\n", + "gcr = 0.33\n", + "cumulativesky = True # This is important for this example!\n", + "trackerdict = demo.set1axis(metdata = metdata, limit_angle = limit_angle, backtrack = backtrack, \n", + " gcr = gcr, cumulativesky = cumulativesky)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Setting backtrack to True is important in this step, so the trackers correct for self-shading when following the sun at high zenith angles. " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 6. Generate the Sky\n", + "\n", + "This will create the skies for each sub-metdata file created by set1axis.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "message: There were 2110 sun up hours in this climate file\n", + "Total Ibh/Lbh: 0.000000\n", + "Created skyfile skies\\1axis_-5.0.rad\n", + "message: There were 13 sun up hours in this climate file\n", + "Total Ibh/Lbh: 0.000000\n", + "Created skyfile skies\\1axis_0.0.rad\n", + "message: There were 2067 sun up hours in this climate file\n", + "Total Ibh/Lbh: 0.000000\n", + "Created skyfile skies\\1axis_5.0.rad\n" + ] + } + ], + "source": [ + "trackerdict = demo.genCumSky1axis(trackerdict = trackerdict)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This is how one of the cumulative sky .cal files associated with each .rad file generated look like: \n", + "\n", + "![Example of the gencumsky1axis](../images_wiki/Journal2Pics/gencumsky1axis_example_file_structure_and_contents.png)\n", + "\n", + "\n", + "Each of the values corresponds to the cumulative rradiance of one of those patches, for when the tracker is at that specific angle through the year." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 7. Define the Module type\n", + "\n", + "Let's make a more interesting module in this example. Let's do 2-up configuration in portrait, with the modules rotating around a 10 centimeter round torque tube. Let's add a gap between the two modules in 2-UP of 10 centimeters, as well as gap between the torque tube and the modules of 5 centimeters. Along the row, the modules are separated only 2 centimeters for this example. The torquetube is painted Metal_Grey in this example (it's one of the materials available in Ground.rad, and it is 40% reflective).\n" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Module Name: Custom_Tracker_Module\n", + "REWRITING pre-existing module file. \n", + "Module Custom Tracker Module successfully created\n" + ] + }, + { + "data": { + "text/plain": [ + "{'x': 0.984,\n", + " 'y': 1.7,\n", + " 'scenex': 1.004,\n", + " 'sceney': 3.5,\n", + " 'scenez': 0.1,\n", + " 'numpanels': 2,\n", + " 'bifi': 1,\n", + " 'text': '! genbox black Custom_Tracker_Module 0.984 1.7 0.02 | xform -t -0.492 -1.75 0.1 -a 2 -t 0 1.8 0\\r\\n! genrev Metal_Grey tube1 t*1.004 0.05 32 | xform -ry 90 -t -0.502 0 0',\n", + " 'modulefile': 'objects\\\\Custom_Tracker_Module.rad',\n", + " 'offsetfromaxis': 0.1,\n", + " 'xgap': 0.02,\n", + " 'ygap': 0.1,\n", + " 'zgap': 0.05,\n", + " 'cellModule': None,\n", + " 'torquetube': {'bool': True,\n", + " 'diameter': 0.1,\n", + " 'tubetype': 'round',\n", + " 'material': 'Metal_Grey'}}" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "x = 0.984 # meters\n", + "y = 1.7 # meters\n", + "moduletype = 'Custom Tracker Module'\n", + "torquetube = True\n", + "tubetype = 'round'\n", + "diameter = 0.1 # diameter of the torque tube\n", + "numpanels = 2\n", + "axisofrotationTorqueTube = True\n", + "zgap = 0.05\n", + "ygap = 0.10\n", + "xgap = 0.02\n", + "material = 'Metal_Grey'\n", + "\n", + "demo.makeModule(name = moduletype, x = x, y = y, \n", + " torquetube = torquetube, tubetype = tubetype, material = material,\n", + " diameter = diameter, xgap = xgap, ygap = ygap, zgap = zgap, \n", + " numpanels = numpanels, axisofrotationTorqueTube = axisofrotationTorqueTube)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 8. Make the Scene\n", + "\n", + "The scene Dictionary specifies the information of the scene. For tracking, different input parameters are expected in the dictionary, such as number of rows, number of modules per row, row azimuth, hub_height (distance between the axis of rotation of the modules and the ground). " + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "hub_height = 2.3\n", + "sceneDict = {'gcr': gcr,'hub_height':hub_height, 'nMods': 20, 'nRows': 7} \n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To make the scene we have to create a Scene Object through the method makeScene1axis. This method will create a .rad file in the objects folder, with the parameters specified in sceneDict and the module created above." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Making .rad files for cumulativesky 1-axis workflow\n", + "3 Radfiles created in /objects/\n" + ] + } + ], + "source": [ + "trackerdict = demo.makeScene1axis(trackerdict = trackerdict, moduletype = moduletype, sceneDict = sceneDict) " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 9. Combine Ground, Sky and Scene Objects\n", + "\n", + "makeOct1axis joins the sky.rad file, ground.rad file, and the geometry.rad files created in makeScene." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Making 3 octfiles in root directory.\n", + "Created 1axis_-5.0.oct\n", + "Created 1axis_0.0.oct\n", + "Created 1axis_5.0.oct\n" + ] + } + ], + "source": [ + "trackerdict = demo.makeOct1axis(trackerdict = trackerdict)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 10. Analyze and get results\n", + "\n", + "We can choose to analyze any module in the Scene we have created. The default, if no modWanted or rowWanted is passed, is to sample the center module of the center row. \n", + "\n", + "For this example we will sample row 2, module 9." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Linescan in process: 1axis_-5.0_Row_2_Module_09_Front\n", + "Linescan in process: 1axis_-5.0_Row_2_Module_09_Back\n", + "Saved: results\\irr_1axis_-5.0_Row_2_Module_09.csv\n", + "Index: -5.0. Wm2Front: 737969.9259259258. Wm2Back: 103511.26148148149\n", + "Linescan in process: 1axis_0.0_Row_2_Module_09_Front\n", + "Linescan in process: 1axis_0.0_Row_2_Module_09_Back\n", + "Saved: results\\irr_1axis_0.0_Row_2_Module_09.csv\n", + "Index: 0.0. Wm2Front: 4992.931111111112. Wm2Back: 609.6241185185185\n", + "Linescan in process: 1axis_5.0_Row_2_Module_09_Front\n", + "Linescan in process: 1axis_5.0_Row_2_Module_09_Back\n", + "Saved: results\\irr_1axis_5.0_Row_2_Module_09.csv\n", + "Index: 5.0. Wm2Front: 760179.5777777777. Wm2Back: 103316.52851851853\n", + "\n", + "Saving Cumulative results\n", + "Saved: cumulative_results__Row_2_Module_09.csv\n" + ] + } + ], + "source": [ + "modWanted = 9\n", + "rowWanted = 2\n", + "customname = '_Row_2_Module_09' # This is useful if we want to do various analysis.\n", + "trackerdict = demo.analysis1axis(trackerdict, modWanted=9, rowWanted = 2, customname=customname)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's look at the results with more detail. The analysis1axis routine created individual result .csv files for each angle, as well as one cumulative result .csv where the irradiance is added by sensor.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
xyzrearZmattyperearMatWm2FrontWm2BackBack/FrontRatio
022.61212-1.0042.462.37a8.1.a0.Custom_Tracker_Module.6457a8.1.a0.Custom_Tracker_Module.23101.500079e+06217173.5843330.144775
122.26212-1.0042.462.37a8.1.a0.Custom_Tracker_Module.6457a8.1.a0.Custom_Tracker_Module.23101.500218e+06210772.9703670.140495
221.91212-1.0042.462.37a8.1.a0.Custom_Tracker_Module.6457a8.1.a0.Custom_Tracker_Module.23101.500313e+06206189.9517000.137431
321.56212-1.0042.462.37a8.1.a0.Custom_Tracker_Module.6457a8.1.a0.Custom_Tracker_Module.23101.500407e+06198467.7356000.132276
421.21212-1.0042.462.37a8.1.tube1.16sky1.568887e+06186642.7283000.118965
520.86212-1.0042.462.37a8.1.a1.Custom_Tracker_Module.6457a8.1.a1.Custom_Tracker_Module.23101.499679e+06201038.6267670.134054
620.51212-1.0042.462.37a8.1.a1.Custom_Tracker_Module.6457a8.1.a1.Custom_Tracker_Module.23101.496805e+06209103.1823000.139700
720.16212-1.0042.462.37a8.1.a1.Custom_Tracker_Module.6457a8.1.a1.Custom_Tracker_Module.23101.496917e+06215010.6658000.143636
819.81212-1.0042.462.37a8.1.a1.Custom_Tracker_Module.6457a8.1.a1.Custom_Tracker_Module.23101.497009e+06222066.0502330.148340
\n", + "
" + ], + "text/plain": [ + " x y z rearZ mattype \\\n", + "0 22.61212 -1.004 2.46 2.37 a8.1.a0.Custom_Tracker_Module.6457 \n", + "1 22.26212 -1.004 2.46 2.37 a8.1.a0.Custom_Tracker_Module.6457 \n", + "2 21.91212 -1.004 2.46 2.37 a8.1.a0.Custom_Tracker_Module.6457 \n", + "3 21.56212 -1.004 2.46 2.37 a8.1.a0.Custom_Tracker_Module.6457 \n", + "4 21.21212 -1.004 2.46 2.37 a8.1.tube1.16 \n", + "5 20.86212 -1.004 2.46 2.37 a8.1.a1.Custom_Tracker_Module.6457 \n", + "6 20.51212 -1.004 2.46 2.37 a8.1.a1.Custom_Tracker_Module.6457 \n", + "7 20.16212 -1.004 2.46 2.37 a8.1.a1.Custom_Tracker_Module.6457 \n", + "8 19.81212 -1.004 2.46 2.37 a8.1.a1.Custom_Tracker_Module.6457 \n", + "\n", + " rearMat Wm2Front Wm2Back \\\n", + "0 a8.1.a0.Custom_Tracker_Module.2310 1.500079e+06 217173.584333 \n", + "1 a8.1.a0.Custom_Tracker_Module.2310 1.500218e+06 210772.970367 \n", + "2 a8.1.a0.Custom_Tracker_Module.2310 1.500313e+06 206189.951700 \n", + "3 a8.1.a0.Custom_Tracker_Module.2310 1.500407e+06 198467.735600 \n", + "4 sky 1.568887e+06 186642.728300 \n", + "5 a8.1.a1.Custom_Tracker_Module.2310 1.499679e+06 201038.626767 \n", + "6 a8.1.a1.Custom_Tracker_Module.2310 1.496805e+06 209103.182300 \n", + "7 a8.1.a1.Custom_Tracker_Module.2310 1.496917e+06 215010.665800 \n", + "8 a8.1.a1.Custom_Tracker_Module.2310 1.497009e+06 222066.050233 \n", + "\n", + " Back/FrontRatio \n", + "0 0.144775 \n", + "1 0.140495 \n", + "2 0.137431 \n", + "3 0.132276 \n", + "4 0.118965 \n", + "5 0.134054 \n", + "6 0.139700 \n", + "7 0.143636 \n", + "8 0.148340 " + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "results = load.read1Result('cumulative_results__Row_2_Module_09.csv')\n", + "results" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "There are various things to notice:\n", + "\n", + "I. The materials column has a specific format that will tell you if you are sampling the correct module:\n", + "\n", + " a{ModWanted}.{rowWanted}.a{numPanel}.{moduletype}.material_key\n", + "\n", + "* Since for this journal numPanels = 2, numPanel can either be 0 or 1, for the East-most and West-most module in the collector width.\n", + "* numPanel, ModWanted and RowWanted are indexed starting at 0 in the results.\n", + "* material_key is from the surface generated inside radiance. Usually it is 6457 for top surface of hte module and .2310 for the bottom one. \n", + "\n", + "II. Sensors sample always in the same direction. For this N-S aligned tracker, that is East-most to West. For this 2-up portrait tracker which is 3.5 meters, 20x7 rows and we are sampling module 9 on row 2, the East to West sampling goes from 22.6 m to 19.81 m = 2.79m. It is not exatly 3.5 because the sensors are spaced evenly through the collector width (CW): \n", + "\n", + "\n", + "![Sensors spaced along collector width](../images_wiki/Journal2Pics/spaced_sensors.png)\n", + "\n", + "III. When there is a ygap in the collector width (2-UP or more configuration), some of the sensors might end up sampling the torque tube, or the sky. You can ses that in the materials columns. This also happens if the number of sensors is quite high, the edges of the module might be sampled instead of the sensors. For this reason, before calculating bifacial gain these results must be cleaned. For more advanced simulations, make sure you clean each result csv file individually. We provide some options on load.py but some are very use-specific, so you might have to develop your own cleaning tool (or let us know on issues!)\n", + "\n", + "
\n", + "Important: If you have torquetubes and y-gap values, make sure you clean your results.\n", + "
\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 11. Clean Results\n", + "\n", + "We have two options for cleaning results. The simples on is load.cleanResults, but there is also a deepClean for specific purposes.\n", + "\n", + "cleanResults will find materials that should not have values and set them to NaN." + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "C:\\ProgramData\\Anaconda3\\lib\\site-packages\\pandas\\core\\indexing.py:190: SettingWithCopyWarning: \n", + "A value is trying to be set on a copy of a slice from a DataFrame\n", + "\n", + "See the caveats in the documentation: http://pandas.pydata.org/pandas-docs/stable/indexing.html#indexing-view-versus-copy\n", + " self._setitem_with_indexer(indexer, value)\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
xyzrearZmattyperearMatWm2FrontWm2BackBack/FrontRatio
022.61212-1.0042.462.37a8.1.a0.Custom_Tracker_Module.6457a8.1.a0.Custom_Tracker_Module.23101.500079e+06217173.5843330.144775
122.26212-1.0042.462.37a8.1.a0.Custom_Tracker_Module.6457a8.1.a0.Custom_Tracker_Module.23101.500218e+06210772.9703670.140495
221.91212-1.0042.462.37a8.1.a0.Custom_Tracker_Module.6457a8.1.a0.Custom_Tracker_Module.23101.500313e+06206189.9517000.137431
321.56212-1.0042.462.37a8.1.a0.Custom_Tracker_Module.6457a8.1.a0.Custom_Tracker_Module.23101.500407e+06198467.7356000.132276
421.21212-1.0042.462.37a8.1.tube1.16skyNaNNaN0.118965
520.86212-1.0042.462.37a8.1.a1.Custom_Tracker_Module.6457a8.1.a1.Custom_Tracker_Module.23101.499679e+06201038.6267670.134054
620.51212-1.0042.462.37a8.1.a1.Custom_Tracker_Module.6457a8.1.a1.Custom_Tracker_Module.23101.496805e+06209103.1823000.139700
720.16212-1.0042.462.37a8.1.a1.Custom_Tracker_Module.6457a8.1.a1.Custom_Tracker_Module.23101.496917e+06215010.6658000.143636
819.81212-1.0042.462.37a8.1.a1.Custom_Tracker_Module.6457a8.1.a1.Custom_Tracker_Module.23101.497009e+06222066.0502330.148340
\n", + "
" + ], + "text/plain": [ + " x y z rearZ mattype \\\n", + "0 22.61212 -1.004 2.46 2.37 a8.1.a0.Custom_Tracker_Module.6457 \n", + "1 22.26212 -1.004 2.46 2.37 a8.1.a0.Custom_Tracker_Module.6457 \n", + "2 21.91212 -1.004 2.46 2.37 a8.1.a0.Custom_Tracker_Module.6457 \n", + "3 21.56212 -1.004 2.46 2.37 a8.1.a0.Custom_Tracker_Module.6457 \n", + "4 21.21212 -1.004 2.46 2.37 a8.1.tube1.16 \n", + "5 20.86212 -1.004 2.46 2.37 a8.1.a1.Custom_Tracker_Module.6457 \n", + "6 20.51212 -1.004 2.46 2.37 a8.1.a1.Custom_Tracker_Module.6457 \n", + "7 20.16212 -1.004 2.46 2.37 a8.1.a1.Custom_Tracker_Module.6457 \n", + "8 19.81212 -1.004 2.46 2.37 a8.1.a1.Custom_Tracker_Module.6457 \n", + "\n", + " rearMat Wm2Front Wm2Back \\\n", + "0 a8.1.a0.Custom_Tracker_Module.2310 1.500079e+06 217173.584333 \n", + "1 a8.1.a0.Custom_Tracker_Module.2310 1.500218e+06 210772.970367 \n", + "2 a8.1.a0.Custom_Tracker_Module.2310 1.500313e+06 206189.951700 \n", + "3 a8.1.a0.Custom_Tracker_Module.2310 1.500407e+06 198467.735600 \n", + "4 sky NaN NaN \n", + "5 a8.1.a1.Custom_Tracker_Module.2310 1.499679e+06 201038.626767 \n", + "6 a8.1.a1.Custom_Tracker_Module.2310 1.496805e+06 209103.182300 \n", + "7 a8.1.a1.Custom_Tracker_Module.2310 1.496917e+06 215010.665800 \n", + "8 a8.1.a1.Custom_Tracker_Module.2310 1.497009e+06 222066.050233 \n", + "\n", + " Back/FrontRatio \n", + "0 0.144775 \n", + "1 0.140495 \n", + "2 0.137431 \n", + "3 0.132276 \n", + "4 0.118965 \n", + "5 0.134054 \n", + "6 0.139700 \n", + "7 0.143636 \n", + "8 0.148340 " + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "results_clean = load.cleanResult(results)\n", + "results_clean\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "These are the total irradiance values over all the hours of the year that the module at each sampling point will receive. Dividing the back irradiance average by the front irradiance average will give us the bifacial gain for the year:\n", + "\n", + "![Bifacial Gain in Irradiance Formula](../images_wiki/Journal1Pics/BGG_Formula.png)\n", + "\n", + "Assuming that our module from Prism Solar has a bifaciality factor (rear to front performance) of 90%, our bifacial gain is of:" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Annual bifacial ratio: 0.126 \n" + ] + } + ], + "source": [ + "bifacialityfactor = 0.9\n", + "print('Annual bifacial ratio: %0.3f ' %( np.nanmean(results_clean.Wm2Back) * bifacialityfactor / np.nanmean(results_clean.Wm2Front)) )" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## CONDENSED VERSION\n", + "Everything we've done so far in super short condensed version:" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "path = C:\\Users\\sayala\\Documents\\RadianceScenes\\Tutorials\\Journal2\n", + "Getting weather file: USA_VA_Richmond.Intl.AP.724010_TMY.epw\n", + " ... OK!\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "C:\\ProgramData\\Anaconda3\\lib\\site-packages\\pvlib\\tracking.py:449: RuntimeWarning: invalid value encountered in arccos\n", + " wc = np.degrees(np.arccos(temp))\n", + "C:\\ProgramData\\Anaconda3\\lib\\site-packages\\pvlib\\tracking.py:473: RuntimeWarning: invalid value encountered in arccos\n", + " aoi = np.degrees(np.arccos(np.abs(np.sum(sun_vec*panel_norm, axis=0))))\n", + "C:\\ProgramData\\Anaconda3\\lib\\site-packages\\pvlib\\tracking.py:568: RuntimeWarning: invalid value encountered in remainder\n", + " surface_azimuth = surface_azimuth % 360\n", + "C:\\ProgramData\\Anaconda3\\lib\\site-packages\\pvlib\\tracking.py:572: RuntimeWarning: invalid value encountered in arccos\n", + " surface_tilt = 90 - np.degrees(np.arccos(dotproduct))\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Saving file EPWs\\1axis_-60.0.csv, # points: 364\n", + "Saving file EPWs\\1axis_-55.0.csv, # points: 136\n", + "Saving file EPWs\\1axis_-50.0.csv, # points: 169\n", + "Saving file EPWs\\1axis_-45.0.csv, # points: 146\n", + "Saving file EPWs\\1axis_-40.0.csv, # points: 173\n", + "Saving file EPWs\\1axis_-35.0.csv, # points: 146\n", + "Saving file EPWs\\1axis_-30.0.csv, # points: 157\n", + "Saving file EPWs\\1axis_-25.0.csv, # points: 175\n", + "Saving file EPWs\\1axis_-20.0.csv, # points: 110\n", + "Saving file EPWs\\1axis_-15.0.csv, # points: 136\n", + "Saving file EPWs\\1axis_-10.0.csv, # points: 362\n", + "Saving file EPWs\\1axis_-5.0.csv, # points: 141\n", + "Saving file EPWs\\1axis_0.0.csv, # points: 81\n", + "Saving file EPWs\\1axis_5.0.csv, # points: 244\n", + "Saving file EPWs\\1axis_10.0.csv, # points: 81\n", + "Saving file EPWs\\1axis_15.0.csv, # points: 123\n", + "Saving file EPWs\\1axis_20.0.csv, # points: 225\n", + "Saving file EPWs\\1axis_25.0.csv, # points: 154\n", + "Saving file EPWs\\1axis_30.0.csv, # points: 116\n", + "Saving file EPWs\\1axis_35.0.csv, # points: 167\n", + "Saving file EPWs\\1axis_40.0.csv, # points: 135\n", + "Saving file EPWs\\1axis_45.0.csv, # points: 168\n", + "Saving file EPWs\\1axis_50.0.csv, # points: 126\n", + "Saving file EPWs\\1axis_55.0.csv, # points: 173\n", + "Saving file EPWs\\1axis_60.0.csv, # points: 375\n", + "message: There were 362 sun up hours in this climate file\n", + "Total Ibh/Lbh: 0.000000\n", + "Created skyfile skies\\1axis_-60.0.rad\n", + "message: There were 135 sun up hours in this climate file\n", + "Total Ibh/Lbh: 0.000000\n", + "Created skyfile skies\\1axis_-55.0.rad\n", + "message: There were 169 sun up hours in this climate file\n", + "Total Ibh/Lbh: 0.000000\n", + "Created skyfile skies\\1axis_-50.0.rad\n", + "message: There were 146 sun up hours in this climate file\n", + "Total Ibh/Lbh: 0.000000\n", + "Created skyfile skies\\1axis_-45.0.rad\n", + "message: There were 172 sun up hours in this climate file\n", + "Total Ibh/Lbh: 0.000000\n", + "Created skyfile skies\\1axis_-40.0.rad\n", + "message: There were 145 sun up hours in this climate file\n", + "Total Ibh/Lbh: 0.000000\n", + "Created skyfile skies\\1axis_-35.0.rad\n", + "message: There were 157 sun up hours in this climate file\n", + "Total Ibh/Lbh: 0.000000\n", + "Created skyfile skies\\1axis_-30.0.rad\n", + "message: There were 172 sun up hours in this climate file\n", + "Total Ibh/Lbh: 0.000000\n", + "Created skyfile skies\\1axis_-25.0.rad\n", + "message: There were 108 sun up hours in this climate file\n", + "Total Ibh/Lbh: 0.000000\n", + "Created skyfile skies\\1axis_-20.0.rad\n", + "message: There were 133 sun up hours in this climate file\n", + "Total Ibh/Lbh: 0.000000\n", + "Created skyfile skies\\1axis_-15.0.rad\n", + "message: There were 341 sun up hours in this climate file\n", + "Total Ibh/Lbh: 0.000000\n", + "Created skyfile skies\\1axis_-10.0.rad\n", + "message: There were 70 sun up hours in this climate file\n", + "Total Ibh/Lbh: 0.000000\n", + "Created skyfile skies\\1axis_-5.0.rad\n", + "message: There were 13 sun up hours in this climate file\n", + "Total Ibh/Lbh: 0.000000\n", + "Created skyfile skies\\1axis_0.0.rad\n", + "message: There were 243 sun up hours in this climate file\n", + "Total Ibh/Lbh: 0.000000\n", + "Created skyfile skies\\1axis_5.0.rad\n", + "message: There were 76 sun up hours in this climate file\n", + "Total Ibh/Lbh: 0.000000\n", + "Created skyfile skies\\1axis_10.0.rad\n", + "message: There were 112 sun up hours in this climate file\n", + "Total Ibh/Lbh: 0.000000\n", + "Created skyfile skies\\1axis_15.0.rad\n", + "message: There were 224 sun up hours in this climate file\n", + "Total Ibh/Lbh: 0.000000\n", + "Created skyfile skies\\1axis_20.0.rad\n", + "message: There were 152 sun up hours in this climate file\n", + "Total Ibh/Lbh: 0.000000\n", + "Created skyfile skies\\1axis_25.0.rad\n", + "message: There were 116 sun up hours in this climate file\n", + "Total Ibh/Lbh: 0.000000\n", + "Created skyfile skies\\1axis_30.0.rad\n", + "message: There were 167 sun up hours in this climate file\n", + "Total Ibh/Lbh: 0.000000\n", + "Created skyfile skies\\1axis_35.0.rad\n", + "message: There were 135 sun up hours in this climate file\n", + "Total Ibh/Lbh: 0.000000\n", + "Created skyfile skies\\1axis_40.0.rad\n", + "message: There were 168 sun up hours in this climate file\n", + "Total Ibh/Lbh: 0.000000\n", + "Created skyfile skies\\1axis_45.0.rad\n", + "message: There were 126 sun up hours in this climate file\n", + "Total Ibh/Lbh: 0.000000\n", + "Created skyfile skies\\1axis_50.0.rad\n", + "message: There were 173 sun up hours in this climate file\n", + "Total Ibh/Lbh: 0.000000\n", + "Created skyfile skies\\1axis_55.0.rad\n", + "message: There were 375 sun up hours in this climate file\n", + "Total Ibh/Lbh: 0.000000\n", + "Created skyfile skies\\1axis_60.0.rad\n", + "sceneDict Warning: 'height' is being deprecated. Renaming as 'hub_height'\n", + "\n", + "Making .rad files for cumulativesky 1-axis workflow\n", + "25 Radfiles created in /objects/\n", + "\n", + "Making 25 octfiles in root directory.\n", + "Created 1axis_-60.0.oct\n", + "Created 1axis_-55.0.oct\n", + "Created 1axis_-50.0.oct\n", + "Created 1axis_-45.0.oct\n", + "Created 1axis_-40.0.oct\n", + "Created 1axis_-35.0.oct\n", + "Created 1axis_-30.0.oct\n", + "Created 1axis_-25.0.oct\n", + "Created 1axis_-20.0.oct\n", + "Created 1axis_-15.0.oct\n", + "Created 1axis_-10.0.oct\n", + "Created 1axis_-5.0.oct\n", + "Created 1axis_0.0.oct\n", + "Created 1axis_5.0.oct\n", + "Created 1axis_10.0.oct\n", + "Created 1axis_15.0.oct\n", + "Created 1axis_20.0.oct\n", + "Created 1axis_25.0.oct\n", + "Created 1axis_30.0.oct\n", + "Created 1axis_35.0.oct\n", + "Created 1axis_40.0.oct\n", + "Created 1axis_45.0.oct\n", + "Created 1axis_50.0.oct\n", + "Created 1axis_55.0.oct\n", + "Created 1axis_60.0.oct\n", + "Linescan in process: 1axis_-60.0_Front\n", + "Linescan in process: 1axis_-60.0_Back\n", + "Saved: results\\irr_1axis_-60.0.csv\n", + "Index: -60.0. Wm2Front: 119801.51777777777. Wm2Back: 16911.519259259258\n", + "Linescan in process: 1axis_-55.0_Front\n", + "Linescan in process: 1axis_-55.0_Back\n", + "Saved: results\\irr_1axis_-55.0.csv\n", + "Index: -55.0. Wm2Front: 52923.84740740741. Wm2Back: 6788.013481481482\n", + "Linescan in process: 1axis_-50.0_Front\n", + "Linescan in process: 1axis_-50.0_Back\n", + "Saved: results\\irr_1axis_-50.0.csv\n", + "Index: -50.0. Wm2Front: 74074.72037037036. Wm2Back: 8816.697925925926\n", + "Linescan in process: 1axis_-45.0_Front\n", + "Linescan in process: 1axis_-45.0_Back\n", + "Saved: results\\irr_1axis_-45.0.csv\n", + "Index: -45.0. Wm2Front: 51696.021851851845. Wm2Back: 6670.724333333334\n", + "Linescan in process: 1axis_-40.0_Front\n", + "Linescan in process: 1axis_-40.0_Back\n", + "Saved: results\\irr_1axis_-40.0.csv\n", + "Index: -40.0. Wm2Front: 72015.69296296296. Wm2Back: 8992.954037037036\n", + "Linescan in process: 1axis_-35.0_Front\n", + "Linescan in process: 1axis_-35.0_Back\n", + "Saved: results\\irr_1axis_-35.0.csv\n", + "Index: -35.0. Wm2Front: 57198.43407407407. Wm2Back: 7106.927185185185\n", + "Linescan in process: 1axis_-30.0_Front\n", + "Linescan in process: 1axis_-30.0_Back\n", + "Saved: results\\irr_1axis_-30.0.csv\n", + "Index: -30.0. Wm2Front: 50919.00814814815. Wm2Back: 6820.922185185186\n", + "Linescan in process: 1axis_-25.0_Front\n", + "Linescan in process: 1axis_-25.0_Back\n", + "Saved: results\\irr_1axis_-25.0.csv\n", + "Index: -25.0. Wm2Front: 91470.30222222224. Wm2Back: 11276.371777777778\n", + "Linescan in process: 1axis_-20.0_Front\n", + "Linescan in process: 1axis_-20.0_Back\n", + "Saved: results\\irr_1axis_-20.0.csv\n", + "Index: -20.0. Wm2Front: 29929.65185185185. Wm2Back: 4032.4610370370374\n", + "Linescan in process: 1axis_-15.0_Front\n", + "Linescan in process: 1axis_-15.0_Back\n", + "Saved: results\\irr_1axis_-15.0.csv\n", + "Index: -15.0. Wm2Front: 27538.738148148146. Wm2Back: 3781.7024814814818\n", + "Linescan in process: 1axis_-10.0_Front\n", + "Linescan in process: 1axis_-10.0_Back\n", + "Saved: results\\irr_1axis_-10.0.csv\n", + "Index: -10.0. Wm2Front: 139558.37777777776. Wm2Back: 18405.70814814815\n", + "Linescan in process: 1axis_-5.0_Front\n", + "Linescan in process: 1axis_-5.0_Back\n", + "Saved: results\\irr_1axis_-5.0.csv\n", + "Index: -5.0. Wm2Front: 1537.8774814814815. Wm2Back: 255.83928148148146\n", + "Linescan in process: 1axis_0.0_Front\n", + "Linescan in process: 1axis_0.0_Back\n", + "Saved: results\\irr_1axis_0.0.csv\n", + "Index: 0.0. Wm2Front: 5029.540555555555. Wm2Back: 605.9333592592593\n", + "Linescan in process: 1axis_5.0_Front\n", + "Linescan in process: 1axis_5.0_Back\n", + "Saved: results\\irr_1axis_5.0.csv\n", + "Index: 5.0. Wm2Front: 138534.42222222223. Wm2Back: 18245.788888888885\n", + "Linescan in process: 1axis_10.0_Front\n", + "Linescan in process: 1axis_10.0_Back\n", + "Saved: results\\irr_1axis_10.0.csv\n", + "Index: 10.0. Wm2Front: 23701.188518518517. Wm2Back: 3031.6580740740737\n", + "Linescan in process: 1axis_15.0_Front\n", + "Linescan in process: 1axis_15.0_Back\n", + "Saved: results\\irr_1axis_15.0.csv\n", + "Index: 15.0. Wm2Front: 27908.854074074072. Wm2Back: 3280.738148148148\n", + "Linescan in process: 1axis_20.0_Front\n", + "Linescan in process: 1axis_20.0_Back\n", + "Saved: results\\irr_1axis_20.0.csv\n", + "Index: 20.0. Wm2Front: 104181.43222222223. Wm2Back: 12921.735185185185\n", + "Linescan in process: 1axis_25.0_Front\n", + "Linescan in process: 1axis_25.0_Back\n", + "Saved: results\\irr_1axis_25.0.csv\n", + "Index: 25.0. Wm2Front: 46682.793703703705. Wm2Back: 5581.450777777778\n", + "Linescan in process: 1axis_30.0_Front\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Linescan in process: 1axis_30.0_Back\n", + "Saved: results\\irr_1axis_30.0.csv\n", + "Index: 30.0. Wm2Front: 50298.879629629635. Wm2Back: 5706.771296296296\n", + "Linescan in process: 1axis_35.0_Front\n", + "Linescan in process: 1axis_35.0_Back\n", + "Saved: results\\irr_1axis_35.0.csv\n", + "Index: 35.0. Wm2Front: 74908.2585185185. Wm2Back: 8393.601\n", + "Linescan in process: 1axis_40.0_Front\n", + "Linescan in process: 1axis_40.0_Back\n", + "Saved: results\\irr_1axis_40.0.csv\n", + "Index: 40.0. Wm2Front: 53669.156666666655. Wm2Back: 5906.931444444444\n", + "Linescan in process: 1axis_45.0_Front\n", + "Linescan in process: 1axis_45.0_Back\n", + "Saved: results\\irr_1axis_45.0.csv\n", + "Index: 45.0. Wm2Front: 75872.53370370371. Wm2Back: 7519.38462962963\n", + "Linescan in process: 1axis_50.0_Front\n", + "Linescan in process: 1axis_50.0_Back\n", + "Saved: results\\irr_1axis_50.0.csv\n", + "Index: 50.0. Wm2Front: 56625.33333333333. Wm2Back: 5394.450148148148\n", + "Linescan in process: 1axis_55.0_Front\n", + "Linescan in process: 1axis_55.0_Back\n", + "Saved: results\\irr_1axis_55.0.csv\n", + "Index: 55.0. Wm2Front: 77579.87962962962. Wm2Back: 7054.637814814814\n", + "Linescan in process: 1axis_60.0_Front\n", + "Linescan in process: 1axis_60.0_Back\n", + "Saved: results\\irr_1axis_60.0.csv\n", + "Index: 60.0. Wm2Front: 139322.39740740744. Wm2Back: 12499.350370370372\n", + "\n", + "Saving Cumulative results\n", + "Saved: cumulative_results_.csv\n" + ] + }, + { + "data": { + "text/plain": [ + "{-5.0: {'csvfile': 'EPWs\\\\1axis_-5.0.csv',\n", + " 'surf_azm': 90.0,\n", + " 'surf_tilt': 5.0,\n", + " 'datetime': Index(['1955-01-01 08:00:00', '1955-01-02 08:00:00', '1955-01-03 08:00:00',\n", + " '1955-01-04 08:00:00', '1955-01-05 08:00:00', '1955-01-06 08:00:00',\n", + " '1955-01-07 08:00:00', '1955-01-08 08:00:00', '1955-01-09 08:00:00',\n", + " '1955-01-10 08:00:00',\n", + " ...\n", + " '1959-12-22 08:00:00', '1959-12-23 08:00:00', '1959-12-24 08:00:00',\n", + " '1959-12-25 08:00:00', '1959-12-26 08:00:00', '1959-12-27 08:00:00',\n", + " '1959-12-28 08:00:00', '1959-12-29 08:00:00', '1959-12-30 08:00:00',\n", + " '1959-12-31 08:00:00'],\n", + " dtype='object', length=141),\n", + " 'count': 141,\n", + " 'skyfile': 'skies\\\\1axis_-5.0.rad',\n", + " 'clearance_height': 2.156193024466364,\n", + " 'radfile': 'objects\\\\1axis-5.0_2.156_10.60_5.0_20x7_origin0,0.rad',\n", + " 'scene': ,\n", + " 'octfile': '1axis_-5.0.oct',\n", + " 'AnalysisObj': ,\n", + " 'Wm2Front': [1534.3603333333333,\n", + " 1534.283666666667,\n", + " 1534.196,\n", + " 1534.1083333333333,\n", + " 1552.274666666667,\n", + " 1533.9333333333334,\n", + " 1539.0649999999998,\n", + " 1539.251,\n", + " 1539.425],\n", + " 'Wm2Back': [240.0471,\n", + " 233.29353333333333,\n", + " 227.1744,\n", + " 220.82566666666665,\n", + " 463.8707,\n", + " 221.73103333333333,\n", + " 226.72143333333335,\n", + " 231.76966666666667,\n", + " 237.12],\n", + " 'backRatio': [0.15644756863007497,\n", + " 0.1520536171688261,\n", + " 0.1480738132065178,\n", + " 0.1439438910047263,\n", + " 0.2988326815662252,\n", + " 0.1445505381260345,\n", + " 0.14731105315388254,\n", + " 0.1505729189675678,\n", + " 0.15403143769171107]},\n", + " -30.0: {'csvfile': 'EPWs\\\\1axis_-30.0.csv',\n", + " 'surf_azm': 90.0,\n", + " 'surf_tilt': 30.0,\n", + " 'datetime': Index(['1955-01-01 09:00:00', '1955-01-02 09:00:00', '1955-01-03 09:00:00',\n", + " '1955-01-04 09:00:00', '1955-01-05 09:00:00', '1955-01-06 09:00:00',\n", + " '1955-01-07 09:00:00', '1955-01-08 09:00:00', '1955-01-09 09:00:00',\n", + " '1955-01-10 09:00:00',\n", + " ...\n", + " '1959-12-22 09:00:00', '1959-12-23 09:00:00', '1959-12-24 09:00:00',\n", + " '1959-12-25 09:00:00', '1959-12-26 09:00:00', '1959-12-27 09:00:00',\n", + " '1959-12-28 09:00:00', '1959-12-29 09:00:00', '1959-12-30 09:00:00',\n", + " '1959-12-31 09:00:00'],\n", + " dtype='object', length=157),\n", + " 'count': 157,\n", + " 'skyfile': 'skies\\\\1axis_-30.0.rad',\n", + " 'clearance_height': 1.4749999999999999,\n", + " 'radfile': 'objects\\\\1axis-30.0_1.474_10.60_30.0_20x7_origin0,0.rad',\n", + " 'scene': ,\n", + " 'octfile': '1axis_-30.0.oct',\n", + " 'AnalysisObj': ,\n", + " 'Wm2Front': [54062.65666666668,\n", + " 54337.86666666667,\n", + " 55593.15666666667,\n", + " 55772.82,\n", + " 11065.986666666666,\n", + " 56943.15333333333,\n", + " 57038.58666666667,\n", + " 57132.72,\n", + " 56324.12666666667],\n", + " 'Wm2Back': [7485.416,\n", + " 6976.824333333333,\n", + " 6780.483666666667,\n", + " 6098.631,\n", + " 5105.581,\n", + " 6878.809,\n", + " 7039.092333333333,\n", + " 7351.5,\n", + " 7671.962333333334],\n", + " 'backRatio': [0.13845815805343345,\n", + " 0.1283970945663964,\n", + " 0.12196615467180426,\n", + " 0.10934772332925388,\n", + " 0.4613759886412308,\n", + " 0.12080133390104963,\n", + " 0.12340930274202733,\n", + " 0.12867407452902516,\n", + " 0.1362109392752069]},\n", + " -60.0: {'csvfile': 'EPWs\\\\1axis_-60.0.csv',\n", + " 'surf_azm': 90.0,\n", + " 'surf_tilt': 60.0,\n", + " 'datetime': Index(['1955-01-01 10:00:00', '1955-01-02 10:00:00', '1955-01-03 10:00:00',\n", + " '1955-01-04 10:00:00', '1955-01-05 10:00:00', '1955-01-06 10:00:00',\n", + " '1955-01-07 10:00:00', '1955-01-08 10:00:00', '1955-01-09 10:00:00',\n", + " '1955-01-10 10:00:00',\n", + " ...\n", + " '1959-12-22 10:00:00', '1959-12-23 10:00:00', '1959-12-24 10:00:00',\n", + " '1959-12-25 10:00:00', '1959-12-26 10:00:00', '1959-12-27 10:00:00',\n", + " '1959-12-28 10:00:00', '1959-12-29 10:00:00', '1959-12-30 10:00:00',\n", + " '1959-12-31 10:00:00'],\n", + " dtype='object', length=364),\n", + " 'count': 364,\n", + " 'skyfile': 'skies\\\\1axis_-60.0.rad',\n", + " 'clearance_height': 0.8710580837556761,\n", + " 'radfile': 'objects\\\\1axis-60.0_0.871_10.60_60.0_20x7_origin0,0.rad',\n", + " 'scene': ,\n", + " 'octfile': '1axis_-60.0.oct',\n", + " 'AnalysisObj': ,\n", + " 'Wm2Front': [119283.8,\n", + " 121837.60000000002,\n", + " 125835.3,\n", + " 127907.60000000002,\n", + " 59844.02666666667,\n", + " 127403.39999999998,\n", + " 128563.03333333333,\n", + " 133242.56666666668,\n", + " 134296.33333333334],\n", + " 'Wm2Back': [17105.53,\n", + " 16454.7,\n", + " 16353.996666666666,\n", + " 16443.763333333332,\n", + " 13750.69,\n", + " 17462.969999999998,\n", + " 18039.23,\n", + " 18522.78,\n", + " 18070.013333333332],\n", + " 'backRatio': [0.14340195279323803,\n", + " 0.13505436634459012,\n", + " 0.12996350417333738,\n", + " 0.12855970407367212,\n", + " 0.22977547695472347,\n", + " 0.1370683189218787,\n", + " 0.14031428313389502,\n", + " 0.13901548374794528,\n", + " 0.13455328786921494]},\n", + " -45.0: {'csvfile': 'EPWs\\\\1axis_-45.0.csv',\n", + " 'surf_azm': 90.0,\n", + " 'surf_tilt': 45.0,\n", + " 'datetime': Index(['1955-01-01 11:00:00', '1955-01-02 11:00:00', '1955-01-03 11:00:00',\n", + " '1955-01-04 11:00:00', '1955-01-05 11:00:00', '1955-01-06 11:00:00',\n", + " '1955-01-07 11:00:00', '1955-01-08 11:00:00', '1955-01-09 11:00:00',\n", + " '1955-01-10 11:00:00',\n", + " ...\n", + " '1959-12-03 09:00:00', '1959-12-04 09:00:00', '1959-12-05 09:00:00',\n", + " '1959-12-25 11:00:00', '1959-12-26 11:00:00', '1959-12-27 11:00:00',\n", + " '1959-12-28 11:00:00', '1959-12-29 11:00:00', '1959-12-30 11:00:00',\n", + " '1959-12-31 11:00:00'],\n", + " dtype='object', length=146),\n", + " 'count': 146,\n", + " 'skyfile': 'skies\\\\1axis_-45.0.rad',\n", + " 'clearance_height': 1.1332738110421965,\n", + " 'radfile': 'objects\\\\1axis-45.0_1.133_10.60_45.0_20x7_origin0,0.rad',\n", + " 'scene': ,\n", + " 'octfile': '1axis_-45.0.oct',\n", + " 'AnalysisObj': ,\n", + " 'Wm2Front': [54869.41333333334,\n", + " 55724.19999999999,\n", + " 56134.369999999995,\n", + " 56670.329999999994,\n", + " 13616.066666666666,\n", + " 57094.206666666665,\n", + " 57367.33666666666,\n", + " 56788.00666666666,\n", + " 57000.26666666666],\n", + " 'Wm2Back': [7255.617000000001,\n", + " 6840.532666666667,\n", + " 6609.668333333334,\n", + " 6084.226333333333,\n", + " 5141.047,\n", + " 6894.012999999999,\n", + " 6855.9130000000005,\n", + " 7094.611,\n", + " 7260.890666666666],\n", + " 'backRatio': [0.1322342709167973,\n", + " 0.1227569448087137,\n", + " 0.11774725922079603,\n", + " 0.10736175748352933,\n", + " 0.37757208071062515,\n", + " 0.12074802824569074,\n", + " 0.11950899725966602,\n", + " 0.12493150035556512,\n", + " 0.12738344860287001]},\n", + " -20.0: {'csvfile': 'EPWs\\\\1axis_-20.0.csv',\n", + " 'surf_azm': 90.0,\n", + " 'surf_tilt': 20.0,\n", + " 'datetime': Index(['1955-01-01 12:00:00', '1955-01-02 12:00:00', '1955-01-03 12:00:00',\n", + " '1955-01-04 12:00:00', '1955-01-05 12:00:00', '1955-01-06 12:00:00',\n", + " '1955-01-07 12:00:00', '1955-01-08 12:00:00', '1955-01-09 12:00:00',\n", + " '1955-01-10 12:00:00',\n", + " ...\n", + " '1959-12-22 12:00:00', '1959-12-23 12:00:00', '1959-12-24 12:00:00',\n", + " '1959-12-25 12:00:00', '1959-12-26 12:00:00', '1959-12-27 12:00:00',\n", + " '1959-12-28 12:00:00', '1959-12-29 12:00:00', '1959-12-30 12:00:00',\n", + " '1959-12-31 12:00:00'],\n", + " dtype='object', length=110),\n", + " 'count': 110,\n", + " 'skyfile': 'skies\\\\1axis_-20.0.rad',\n", + " 'clearance_height': 1.7356667635126466,\n", + " 'radfile': 'objects\\\\1axis-20.0_1.735_10.60_20.0_20x7_origin0,0.rad',\n", + " 'scene': ,\n", + " 'octfile': '1axis_-20.0.oct',\n", + " 'AnalysisObj': ,\n", + " 'Wm2Front': [30459.63,\n", + " 30506.34,\n", + " 30551.876666666667,\n", + " 30878.60666666667,\n", + " 20904.02,\n", + " 30947.069999999996,\n", + " 31691.679999999997,\n", + " 31706.86,\n", + " 31720.783333333336],\n", + " 'Wm2Back': [4411.466333333334,\n", + " 4112.6140000000005,\n", + " 3949.1746666666672,\n", + " 3788.402666666667,\n", + " 2979.804,\n", + " 3917.3323333333333,\n", + " 4162.909000000001,\n", + " 4368.173,\n", + " 4602.2733333333335],\n", + " 'backRatio': [0.14482993353837192,\n", + " 0.13481177568951977,\n", + " 0.12926127519080036,\n", + " 0.12268696527907999,\n", + " 0.14254692912908956,\n", + " 0.12658168307215031,\n", + " 0.13135652223686087,\n", + " 0.13776743777947617,\n", + " 0.14508699674544617]},\n", + " 10.0: {'csvfile': 'EPWs\\\\1axis_10.0.csv',\n", + " 'surf_azm': 90.0,\n", + " 'surf_tilt': -10.0,\n", + " 'datetime': Index(['1955-01-01 13:00:00', '1955-01-01 17:00:00', '1955-01-02 13:00:00',\n", + " '1955-01-02 17:00:00', '1955-01-03 13:00:00', '1955-01-03 17:00:00',\n", + " '1955-01-04 17:00:00', '1971-02-27 18:00:00', '1971-02-28 18:00:00',\n", + " '1972-03-01 18:00:00', '1974-05-03 19:00:00', '1974-05-04 19:00:00',\n", + " '1974-05-05 19:00:00', '1974-05-06 19:00:00', '1974-05-07 19:00:00',\n", + " '1974-05-08 19:00:00', '1954-08-13 19:00:00', '1954-08-14 19:00:00',\n", + " '1954-08-15 19:00:00', '1954-08-16 19:00:00', '1954-08-17 19:00:00',\n", + " '1965-09-14 13:00:00', '1965-09-15 13:00:00', '1965-09-16 13:00:00',\n", + " '1965-09-17 13:00:00', '1965-09-18 13:00:00', '1965-09-19 13:00:00',\n", + " '1965-09-20 13:00:00', '1965-09-21 13:00:00', '1965-09-22 13:00:00',\n", + " '1965-09-23 13:00:00', '1965-09-24 13:00:00', '1965-09-24 18:00:00',\n", + " '1965-09-25 13:00:00', '1965-09-25 18:00:00', '1965-09-26 13:00:00',\n", + " '1965-09-26 18:00:00', '1965-09-27 13:00:00', '1965-09-28 13:00:00',\n", + " '1965-09-29 13:00:00', '1965-09-30 13:00:00', '1969-10-01 13:00:00',\n", + " '1969-10-02 13:00:00', '1969-10-03 13:00:00', '1969-10-04 13:00:00',\n", + " '1969-10-05 13:00:00', '1969-10-06 13:00:00', '1969-10-07 13:00:00',\n", + " '1969-10-08 13:00:00', '1969-10-09 13:00:00', '1969-10-10 13:00:00',\n", + " '1969-10-11 13:00:00', '1969-10-12 13:00:00', '1969-10-13 13:00:00',\n", + " '1969-10-14 13:00:00', '1969-10-15 13:00:00', '1960-11-08 17:00:00',\n", + " '1960-11-09 17:00:00', '1960-11-10 17:00:00', '1960-11-11 17:00:00',\n", + " '1960-11-12 17:00:00', '1959-12-13 13:00:00', '1959-12-14 13:00:00',\n", + " '1959-12-15 13:00:00', '1959-12-16 13:00:00', '1959-12-17 13:00:00',\n", + " '1959-12-18 13:00:00', '1959-12-19 13:00:00', '1959-12-20 13:00:00',\n", + " '1959-12-21 13:00:00', '1959-12-22 13:00:00', '1959-12-23 13:00:00',\n", + " '1959-12-24 13:00:00', '1959-12-25 13:00:00', '1959-12-26 13:00:00',\n", + " '1959-12-27 13:00:00', '1959-12-28 13:00:00', '1959-12-29 13:00:00',\n", + " '1959-12-30 13:00:00', '1959-12-31 13:00:00', '1959-12-31 17:00:00'],\n", + " dtype='object'),\n", + " 'count': 81,\n", + " 'skyfile': 'skies\\\\1axis_10.0.rad',\n", + " 'clearance_height': 2.013480506849565,\n", + " 'radfile': 'objects\\\\1axis10.0_2.013_10.60_-10.0_20x7_origin0,0.rad',\n", + " 'scene': ,\n", + " 'octfile': '1axis_10.0.oct',\n", + " 'AnalysisObj': ,\n", + " 'Wm2Front': [23457.553333333333,\n", + " 23456.61666666667,\n", + " 23454.88,\n", + " 23453.14,\n", + " 23972.696666666667,\n", + " 23449.66,\n", + " 24024.210000000003,\n", + " 24022.33,\n", + " 24019.61],\n", + " 'Wm2Back': [3126.5446666666667,\n", + " 2954.501666666667,\n", + " 2826.9336666666663,\n", + " 2705.961333333333,\n", + " 3187.428,\n", + " 2822.3060000000005,\n", + " 3004.9166666666665,\n", + " 3206.300333333333,\n", + " 3450.0303333333336],\n", + " 'backRatio': [0.1332851934280219,\n", + " 0.12595599709438074,\n", + " 0.12052645531080146,\n", + " 0.11537735322246744,\n", + " 0.13296075578644723,\n", + " 0.12035594032681328,\n", + " 0.12507868277824674,\n", + " 0.1334716574063247,\n", + " 0.14363389704076945]},\n", + " 35.0: {'csvfile': 'EPWs\\\\1axis_35.0.csv',\n", + " 'surf_azm': 90.0,\n", + " 'surf_tilt': -35.0,\n", + " 'datetime': Index(['1955-01-01 14:00:00', '1955-01-02 14:00:00', '1955-01-03 14:00:00',\n", + " '1955-01-04 14:00:00', '1955-01-05 14:00:00', '1955-01-06 14:00:00',\n", + " '1955-01-07 14:00:00', '1971-02-09 17:00:00', '1971-02-10 17:00:00',\n", + " '1971-02-11 17:00:00',\n", + " ...\n", + " '1959-12-22 14:00:00', '1959-12-23 14:00:00', '1959-12-24 14:00:00',\n", + " '1959-12-25 14:00:00', '1959-12-26 14:00:00', '1959-12-27 14:00:00',\n", + " '1959-12-28 14:00:00', '1959-12-29 14:00:00', '1959-12-30 14:00:00',\n", + " '1959-12-31 14:00:00'],\n", + " dtype='object', length=167),\n", + " 'count': 167,\n", + " 'skyfile': 'skies\\\\1axis_35.0.rad',\n", + " 'clearance_height': 1.3535988800207739,\n", + " 'radfile': 'objects\\\\1axis35.0_1.353_10.60_-35.0_20x7_origin0,0.rad',\n", + " 'scene': ,\n", + " 'octfile': '1axis_35.0.oct',\n", + " 'AnalysisObj': ,\n", + " 'Wm2Front': [83889.76666666666,\n", + " 83629.84666666668,\n", + " 83318.62666666666,\n", + " 82984.12333333334,\n", + " 15074.036666666667,\n", + " 82892.26333333334,\n", + " 82380.65999999999,\n", + " 80191.84666666666,\n", + " 79813.15666666666],\n", + " 'Wm2Back': [8390.268666666667,\n", + " 8215.417000000001,\n", + " 8067.439666666666,\n", + " 7817.835666666667,\n", + " 7727.05,\n", + " 7723.242000000001,\n", + " 8382.127999999999,\n", + " 9132.451333333333,\n", + " 10086.576666666666],\n", + " 'backRatio': [0.10001539997112799,\n", + " 0.09823546531789888,\n", + " 0.09682636275458258,\n", + " 0.09420881077522406,\n", + " 0.51260652061968,\n", + " 0.09317204762247357,\n", + " 0.10174873445115959,\n", + " 0.11388254042099366,\n", + " 0.12637736635847005]},\n", + " 55.0: {'csvfile': 'EPWs\\\\1axis_55.0.csv',\n", + " 'surf_azm': 90.0,\n", + " 'surf_tilt': -55.0,\n", + " 'datetime': Index(['1955-01-01 15:00:00', '1955-01-02 15:00:00', '1955-01-03 15:00:00',\n", + " '1955-01-04 15:00:00', '1955-01-05 15:00:00', '1955-01-06 15:00:00',\n", + " '1955-01-07 15:00:00', '1955-01-08 15:00:00', '1955-01-09 15:00:00',\n", + " '1955-01-10 15:00:00',\n", + " ...\n", + " '1959-12-25 15:00:00', '1959-12-26 15:00:00', '1959-12-27 15:00:00',\n", + " '1959-12-28 15:00:00', '1959-12-29 15:00:00', '1959-12-29 16:00:00',\n", + " '1959-12-30 15:00:00', '1959-12-30 16:00:00', '1959-12-31 15:00:00',\n", + " '1959-12-31 16:00:00'],\n", + " dtype='object', length=173),\n", + " 'count': 173,\n", + " 'skyfile': 'skies\\\\1axis_55.0.rad',\n", + " 'clearance_height': 0.9483991269231634,\n", + " 'radfile': 'objects\\\\1axis55.0_0.948_10.60_-55.0_20x7_origin0,0.rad',\n", + " 'scene': ,\n", + " 'octfile': '1axis_55.0.oct',\n", + " 'AnalysisObj': ,\n", + " 'Wm2Front': [85880.50666666665,\n", + " 85417.34000000001,\n", + " 83645.57,\n", + " 83123.26666666668,\n", + " 37171.57666666667,\n", + " 81589.56666666667,\n", + " 80841.56666666667,\n", + " 80858.04333333333,\n", + " 79691.48],\n", + " 'Wm2Back': [7203.573666666666,\n", + " 7003.507333333334,\n", + " 6963.620666666667,\n", + " 6793.723000000001,\n", + " 5704.894,\n", + " 6779.987,\n", + " 7223.464333333333,\n", + " 7632.127,\n", + " 8186.843333333333],\n", + " 'backRatio': [0.08387902985653442,\n", + " 0.08199163368165877,\n", + " 0.08325151688744722,\n", + " 0.08173070177225908,\n", + " 0.15347462653208344,\n", + " 0.083098699918347,\n", + " 0.08935334311078405,\n", + " 0.09438921090568218,\n", + " 0.10273172528106653]},\n", + " 60.0: {'csvfile': 'EPWs\\\\1axis_60.0.csv',\n", + " 'surf_azm': 90.0,\n", + " 'surf_tilt': -60.0,\n", + " 'datetime': Index(['1955-01-01 16:00:00', '1955-01-02 16:00:00', '1955-01-03 16:00:00',\n", + " '1955-01-04 16:00:00', '1955-01-05 16:00:00', '1955-01-06 16:00:00',\n", + " '1955-01-07 16:00:00', '1955-01-08 16:00:00', '1955-01-09 16:00:00',\n", + " '1955-01-10 16:00:00',\n", + " ...\n", + " '1959-12-06 15:00:00', '1959-12-07 15:00:00', '1959-12-08 15:00:00',\n", + " '1959-12-09 15:00:00', '1959-12-10 15:00:00', '1959-12-11 15:00:00',\n", + " '1959-12-12 15:00:00', '1959-12-13 15:00:00', '1959-12-14 15:00:00',\n", + " '1959-12-15 15:00:00'],\n", + " dtype='object', length=375),\n", + " 'count': 375,\n", + " 'skyfile': 'skies\\\\1axis_60.0.rad',\n", + " 'clearance_height': 0.8710580837556761,\n", + " 'radfile': 'objects\\\\1axis60.0_0.871_10.60_-60.0_20x7_origin0,0.rad',\n", + " 'scene': ,\n", + " 'octfile': '1axis_60.0.oct',\n", + " 'AnalysisObj': ,\n", + " 'Wm2Front': [161390.33333333334,\n", + " 160223.83333333334,\n", + " 157598.6,\n", + " 156292.3333333333,\n", + " 43372.74333333333,\n", + " 155355.6,\n", + " 150839.30000000002,\n", + " 140319.46666666667,\n", + " 128509.36666666665],\n", + " 'Wm2Back': [13750.003333333334,\n", + " 13362.163333333336,\n", + " 12859.533333333335,\n", + " 12501.663333333332,\n", + " 10394.85,\n", + " 11787.826666666666,\n", + " 12227.046666666667,\n", + " 12689.199999999999,\n", + " 12921.866666666667],\n", + " 'backRatio': [0.0851971921994676,\n", + " 0.0833968515915952,\n", + " 0.08159674801512569,\n", + " 0.07998897314228057,\n", + " 0.23966318386755225,\n", + " 0.07587641894331616,\n", + " 0.08106008570449863,\n", + " 0.09043078776598266,\n", + " 0.10055194342084059]},\n", + " 5.0: {'csvfile': 'EPWs\\\\1axis_5.0.csv',\n", + " 'surf_azm': 90.0,\n", + " 'surf_tilt': -5.0,\n", + " 'datetime': Index(['1955-01-04 13:00:00', '1955-01-05 13:00:00', '1955-01-06 13:00:00',\n", + " '1955-01-07 13:00:00', '1955-01-08 13:00:00', '1955-01-09 13:00:00',\n", + " '1955-01-10 13:00:00', '1955-01-11 13:00:00', '1955-01-12 13:00:00',\n", + " '1955-01-13 13:00:00',\n", + " ...\n", + " '1965-09-04 13:00:00', '1965-09-05 13:00:00', '1965-09-06 13:00:00',\n", + " '1965-09-07 13:00:00', '1965-09-08 13:00:00', '1965-09-09 13:00:00',\n", + " '1965-09-10 13:00:00', '1965-09-11 13:00:00', '1965-09-12 13:00:00',\n", + " '1965-09-13 13:00:00'],\n", + " dtype='object', length=244),\n", + " 'count': 244,\n", + " 'skyfile': 'skies\\\\1axis_5.0.rad',\n", + " 'clearance_height': 2.156193024466364,\n", + " 'radfile': 'objects\\\\1axis5.0_2.156_10.60_-5.0_20x7_origin0,0.rad',\n", + " 'scene': ,\n", + " 'octfile': '1axis_5.0.oct',\n", + " 'AnalysisObj': ,\n", + " 'Wm2Front': [138471.2,\n", + " 138477.0,\n", + " 138479.6,\n", + " 138482.3,\n", + " 139722.6,\n", + " 138487.6,\n", + " 138490.3,\n", + " 138493.0,\n", + " 137706.2],\n", + " 'Wm2Back': [18093.4,\n", + " 16930.41,\n", + " 16416.066666666666,\n", + " 15745.226666666667,\n", + " 23449.039999999997,\n", + " 16221.06,\n", + " 17720.286666666667,\n", + " 19047.736666666668,\n", + " 20588.873333333337],\n", + " 'backRatio': [0.13066543706802977,\n", + " 0.12226152991282647,\n", + " 0.11854501708642752,\n", + " 0.11369847664985484,\n", + " 0.16782567624832576,\n", + " 0.11713005267525718,\n", + " 0.12795326848677055,\n", + " 0.13753573486841164,\n", + " 0.1495130443205919]},\n", + " 15.0: {'csvfile': 'EPWs\\\\1axis_15.0.csv',\n", + " 'surf_azm': 90.0,\n", + " 'surf_tilt': -15.0,\n", + " 'datetime': Index(['1955-01-05 17:00:00', '1955-01-06 17:00:00', '1955-01-07 17:00:00',\n", + " '1955-01-08 17:00:00', '1955-01-09 17:00:00', '1955-01-10 17:00:00',\n", + " '1955-01-11 17:00:00', '1955-01-12 17:00:00', '1955-01-13 17:00:00',\n", + " '1955-01-14 17:00:00',\n", + " ...\n", + " '1959-12-03 13:00:00', '1959-12-04 13:00:00', '1959-12-05 13:00:00',\n", + " '1959-12-06 13:00:00', '1959-12-07 13:00:00', '1959-12-08 13:00:00',\n", + " '1959-12-09 13:00:00', '1959-12-10 13:00:00', '1959-12-11 13:00:00',\n", + " '1959-12-12 13:00:00'],\n", + " dtype='object', length=123),\n", + " 'count': 123,\n", + " 'skyfile': 'skies\\\\1axis_15.0.rad',\n", + " 'clearance_height': 1.8729485755808406,\n", + " 'radfile': 'objects\\\\1axis15.0_1.872_10.60_-15.0_20x7_origin0,0.rad',\n", + " 'scene': ,\n", + " 'octfile': '1axis_15.0.oct',\n", + " 'AnalysisObj': ,\n", + " 'Wm2Front': [28728.400000000005,\n", + " 28725.056666666667,\n", + " 28720.53,\n", + " 28716.0,\n", + " 22983.27,\n", + " 27709.966666666664,\n", + " 27688.786666666667,\n", + " 27666.426666666666,\n", + " 30241.25],\n", + " 'Wm2Back': [3516.246333333333,\n", + " 3338.479,\n", + " 3235.217666666666,\n", + " 3058.172,\n", + " 2228.25,\n", + " 3264.0533333333333,\n", + " 3340.9906666666666,\n", + " 3652.3789999999995,\n", + " 3892.8553333333334],\n", + " 'backRatio': [0.12239617280938582,\n", + " 0.11622183804609246,\n", + " 0.11264477201576344,\n", + " 0.10649714074045338,\n", + " 0.09695095184667143,\n", + " 0.11779347318617707,\n", + " 0.12066222280611948,\n", + " 0.13201483921252669,\n", + " 0.12872666323669393]},\n", + " 30.0: {'csvfile': 'EPWs\\\\1axis_30.0.csv',\n", + " 'surf_azm': 90.0,\n", + " 'surf_tilt': -30.0,\n", + " 'datetime': Index(['1955-01-08 14:00:00', '1955-01-09 14:00:00', '1955-01-10 14:00:00',\n", + " '1955-01-11 14:00:00', '1955-01-12 14:00:00', '1955-01-13 14:00:00',\n", + " '1955-01-14 14:00:00', '1955-01-15 14:00:00', '1955-01-16 14:00:00',\n", + " '1955-01-17 14:00:00',\n", + " ...\n", + " '1969-10-10 14:00:00', '1969-10-10 17:00:00', '1969-10-11 14:00:00',\n", + " '1969-10-11 17:00:00', '1969-10-12 14:00:00', '1969-10-12 17:00:00',\n", + " '1969-10-13 14:00:00', '1969-10-13 17:00:00', '1969-10-14 14:00:00',\n", + " '1969-10-15 14:00:00'],\n", + " dtype='object', length=116),\n", + " 'count': 116,\n", + " 'skyfile': 'skies\\\\1axis_30.0.rad',\n", + " 'clearance_height': 1.4749999999999999,\n", + " 'radfile': 'objects\\\\1axis30.0_1.474_10.60_-30.0_20x7_origin0,0.rad',\n", + " 'scene': ,\n", + " 'octfile': '1axis_30.0.oct',\n", + " 'AnalysisObj': ,\n", + " 'Wm2Front': [55922.36333333334,\n", + " 55776.723333333335,\n", + " 56500.86666666667,\n", + " 56303.56666666667,\n", + " 13493.799999999997,\n", + " 53965.38333333333,\n", + " 53725.76,\n", + " 53633.06333333333,\n", + " 53368.38999999999],\n", + " 'Wm2Back': [5614.835,\n", + " 5452.806666666666,\n", + " 5202.816999999999,\n", + " 5266.381,\n", + " 5129.235,\n", + " 6065.844666666667,\n", + " 5681.163666666667,\n", + " 6169.126333333334,\n", + " 6778.732333333333],\n", + " 'backRatio': [0.10040410606625937,\n", + " 0.09776132843656356,\n", + " 0.09208384251184625,\n", + " 0.0935354759609283,\n", + " 0.3801178778314576,\n", + " 0.11240251026841881,\n", + " 0.10574375422372645,\n", + " 0.11502468505233586,\n", + " 0.12701773852116574]},\n", + " 50.0: {'csvfile': 'EPWs\\\\1axis_50.0.csv',\n", + " 'surf_azm': 90.0,\n", + " 'surf_tilt': -50.0,\n", + " 'datetime': Index(['1955-01-12 15:00:00', '1955-01-13 15:00:00', '1955-01-14 15:00:00',\n", + " '1955-01-15 15:00:00', '1955-01-16 15:00:00', '1955-01-17 15:00:00',\n", + " '1955-01-18 15:00:00', '1955-01-19 15:00:00', '1955-01-20 15:00:00',\n", + " '1955-01-21 15:00:00',\n", + " ...\n", + " '1960-11-15 16:00:00', '1960-11-16 16:00:00', '1960-11-17 16:00:00',\n", + " '1960-11-18 16:00:00', '1959-12-23 16:00:00', '1959-12-24 16:00:00',\n", + " '1959-12-25 16:00:00', '1959-12-26 16:00:00', '1959-12-27 16:00:00',\n", + " '1959-12-28 16:00:00'],\n", + " dtype='object', length=126),\n", + " 'count': 126,\n", + " 'skyfile': 'skies\\\\1axis_50.0.rad',\n", + " 'clearance_height': 1.036026668853686,\n", + " 'radfile': 'objects\\\\1axis50.0_1.036_10.60_-50.0_20x7_origin0,0.rad',\n", + " 'scene': ,\n", + " 'octfile': '1axis_50.0.oct',\n", + " 'AnalysisObj': ,\n", + " 'Wm2Front': [62983.89333333333,\n", + " 62677.28,\n", + " 61405.54666666667,\n", + " 61022.58666666667,\n", + " 25149.289999999997,\n", + " 60820.98333333334,\n", + " 60245.556666666664,\n", + " 58120.11666666667,\n", + " 57202.746666666666],\n", + " 'Wm2Back': [5398.505,\n", + " 5293.818666666666,\n", + " 5445.18,\n", + " 5402.928,\n", + " 4185.281,\n", + " 5112.665666666667,\n", + " 5534.503333333334,\n", + " 5873.1466666666665,\n", + " 6304.023],\n", + " 'backRatio': [0.08571246756240854,\n", + " 0.08446152389199311,\n", + " 0.08867570125029366,\n", + " 0.08853980479348512,\n", + " 0.1664174548698013,\n", + " 0.08406088330708975,\n", + " 0.09186574990234551,\n", + " 0.10105187158000305,\n", + " 0.1102048984908027]},\n", + " 20.0: {'csvfile': 'EPWs\\\\1axis_20.0.csv',\n", + " 'surf_azm': 90.0,\n", + " 'surf_tilt': -20.0,\n", + " 'datetime': Index(['1955-01-16 17:00:00', '1955-01-17 17:00:00', '1955-01-18 17:00:00',\n", + " '1955-01-19 17:00:00', '1955-01-20 17:00:00', '1955-01-21 17:00:00',\n", + " '1955-01-22 17:00:00', '1955-01-23 17:00:00', '1955-01-24 17:00:00',\n", + " '1972-03-14 18:00:00',\n", + " ...\n", + " '1965-09-15 18:00:00', '1965-09-16 18:00:00', '1969-10-21 17:00:00',\n", + " '1969-10-22 17:00:00', '1969-10-23 17:00:00', '1969-10-24 17:00:00',\n", + " '1969-10-25 17:00:00', '1969-10-26 17:00:00', '1969-10-27 17:00:00',\n", + " '1969-10-28 17:00:00'],\n", + " dtype='object', length=225),\n", + " 'count': 225,\n", + " 'skyfile': 'skies\\\\1axis_20.0.rad',\n", + " 'clearance_height': 1.7356667635126466,\n", + " 'radfile': 'objects\\\\1axis20.0_1.735_10.60_-20.0_20x7_origin0,0.rad',\n", + " 'scene': ,\n", + " 'octfile': '1axis_20.0.oct',\n", + " 'AnalysisObj': ,\n", + " 'Wm2Front': [105404.43333333333,\n", + " 105354.63333333335,\n", + " 105303.86666666665,\n", + " 105253.06666666667,\n", + " 83294.49,\n", + " 109354.2,\n", + " 109239.5,\n", + " 109123.59999999999,\n", + " 105305.10000000002],\n", + " 'Wm2Back': [12661.416666666666,\n", + " 12051.793333333333,\n", + " 11884.773333333333,\n", + " 11132.796666666667,\n", + " 16893.97,\n", + " 11709.910000000002,\n", + " 12357.17,\n", + " 13264.453333333333,\n", + " 14339.333333333334],\n", + " 'backRatio': [0.12012223913299434,\n", + " 0.11439262648098095,\n", + " 0.11286169821372469,\n", + " 0.10577170730950952,\n", + " 0.20282217703929542,\n", + " 0.10708239731914827,\n", + " 0.11311997845907407,\n", + " 0.12155439530751312,\n", + " 0.13616940867217184]},\n", + " -35.0: {'csvfile': 'EPWs\\\\1axis_-35.0.csv',\n", + " 'surf_azm': 90.0,\n", + " 'surf_tilt': 35.0,\n", + " 'datetime': Index(['1955-01-24 09:00:00', '1955-01-25 09:00:00', '1955-01-26 09:00:00',\n", + " '1955-01-27 09:00:00', '1955-01-28 09:00:00', '1955-01-29 09:00:00',\n", + " '1955-01-30 09:00:00', '1955-01-31 09:00:00', '1971-02-01 09:00:00',\n", + " '1971-02-28 11:00:00',\n", + " ...\n", + " '1959-12-01 11:00:00', '1959-12-12 09:00:00', '1959-12-13 09:00:00',\n", + " '1959-12-14 09:00:00', '1959-12-15 09:00:00', '1959-12-16 09:00:00',\n", + " '1959-12-17 09:00:00', '1959-12-18 09:00:00', '1959-12-19 09:00:00',\n", + " '1959-12-20 09:00:00'],\n", + " dtype='object', length=146),\n", + " 'count': 146,\n", + " 'skyfile': 'skies\\\\1axis_-35.0.rad',\n", + " 'clearance_height': 1.3535988800207739,\n", + " 'radfile': 'objects\\\\1axis-35.0_1.353_10.60_35.0_20x7_origin0,0.rad',\n", + " 'scene': ,\n", + " 'octfile': '1axis_-35.0.oct',\n", + " 'AnalysisObj': ,\n", + " 'Wm2Front': [61511.4,\n", + " 62014.53666666666,\n", + " 64643.99,\n", + " 64913.13333333333,\n", + " 11228.246666666666,\n", + " 62641.16,\n", + " 62787.68,\n", + " 62933.22666666668,\n", + " 62112.53333333333],\n", + " 'Wm2Back': [7968.215,\n", + " 7417.069666666666,\n", + " 7039.045000000001,\n", + " 6513.642666666667,\n", + " 5536.068,\n", + " 6925.639,\n", + " 7458.2,\n", + " 7407.279333333333,\n", + " 7697.186000000001],\n", + " 'backRatio': [0.12954045706096012,\n", + " 0.11960211179085198,\n", + " 0.1088893939113382,\n", + " 0.10034398636828522,\n", + " 0.4930482622355172,\n", + " 0.11056051467500738,\n", + " 0.1187844475415488,\n", + " 0.11770061075791105,\n", + " 0.12392323196300858]},\n", + " 25.0: {'csvfile': 'EPWs\\\\1axis_25.0.csv',\n", + " 'surf_azm': 90.0,\n", + " 'surf_tilt': -25.0,\n", + " 'datetime': Index(['1955-01-25 17:00:00', '1955-01-26 17:00:00', '1955-01-27 17:00:00',\n", + " '1955-01-28 17:00:00', '1955-01-29 14:00:00', '1955-01-29 17:00:00',\n", + " '1955-01-30 14:00:00', '1955-01-30 17:00:00', '1955-01-31 14:00:00',\n", + " '1955-01-31 17:00:00',\n", + " ...\n", + " '1965-09-22 14:00:00', '1965-09-23 14:00:00', '1965-09-24 14:00:00',\n", + " '1969-10-14 17:00:00', '1969-10-15 17:00:00', '1969-10-16 17:00:00',\n", + " '1969-10-17 17:00:00', '1969-10-18 17:00:00', '1969-10-19 17:00:00',\n", + " '1969-10-20 17:00:00'],\n", + " dtype='object', length=154),\n", + " 'count': 154,\n", + " 'skyfile': 'skies\\\\1axis_25.0.rad',\n", + " 'clearance_height': 1.6026798681278458,\n", + " 'radfile': 'objects\\\\1axis25.0_1.602_10.60_-25.0_20x7_origin0,0.rad',\n", + " 'scene': ,\n", + " 'octfile': '1axis_25.0.oct',\n", + " 'AnalysisObj': ,\n", + " 'Wm2Front': [51040.78,\n", + " 50905.73,\n", + " 49022.34666666667,\n", + " 48858.1,\n", + " 29263.069999999996,\n", + " 48275.43666666667,\n", + " 48014.56666666667,\n", + " 47536.27,\n", + " 47228.84333333333],\n", + " 'Wm2Back': [5546.28,\n", + " 5358.104333333333,\n", + " 5177.282999999999,\n", + " 5077.035666666667,\n", + " 6236.573,\n", + " 5028.856666666667,\n", + " 5587.237666666667,\n", + " 5843.6973333333335,\n", + " 6377.989333333334],\n", + " 'backRatio': [0.10866369775964048,\n", + " 0.105255424646261,\n", + " 0.10561067036616761,\n", + " 0.10391389683087902,\n", + " 0.21312093320622436,\n", + " 0.10417008958862331,\n", + " 0.11636546861059244,\n", + " 0.1229313366488788,\n", + " 0.13504436586079782]},\n", + " -10.0: {'csvfile': 'EPWs\\\\1axis_-10.0.csv',\n", + " 'surf_azm': 90.0,\n", + " 'surf_tilt': 10.0,\n", + " 'datetime': Index(['1955-01-30 08:00:00', '1971-02-01 08:00:00', '1971-02-02 08:00:00',\n", + " '1971-02-03 08:00:00', '1971-02-04 08:00:00', '1971-02-05 08:00:00',\n", + " '1971-02-06 08:00:00', '1971-02-07 08:00:00', '1971-02-08 08:00:00',\n", + " '1971-02-09 08:00:00',\n", + " ...\n", + " '1959-12-02 08:00:00', '1959-12-03 08:00:00', '1959-12-04 08:00:00',\n", + " '1959-12-05 08:00:00', '1959-12-06 08:00:00', '1959-12-07 08:00:00',\n", + " '1959-12-08 08:00:00', '1959-12-09 08:00:00', '1959-12-10 08:00:00',\n", + " '1959-12-11 08:00:00'],\n", + " dtype='object', length=362),\n", + " 'count': 362,\n", + " 'skyfile': 'skies\\\\1axis_-10.0.rad',\n", + " 'clearance_height': 2.013480506849565,\n", + " 'radfile': 'objects\\\\1axis-10.0_2.013_10.60_10.0_20x7_origin0,0.rad',\n", + " 'scene': ,\n", + " 'octfile': '1axis_-10.0.oct',\n", + " 'AnalysisObj': ,\n", + " 'Wm2Front': [142149.30000000002,\n", + " 142170.7,\n", + " 142189.6,\n", + " 142208.5,\n", + " 116055.89999999998,\n", + " 142804.1,\n", + " 142811.1,\n", + " 142815.8,\n", + " 142820.4],\n", + " 'Wm2Back': [19357.343333333334,\n", + " 17860.19,\n", + " 17304.816666666666,\n", + " 16615.5,\n", + " 18122.1,\n", + " 17010.86,\n", + " 18373.096666666668,\n", + " 19992.573333333334,\n", + " 21014.89333333333],\n", + " 'backRatio': [0.1361761415438359,\n", + " 0.12562496966234976,\n", + " 0.12170240682134459,\n", + " 0.11683900669201204,\n", + " 0.1561497506275015,\n", + " 0.11912024851443168,\n", + " 0.12865314067333372,\n", + " 0.1399885250325581,\n", + " 0.14714209725075153]},\n", + " 45.0: {'csvfile': 'EPWs\\\\1axis_45.0.csv',\n", + " 'surf_azm': 90.0,\n", + " 'surf_tilt': -45.0,\n", + " 'datetime': Index(['1955-01-31 15:00:00', '1971-02-01 15:00:00', '1971-02-02 15:00:00',\n", + " '1971-02-03 15:00:00', '1971-02-04 15:00:00', '1971-02-05 15:00:00',\n", + " '1971-02-06 15:00:00', '1971-02-07 15:00:00', '1971-02-08 15:00:00',\n", + " '1971-02-09 15:00:00',\n", + " ...\n", + " '1959-12-13 16:00:00', '1959-12-14 16:00:00', '1959-12-15 16:00:00',\n", + " '1959-12-16 16:00:00', '1959-12-17 16:00:00', '1959-12-18 16:00:00',\n", + " '1959-12-19 16:00:00', '1959-12-20 16:00:00', '1959-12-21 16:00:00',\n", + " '1959-12-22 16:00:00'],\n", + " dtype='object', length=168),\n", + " 'count': 168,\n", + " 'skyfile': 'skies\\\\1axis_45.0.rad',\n", + " 'clearance_height': 1.1332738110421965,\n", + " 'radfile': 'objects\\\\1axis45.0_1.133_10.60_-45.0_20x7_origin0,0.rad',\n", + " 'scene': ,\n", + " 'octfile': '1axis_45.0.oct',\n", + " 'AnalysisObj': ,\n", + " 'Wm2Front': [84419.61,\n", + " 84044.59333333332,\n", + " 83932.23,\n", + " 83421.37666666666,\n", + " 28896.943333333333,\n", + " 81552.68666666666,\n", + " 80827.87333333334,\n", + " 78352.79333333333,\n", + " 77404.69666666667],\n", + " 'Wm2Back': [7521.922,\n", + " 7388.613333333334,\n", + " 7263.970666666667,\n", + " 7074.316,\n", + " 5837.723999999999,\n", + " 7190.440333333333,\n", + " 7792.730333333333,\n", + " 8352.11,\n", + " 9252.635],\n", + " 'backRatio': [0.08910159512580554,\n", + " 0.0879130108478958,\n", + " 0.08654566404492056,\n", + " 0.08480219576650243,\n", + " 0.20201873016954397,\n", + " 0.088169262584246,\n", + " 0.09641142239119381,\n", + " 0.10659619827300522,\n", + " 0.11953583282303197]},\n", + " -40.0: {'csvfile': 'EPWs\\\\1axis_-40.0.csv',\n", + " 'surf_azm': 90.0,\n", + " 'surf_tilt': 40.0,\n", + " 'datetime': Index(['1971-02-02 09:00:00', '1971-02-03 09:00:00', '1971-02-04 09:00:00',\n", + " '1971-02-04 11:00:00', '1971-02-05 09:00:00', '1971-02-05 11:00:00',\n", + " '1971-02-06 09:00:00', '1971-02-06 11:00:00', '1971-02-07 09:00:00',\n", + " '1971-02-07 11:00:00',\n", + " ...\n", + " '1959-12-15 11:00:00', '1959-12-16 11:00:00', '1959-12-17 11:00:00',\n", + " '1959-12-18 11:00:00', '1959-12-19 11:00:00', '1959-12-20 11:00:00',\n", + " '1959-12-21 11:00:00', '1959-12-22 11:00:00', '1959-12-23 11:00:00',\n", + " '1959-12-24 11:00:00'],\n", + " dtype='object', length=173),\n", + " 'count': 173,\n", + " 'skyfile': 'skies\\\\1axis_-40.0.rad',\n", + " 'clearance_height': 1.2394004440172102,\n", + " 'radfile': 'objects\\\\1axis-40.0_1.239_10.60_40.0_20x7_origin0,0.rad',\n", + " 'scene': ,\n", + " 'octfile': '1axis_-40.0.oct',\n", + " 'AnalysisObj': ,\n", + " 'Wm2Front': [77381.89666666667,\n", + " 77961.64666666667,\n", + " 78261.67333333332,\n", + " 78605.46666666666,\n", + " 15135.433333333332,\n", + " 77744.19666666667,\n", + " 77947.39333333333,\n", + " 82461.55,\n", + " 82641.98],\n", + " 'Wm2Back': [9986.437666666667,\n", + " 9348.026333333333,\n", + " 8793.681333333332,\n", + " 8296.652666666667,\n", + " 6766.795999999999,\n", + " 9035.484333333332,\n", + " 9160.425333333333,\n", + " 9498.352666666666,\n", + " 10050.73],\n", + " 'backRatio': [0.12905392563109064,\n", + " 0.11990544855211135,\n", + " 0.11236255048517298,\n", + " 0.10554803518057224,\n", + " 0.44708304043163344,\n", + " 0.11622069047613767,\n", + " 0.11752061004322731,\n", + " 0.11518522937637524,\n", + " 0.12161772840367899]},\n", + " 0.0: {'csvfile': 'EPWs\\\\1axis_0.0.csv',\n", + " 'surf_azm': 90.0,\n", + " 'surf_tilt': 0.0,\n", + " 'datetime': Index(['1971-02-10 13:00:00', '1971-02-11 13:00:00', '1971-02-12 13:00:00',\n", + " '1971-02-13 13:00:00', '1971-02-14 13:00:00', '1971-02-15 13:00:00',\n", + " '1971-02-16 13:00:00', '1971-02-17 13:00:00', '1971-02-18 13:00:00',\n", + " '1971-02-23 07:00:00', '1971-02-24 07:00:00', '1971-02-25 07:00:00',\n", + " '1971-02-26 07:00:00', '1971-02-27 07:00:00', '1971-02-28 07:00:00',\n", + " '1972-03-01 07:00:00', '1967-04-04 06:00:00', '1967-04-05 06:00:00',\n", + " '1967-04-06 06:00:00', '1967-04-07 06:00:00', '1967-04-08 06:00:00',\n", + " '1967-04-09 06:00:00', '1967-04-10 06:00:00', '1967-04-11 06:00:00',\n", + " '1967-04-12 06:00:00', '1974-05-29 05:00:00', '1963-06-01 05:00:00',\n", + " '1963-06-02 05:00:00', '1963-06-03 05:00:00', '1963-06-04 05:00:00',\n", + " '1963-06-05 05:00:00', '1963-06-06 05:00:00', '1963-06-07 05:00:00',\n", + " '1963-06-08 05:00:00', '1963-06-09 05:00:00', '1963-06-10 05:00:00',\n", + " '1963-06-11 05:00:00', '1963-06-12 05:00:00', '1963-06-13 05:00:00',\n", + " '1963-06-14 05:00:00', '1963-06-15 05:00:00', '1963-06-16 05:00:00',\n", + " '1963-06-17 05:00:00', '1963-06-18 05:00:00', '1963-06-19 05:00:00',\n", + " '1963-06-20 05:00:00', '1963-06-21 05:00:00', '1963-06-22 05:00:00',\n", + " '1963-06-23 05:00:00', '1963-06-24 05:00:00', '1963-06-25 05:00:00',\n", + " '1963-06-26 05:00:00', '1963-06-27 05:00:00', '1963-06-30 05:00:00',\n", + " '1965-09-02 06:00:00', '1965-09-03 06:00:00', '1965-09-04 06:00:00',\n", + " '1965-09-05 06:00:00', '1965-09-06 06:00:00', '1965-09-07 06:00:00',\n", + " '1965-09-08 06:00:00', '1965-09-09 06:00:00', '1965-09-10 06:00:00',\n", + " '1965-09-11 06:00:00', '1965-09-12 06:00:00', '1965-09-13 06:00:00',\n", + " '1965-09-14 06:00:00', '1965-09-15 06:00:00', '1960-11-05 07:00:00',\n", + " '1960-11-06 07:00:00', '1960-11-07 07:00:00', '1960-11-08 07:00:00',\n", + " '1960-11-09 07:00:00', '1960-11-10 07:00:00', '1960-11-11 07:00:00',\n", + " '1960-11-12 07:00:00', '1960-11-13 07:00:00', '1960-11-14 07:00:00',\n", + " '1960-11-15 07:00:00', '1960-11-16 07:00:00', '1960-11-17 07:00:00'],\n", + " dtype='object'),\n", + " 'count': 81,\n", + " 'skyfile': 'skies\\\\1axis_0.0.rad',\n", + " 'clearance_height': 2.3,\n", + " 'radfile': 'objects\\\\1axis0.0_2.3_10.60_0.0_20x7_origin0,0.rad',\n", + " 'scene': ,\n", + " 'octfile': '1axis_0.0.oct',\n", + " 'AnalysisObj': ,\n", + " 'Wm2Front': [5028.632,\n", + " 5029.127,\n", + " 5029.374,\n", + " 5029.62,\n", + " 5027.187,\n", + " 5030.112,\n", + " 5030.358,\n", + " 5030.604,\n", + " 5030.851],\n", + " 'Wm2Back': [612.9485666666667,\n", + " 576.2370999999999,\n", + " 567.6235333333334,\n", + " 614.0968333333334,\n", + " 281.3083,\n", + " 602.3714666666667,\n", + " 673.5401333333333,\n", + " 729.3521999999999,\n", + " 795.9221000000001],\n", + " 'backRatio': [0.12189168839059576,\n", + " 0.11457992319940949,\n", + " 0.11286164450519864,\n", + " 0.12209604527524706,\n", + " 0.05595738611724884,\n", + " 0.11975306850296737,\n", + " 0.13389504274612074,\n", + " 0.14498299906273696,\n", + " 0.15820821204837673]},\n", + " -50.0: {'csvfile': 'EPWs\\\\1axis_-50.0.csv',\n", + " 'surf_azm': 90.0,\n", + " 'surf_tilt': 50.0,\n", + " 'datetime': Index(['1971-02-13 09:00:00', '1971-02-14 09:00:00', '1971-02-15 09:00:00',\n", + " '1972-03-05 10:00:00', '1972-03-06 10:00:00', '1972-03-07 10:00:00',\n", + " '1972-03-08 10:00:00', '1972-03-09 10:00:00', '1972-03-10 10:00:00',\n", + " '1972-03-11 10:00:00',\n", + " ...\n", + " '1960-11-05 10:00:00', '1960-11-06 10:00:00', '1960-11-07 10:00:00',\n", + " '1960-11-08 10:00:00', '1960-11-09 10:00:00', '1960-11-10 10:00:00',\n", + " '1960-11-11 10:00:00', '1960-11-27 09:00:00', '1960-11-28 09:00:00',\n", + " '1960-11-29 09:00:00'],\n", + " dtype='object', length=169),\n", + " 'count': 169,\n", + " 'skyfile': 'skies\\\\1axis_-50.0.rad',\n", + " 'clearance_height': 1.036026668853686,\n", + " 'radfile': 'objects\\\\1axis-50.0_1.036_10.60_50.0_20x7_origin0,0.rad',\n", + " 'scene': ,\n", + " 'octfile': '1axis_-50.0.oct',\n", + " 'AnalysisObj': ,\n", + " 'Wm2Front': [77137.75,\n", + " 78127.16,\n", + " 80169.90999999999,\n", + " 80983.41333333333,\n", + " 26217.89333333333,\n", + " 80002.11666666665,\n", + " 80404.38666666667,\n", + " 81641.67333333334,\n", + " 81988.18],\n", + " 'Wm2Back': [9777.404333333334,\n", + " 9056.757333333333,\n", + " 8799.934666666666,\n", + " 8188.417666666667,\n", + " 7086.842,\n", + " 8623.159,\n", + " 8992.714000000002,\n", + " 9279.886333333334,\n", + " 9545.166],\n", + " 'backRatio': [0.1267525200901091,\n", + " 0.11592328733580032,\n", + " 0.10976605258632091,\n", + " 0.10111228001529515,\n", + " 0.27030553674136276,\n", + " 0.10778663429796795,\n", + " 0.11184357298113122,\n", + " 0.11366604628225348,\n", + " 0.11642124369120958]},\n", + " -55.0: {'csvfile': 'EPWs\\\\1axis_-55.0.csv',\n", + " 'surf_azm': 90.0,\n", + " 'surf_tilt': 55.0,\n", + " 'datetime': Index(['1971-02-15 10:00:00', '1971-02-16 09:00:00', '1971-02-16 10:00:00',\n", + " '1971-02-17 09:00:00', '1971-02-17 10:00:00', '1971-02-18 10:00:00',\n", + " '1971-02-19 10:00:00', '1971-02-20 10:00:00', '1971-02-21 10:00:00',\n", + " '1971-02-22 10:00:00',\n", + " ...\n", + " '1960-11-25 09:00:00', '1960-11-25 10:00:00', '1960-11-26 09:00:00',\n", + " '1960-11-26 10:00:00', '1960-11-27 10:00:00', '1960-11-28 10:00:00',\n", + " '1960-11-29 10:00:00', '1960-11-30 10:00:00', '1959-12-01 10:00:00',\n", + " '1959-12-02 10:00:00'],\n", + " dtype='object', length=136),\n", + " 'count': 136,\n", + " 'skyfile': 'skies\\\\1axis_-55.0.rad',\n", + " 'clearance_height': 0.9483991269231634,\n", + " 'radfile': 'objects\\\\1axis-55.0_0.948_10.60_55.0_20x7_origin0,0.rad',\n", + " 'scene': ,\n", + " 'octfile': '1axis_-55.0.oct',\n", + " 'AnalysisObj': ,\n", + " 'Wm2Front': [54354.88333333333,\n", + " 55420.84666666667,\n", + " 56158.14333333333,\n", + " 57019.57,\n", + " 21914.796666666665,\n", + " 57245.79,\n", + " 57623.42333333334,\n", + " 58116.823333333334,\n", + " 58460.350000000006],\n", + " 'Wm2Back': [7161.026999999999,\n", + " 6836.039333333333,\n", + " 6723.502,\n", + " 6674.837666666666,\n", + " 5387.821,\n", + " 6777.660666666667,\n", + " 6966.037666666667,\n", + " 7182.722666666666,\n", + " 7382.473333333334],\n", + " 'backRatio': [0.1317457867463159,\n", + " 0.12334779457812094,\n", + " 0.11972443320227706,\n", + " 0.11706222178814124,\n", + " 0.2458531026364484,\n", + " 0.11839579029778219,\n", + " 0.12088899171229978,\n", + " 0.12359110720623051,\n", + " 0.12628171413704536]},\n", + " 40.0: {'csvfile': 'EPWs\\\\1axis_40.0.csv',\n", + " 'surf_azm': 90.0,\n", + " 'surf_tilt': -40.0,\n", + " 'datetime': Index(['1971-02-15 17:00:00', '1971-02-16 17:00:00', '1971-02-17 17:00:00',\n", + " '1971-02-18 17:00:00', '1971-02-19 17:00:00', '1971-02-20 17:00:00',\n", + " '1971-02-26 15:00:00', '1971-02-27 15:00:00', '1971-02-28 15:00:00',\n", + " '1972-03-01 15:00:00',\n", + " ...\n", + " '1959-12-08 16:00:00', '1959-12-09 14:00:00', '1959-12-09 16:00:00',\n", + " '1959-12-10 14:00:00', '1959-12-10 16:00:00', '1959-12-11 14:00:00',\n", + " '1959-12-11 16:00:00', '1959-12-12 14:00:00', '1959-12-13 14:00:00',\n", + " '1959-12-14 14:00:00'],\n", + " dtype='object', length=135),\n", + " 'count': 135,\n", + " 'skyfile': 'skies\\\\1axis_40.0.rad',\n", + " 'clearance_height': 1.2394004440172102,\n", + " 'radfile': 'objects\\\\1axis40.0_1.239_10.60_-40.0_20x7_origin0,0.rad',\n", + " 'scene': ,\n", + " 'octfile': '1axis_40.0.oct',\n", + " 'AnalysisObj': ,\n", + " 'Wm2Front': [59704.926666666666,\n", + " 59364.98333333334,\n", + " 60818.68333333333,\n", + " 60430.176666666666,\n", + " 14005.356666666667,\n", + " 57400.01333333333,\n", + " 56900.30333333334,\n", + " 57535.32333333333,\n", + " 56862.64333333333],\n", + " 'Wm2Back': [5890.072999999999,\n", + " 5725.839333333333,\n", + " 5573.4439999999995,\n", + " 5419.622333333333,\n", + " 5302.448,\n", + " 5403.502333333333,\n", + " 5908.546333333333,\n", + " 7005.143,\n", + " 6933.764666666667],\n", + " 'backRatio': [0.09865304640991023,\n", + " 0.09645145867777623,\n", + " 0.09164032502665177,\n", + " 0.08968403772082241,\n", + " 0.3786013985647826,\n", + " 0.09413764780534928,\n", + " 0.1038403291961303,\n", + " 0.12175377615698156,\n", + " 0.12193883608402713]},\n", + " -15.0: {'csvfile': 'EPWs\\\\1axis_-15.0.csv',\n", + " 'surf_azm': 90.0,\n", + " 'surf_tilt': 15.0,\n", + " 'datetime': Index(['1971-02-19 08:00:00', '1971-02-20 08:00:00', '1971-02-21 08:00:00',\n", + " '1971-02-22 08:00:00', '1971-02-23 08:00:00', '1971-02-24 08:00:00',\n", + " '1971-02-25 08:00:00', '1971-02-26 08:00:00', '1972-03-04 12:00:00',\n", + " '1972-03-05 12:00:00',\n", + " ...\n", + " '1959-12-12 12:00:00', '1959-12-13 12:00:00', '1959-12-14 12:00:00',\n", + " '1959-12-15 12:00:00', '1959-12-16 12:00:00', '1959-12-17 12:00:00',\n", + " '1959-12-18 12:00:00', '1959-12-19 12:00:00', '1959-12-20 12:00:00',\n", + " '1959-12-21 12:00:00'],\n", + " dtype='object', length=136),\n", + " 'count': 136,\n", + " 'skyfile': 'skies\\\\1axis_-15.0.rad',\n", + " 'clearance_height': 1.8729485755808406,\n", + " 'radfile': 'objects\\\\1axis-15.0_1.872_10.60_15.0_20x7_origin0,0.rad',\n", + " 'scene': ,\n", + " 'octfile': '1axis_-15.0.oct',\n", + " 'AnalysisObj': ,\n", + " 'Wm2Front': [27974.97,\n", + " 27981.170000000002,\n", + " 27986.55,\n", + " 27991.929999999997,\n", + " 20980.62,\n", + " 28723.486666666664,\n", + " 28730.513333333336,\n", + " 28736.63666666667,\n", + " 28742.766666666663],\n", + " 'Wm2Back': [4082.471666666667,\n", + " 3777.878666666667,\n", + " 3566.92,\n", + " 3538.1229999999996,\n", + " 3442.412,\n", + " 3587.0453333333335,\n", + " 3840.0620000000004,\n", + " 4011.4546666666665,\n", + " 4188.954999999999],\n", + " 'backRatio': [0.14593300799727965,\n", + " 0.13501503088154054,\n", + " 0.1274512175508872,\n", + " 0.1263979608980888,\n", + " 0.16407579165554728,\n", + " 0.12488195636131143,\n", + " 0.13365796224346513,\n", + " 0.13959373790343574,\n", + " 0.14573944473892753]},\n", + " -25.0: {'csvfile': 'EPWs\\\\1axis_-25.0.csv',\n", + " 'surf_azm': 90.0,\n", + " 'surf_tilt': 25.0,\n", + " 'datetime': Index(['1972-03-06 08:00:00', '1972-03-07 08:00:00', '1972-03-08 08:00:00',\n", + " '1972-03-09 08:00:00', '1972-03-10 08:00:00', '1972-03-11 08:00:00',\n", + " '1967-04-13 11:00:00', '1967-04-14 11:00:00', '1967-04-15 11:00:00',\n", + " '1967-04-16 11:00:00',\n", + " ...\n", + " '1965-09-01 11:00:00', '1965-09-02 11:00:00', '1969-10-25 08:00:00',\n", + " '1969-10-26 08:00:00', '1969-10-27 08:00:00', '1969-10-28 08:00:00',\n", + " '1969-10-29 08:00:00', '1969-10-30 08:00:00', '1969-10-31 08:00:00',\n", + " '1960-11-01 08:00:00'],\n", + " dtype='object', length=175),\n", + " 'count': 175,\n", + " 'skyfile': 'skies\\\\1axis_-25.0.rad',\n", + " 'clearance_height': 1.6026798681278458,\n", + " 'radfile': 'objects\\\\1axis-25.0_1.602_10.60_25.0_20x7_origin0,0.rad',\n", + " 'scene': ,\n", + " 'octfile': '1axis_-25.0.oct',\n", + " 'AnalysisObj': ,\n", + " 'Wm2Front': [93427.01666666666,\n", + " 93568.45,\n", + " 93708.84000000001,\n", + " 95790.58333333333,\n", + " 62468.80000000001,\n", + " 95944.17333333332,\n", + " 96060.75333333334,\n", + " 96108.64666666667,\n", + " 96155.45666666667],\n", + " 'Wm2Back': [12603.316666666666,\n", + " 11629.426666666666,\n", + " 10942.686666666666,\n", + " 10420.513333333334,\n", + " 9907.856,\n", + " 10702.693333333335,\n", + " 11085.326666666666,\n", + " 11803.786666666667,\n", + " 12391.74],\n", + " 'backRatio': [0.13490012826518102,\n", + " 0.12428790412130117,\n", + " 0.11677325799672056,\n", + " 0.1087843174342887,\n", + " 0.15860486901293333,\n", + " 0.11155125788200107,\n", + " 0.11539912156217606,\n", + " 0.12281711326961653,\n", + " 0.12887193614071613]}}" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "albedo = 0.25\n", + "lat = 37.5\n", + "lon = -77.6\n", + "nMods = 20\n", + "nRows = 7\n", + "hub_height = 2.3\n", + "gcr = 0.33\n", + "moduletype = 'Custom Tracker Module' # this must already exist since we are not calling makeModule in this CONDENSED example.\n", + "testfolder = r'C:\\Users\\sayala\\Documents\\RadianceScenes\\Tutorials\\Journal2'\n", + "limit_angle = 60\n", + "angeldelta = 5\n", + "backtrack = True\n", + "gcr = gcr\n", + "modWanted = 9\n", + "rowWanted = 2\n", + "cumulativesky = True\n", + "\n", + "import bifacial_radiance\n", + "demo = RadianceObj(path = testfolder) \n", + "demo.setGround(albedo)\n", + "epwfile = demo.getEPW(lat,lon) \n", + "metdata = demo.readEPW(epwfile)\n", + "demo.set1axis(limit_angle = limit_angle, backtrack = backtrack, gcr = gcr, cumulativesky = cumulativesky)\n", + "demo.genCumSky1axis()\n", + "sceneDict = {'gcr': gcr,'height':hub_height, 'nMods': nMods, 'nRows': nRows} # orientation deprecated on v.0.2.4.\n", + "demo.makeScene1axis(moduletype=moduletype,sceneDict = sceneDict)\n", + "demo.makeOct1axis()\n", + "demo.analysis1axis(modWanted = modWanted, rowWanted = rowWanted)\n" + ] + } + ], + "metadata": { + "anaconda-cloud": {}, + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.3" + } + }, + "nbformat": 4, + "nbformat_minor": 1 +} diff --git a/docs/tutorials/2 - Introductory Example - Single Axis Tracking with cumulative Sky.py b/docs/tutorials/2 - Introductory Example - Single Axis Tracking with cumulative Sky.py new file mode 100644 index 00000000..78a1182e --- /dev/null +++ b/docs/tutorials/2 - Introductory Example - Single Axis Tracking with cumulative Sky.py @@ -0,0 +1,347 @@ +#!/usr/bin/env python +# coding: utf-8 + +# ## 2 - Introductory Example - 1-Axis tracker with cumulative sky +# +# Method Gencumsky has been modified to divide the yearly-cumulative sky into various skies, each one representing the cumulative irradiance for the hours at which the tracker is at a certain angle. For faster running, for a tracker that moves between 45 and -45 degrees limit angle, if only positions every 5 degrees are considered (45, 40, 35 .... -4-, -45), then only 18 skies (and 18 simulations) will be run for the whole year. +# +# ![Example of the hemisphere cumulative sky](../images_wiki/Journal2Pics/tracking_cumulativesky.png) +# +# +# This procedure was presented in: +# +# Ayala Pelaez S, Deline C, Greenberg P, Stein JS, Kostuk RK. Model and validation of single-axis tracking with bifacial PV. IEEE J Photovoltaics. 2019;9(3):715–21. https://ieeexplore.ieee.org/document/8644027 and https://www.nrel.gov/docs/fy19osti/72039.pdf (pre-print, conference version) +# +# +# ### Steps: +#
    +#
  1. Create a folder for your simulation, and load bifacial_radiance
  2. +#
  3. Create a Radiance Object
  4. +#
  5. Set the Albedo
  6. +#
  7. Download Weather Files
  8. +#
      (VERY SIMILAR TO FIXED TILT EXAMPLE UNTIL HERE)
    +#
  9. Set Tracking Angles
  10. +#
  11. Generate the Sky
  12. +#
  13. Define a Module type
  14. +#
  15. Create the scene
  16. +#
  17. Combine Ground, Sky and Scene Objects
  18. +#
  19. Analyze and get results
  20. +#
  21. Clean Results
  22. +# +#
+# +# And finally: + +# + +# +# ## 1. Create a folder for your simulation, and load bifacial_radiance +# +# First let's set the folder where the simulation will be saved. By default, this is the TEMP folder in the bifacial_radiance distribution. +# +# The lines below find the location of the folder relative to this Jupyter Journa. You can alternatively point to an empty directory (it will open a load GUI Visual Interface) or specify any other directory in your computer, for example: +# +# #### testfolder = r'C:\Users\sayala\Documents\RadianceScenes\Tutorials\Journal2' +# +# + +# In[1]: + + +import os +testfolder = os.path.abspath(r'..\..\bifacial_radiance\TEMP') + +print ("Your simulation will be stored in %s" % testfolder) + + +# This will load bifacial_radiance and other libraries from python that will be useful for this Jupyter Journal: + +# In[2]: + + +from bifacial_radiance import * +import numpy as np + + +# + +# ## 2. Create a Radiance Object + +# In[3]: + + +# Create a RadianceObj 'object' named bifacial_example. no whitespace allowed +demo = RadianceObj('bifacial_tracking_example', path = testfolder) + + +# This will create all the folder structure of the bifacial_radiance Scene in the designated testfolder in your computer, and it should look like this: +# +# +# ![Folder Structure](../images_wiki/Journal1Pics/folderStructure.png) + +# + +# ## 3. Set the Albedo + +# To see more options of ground materials available (located on ground.rad), run this function without any input. + +# In[4]: + + +# Input albedo number or material name like 'concrete'. +demo.setGround() # This prints available materials. + + +# If a number between 0 and 1 is passed, it assumes it's an albedo value. For this example, we want a natural-ground albedo value, so we'll use 0.25 + +# In[5]: + + +albedo = 0.25 +demo.setGround(albedo) + + +# + +# ## 4. Download and Load Weather Files +# +# There are various options provided in bifacial_radiance to load weatherfiles. getEPW is useful because you just set the latitude and longitude of the location and it donwloads the meteorologicla data for any location. + +# In[6]: + + +# Pull in meteorological data using pyEPW for any global lat/lon +epwfile = demo.getEPW(lat = 37.5, lon = -77.6) # This location corresponds to Richmond, VA. + + +# The downloaded EPW will be in the EPWs folder. +# +# To load the data, use readWeatherFile. This reads EPWs, TMY meterological data, or even your own data as long as it follows TMY data format (With any time resoultion). + +# In[7]: + + +# Read in the weather data pulled in above. +metdata = demo.readWeatherFile(weatherFile = epwfile) + + +# ## TRACKING Workflow + +# + +# Until now, all the steps looked the same from Tutorial 1. The following section follows similar steps, but the functions are specific for working with single axis tracking. +# +# ## 5. Set Tracking Angles +# +# This function will read the weather file, and based on the sun position it will calculate the angle the tracker should be at for each hour. It will create metdata files for each of the tracker angles considered. + +# In[8]: + + +limit_angle = 5 # tracker rotation limit angle. Setting it ridiculously small so this runs faster. +angledelta = 5 # sampling between the limit angles. +backtrack = True +gcr = 0.33 +cumulativesky = True # This is important for this example! +trackerdict = demo.set1axis(metdata = metdata, limit_angle = limit_angle, backtrack = backtrack, + gcr = gcr, cumulativesky = cumulativesky) + + +# Setting backtrack to True is important in this step, so the trackers correct for self-shading when following the sun at high zenith angles. + +# + +# ## 6. Generate the Sky +# +# This will create the skies for each sub-metdata file created by set1axis. +# + +# In[9]: + + +trackerdict = demo.genCumSky1axis(trackerdict = trackerdict) + + +# This is how one of the cumulative sky .cal files associated with each .rad file generated look like: +# +# ![Example of the gencumsky1axis](../images_wiki/Journal2Pics/gencumsky1axis_example_file_structure_and_contents.png) +# +# +# Each of the values corresponds to the cumulative rradiance of one of those patches, for when the tracker is at that specific angle through the year. + +# + +# ## 7. Define the Module type +# +# Let's make a more interesting module in this example. Let's do 2-up configuration in portrait, with the modules rotating around a 10 centimeter round torque tube. Let's add a gap between the two modules in 2-UP of 10 centimeters, as well as gap between the torque tube and the modules of 5 centimeters. Along the row, the modules are separated only 2 centimeters for this example. The torquetube is painted Metal_Grey in this example (it's one of the materials available in Ground.rad, and it is 40% reflective). +# + +# In[10]: + + +x = 0.984 # meters +y = 1.7 # meters +moduletype = 'Custom Tracker Module' +torquetube = True +tubetype = 'round' +diameter = 0.1 # diameter of the torque tube +numpanels = 2 +axisofrotationTorqueTube = True +zgap = 0.05 +ygap = 0.10 +xgap = 0.02 +material = 'Metal_Grey' + +demo.makeModule(name = moduletype, x = x, y = y, + torquetube = torquetube, tubetype = tubetype, material = material, + diameter = diameter, xgap = xgap, ygap = ygap, zgap = zgap, + numpanels = numpanels, axisofrotationTorqueTube = axisofrotationTorqueTube) + + +# + +# ## 8. Make the Scene +# +# The scene Dictionary specifies the information of the scene. For tracking, different input parameters are expected in the dictionary, such as number of rows, number of modules per row, row azimuth, hub_height (distance between the axis of rotation of the modules and the ground). + +# In[11]: + + +hub_height = 2.3 +sceneDict = {'gcr': gcr,'hub_height':hub_height, 'nMods': 20, 'nRows': 7} + + +# To make the scene we have to create a Scene Object through the method makeScene1axis. This method will create a .rad file in the objects folder, with the parameters specified in sceneDict and the module created above. + +# In[12]: + + +trackerdict = demo.makeScene1axis(trackerdict = trackerdict, moduletype = moduletype, sceneDict = sceneDict) + + +# + +# ## 9. Combine Ground, Sky and Scene Objects +# +# makeOct1axis joins the sky.rad file, ground.rad file, and the geometry.rad files created in makeScene. + +# In[13]: + + +trackerdict = demo.makeOct1axis(trackerdict = trackerdict) + + +# + +# ## 10. Analyze and get results +# +# We can choose to analyze any module in the Scene we have created. The default, if no modWanted or rowWanted is passed, is to sample the center module of the center row. +# +# For this example we will sample row 2, module 9. + +# In[14]: + + +modWanted = 9 +rowWanted = 2 +customname = '_Row_2_Module_09' # This is useful if we want to do various analysis. +trackerdict = demo.analysis1axis(trackerdict, modWanted=9, rowWanted = 2, customname=customname) + + +# Let's look at the results with more detail. The analysis1axis routine created individual result .csv files for each angle, as well as one cumulative result .csv where the irradiance is added by sensor. +# + +# In[15]: + + +results = load.read1Result('cumulative_results__Row_2_Module_09.csv') +results + + +# There are various things to notice: +# +# I. The materials column has a specific format that will tell you if you are sampling the correct module: +# +# a{ModWanted}.{rowWanted}.a{numPanel}.{moduletype}.material_key +# +# * Since for this journal numPanels = 2, numPanel can either be 0 or 1, for the East-most and West-most module in the collector width. +# * numPanel, ModWanted and RowWanted are indexed starting at 0 in the results. +# * material_key is from the surface generated inside radiance. Usually it is 6457 for top surface of hte module and .2310 for the bottom one. +# +# II. Sensors sample always in the same direction. For this N-S aligned tracker, that is East-most to West. For this 2-up portrait tracker which is 3.5 meters, 20x7 rows and we are sampling module 9 on row 2, the East to West sampling goes from 22.6 m to 19.81 m = 2.79m. It is not exatly 3.5 because the sensors are spaced evenly through the collector width (CW): +# +# +# ![Sensors spaced along collector width](../images_wiki/Journal2Pics/spaced_sensors.png) +# +# III. When there is a ygap in the collector width (2-UP or more configuration), some of the sensors might end up sampling the torque tube, or the sky. You can ses that in the materials columns. This also happens if the number of sensors is quite high, the edges of the module might be sampled instead of the sensors. For this reason, before calculating bifacial gain these results must be cleaned. For more advanced simulations, make sure you clean each result csv file individually. We provide some options on load.py but some are very use-specific, so you might have to develop your own cleaning tool (or let us know on issues!) +# +#
+# Important: If you have torquetubes and y-gap values, make sure you clean your results. +#
+# + +# + +# ## 11. Clean Results +# +# We have two options for cleaning results. The simples on is load.cleanResults, but there is also a deepClean for specific purposes. +# +# cleanResults will find materials that should not have values and set them to NaN. + +# In[16]: + + +results_clean = load.cleanResult(results) +results_clean + + +# These are the total irradiance values over all the hours of the year that the module at each sampling point will receive. Dividing the back irradiance average by the front irradiance average will give us the bifacial gain for the year: +# +# ![Bifacial Gain in Irradiance Formula](../images_wiki/Journal1Pics/BGG_Formula.png) +# +# Assuming that our module from Prism Solar has a bifaciality factor (rear to front performance) of 90%, our bifacial gain is of: + +# In[17]: + + +bifacialityfactor = 0.9 +print('Annual bifacial ratio: %0.3f ' %( np.nanmean(results_clean.Wm2Back) * bifacialityfactor / np.nanmean(results_clean.Wm2Front)) ) + + +# + +# ## CONDENSED VERSION +# Everything we've done so far in super short condensed version: + +# In[18]: + + +albedo = 0.25 +lat = 37.5 +lon = -77.6 +nMods = 20 +nRows = 7 +hub_height = 2.3 +gcr = 0.33 +moduletype = 'Custom Tracker Module' # this must already exist since we are not calling makeModule in this CONDENSED example. +testfolder = r'C:\Users\sayala\Documents\RadianceScenes\Tutorials\Journal2' +limit_angle = 60 +angeldelta = 5 +backtrack = True +gcr = gcr +modWanted = 9 +rowWanted = 2 +cumulativesky = True + +import bifacial_radiance +demo = RadianceObj(path = testfolder) +demo.setGround(albedo) +epwfile = demo.getEPW(lat,lon) +metdata = demo.readEPW(epwfile) +demo.set1axis(limit_angle = limit_angle, backtrack = backtrack, gcr = gcr, cumulativesky = cumulativesky) +demo.genCumSky1axis() +sceneDict = {'gcr': gcr,'height':hub_height, 'nMods': nMods, 'nRows': nRows} # orientation deprecated on v.0.2.4. +demo.makeScene1axis(moduletype=moduletype,sceneDict = sceneDict) +demo.makeOct1axis() +demo.analysis1axis(modWanted = modWanted, rowWanted = rowWanted) + diff --git a/docs/tutorials/3 - Medium Level Example - Single Axis Tracking - hourly.ipynb b/docs/tutorials/3 - Medium Level Example - Single Axis Tracking - hourly.ipynb new file mode 100644 index 00000000..1697cc4b --- /dev/null +++ b/docs/tutorials/3 - Medium Level Example - Single Axis Tracking - hourly.ipynb @@ -0,0 +1,1172 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 3 - Medium Level Example - 1-Axis tracker by hour (gendaylit)\n", + "\n", + "Example demonstrating the use of doing hourly smiulations with Radiance gendaylit for 1-axis tracking. This is a medium level example because it also explores a couple subtopics:\n", + "\n", + "#### Subtopics:\n", + "
    \n", + "
  • The structure of the tracker dictionary \"trackerDict\".
  • \n", + "
  • How to calculate GCR
  • \n", + "
  • How to make a cell-level module
  • \n", + "
  • Various methods to use the trackerdictionary for analysis.
  • \n", + "
\n", + " \n", + "#### Doing full year simulations with gendaylit: \n", + "\n", + "Performs the simulation hour by hour requires either a good computer or some patience, since there are ~4000 daylight-hours in the year. With a 32GB RAM, Windows 10 i7-8700 CPU @ 3.2GHz with 6 cores this takes 1 day. The code also allows for multiple cores or HPC use -- there is documentation/examples inside the software at the moment, but that is an advanced topic. The procedure can be broken into shorter steps for one day or a single timestamp simulation which is exemplified below.\n", + "\n", + "### Steps:\n", + "
    \n", + "
  1. Load bifacial_radiance
  2. \n", + "
  3. Define all your system variables
  4. \n", + "
  5. Create Radiance Object, Set Albedo and Weather
  6. \n", + "
  7. Make Module: Cell Level Module Example
  8. \n", + "
  9. Calculate GCR
  10. \n", + "
  11. Set Tracking Angles
  12. \n", + "
  13. Generate the Sky
  14. \n", + "
  15. Make Scene 1axis
  16. \n", + "
    1. Make Oct and AnalyzE 1 HOUR
    2. \n", + "
    3. Make Oct and Analye Range of Hours
    4. \n", + "
    5. Make Oct and Analyze All Tracking Dictionary
    \n", + "
\n", + "\n", + "And finally: " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "## 1. Load bifacial_radiance \n", + "\n", + "#### Pay attention: different importing method:\n", + "\n", + "So far we've used \"from bifacial_radiance import *\" to import all the bifacial_radiance files into our working space in jupyter. For this journal we will do a \"import bifacial_radiance\" . This method of importing requires a different call for some functions as you'll see below. For example, instead of calling demo = RadianceObj(path = testfolder) as on Tutorial 2, in this case we will neeed to do demo = bifacial_radiance.RadianceObj(path = testfolder). " + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "import bifacial_radiance\n", + "import numpy as np\n", + "import os # this operative system to do teh relative-path testfolder for this example.\n", + "import pprint # We will be pretty-printing the trackerdictionary throughout to show its structure." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 2. Define all your system variables\n", + "\n", + "Just like in the condensed version show at the end of Tutorial 2, for this tutorial we will be starting all of our system variables from the beginning of the jupyter journal, instead than throughout the different cells (for the most part)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "simulationName = 'Tutorial 3' # For adding a simulation name when defning RadianceObj. This is optional.\n", + "moduletype = 'Custom Cell-Level Module' # We will define the parameters for this below in Step 4.\n", + "testfolder = os.path.abspath(r'..\\..\\bifacial_radiance\\TEMP')\n", + "albedo = \"litesoil\" # this is one of the options on ground.rad\n", + "lat = 37.5 \n", + "lon = -77.6\n", + "\n", + "# Scene variables\n", + "nMods = 20\n", + "nRows = 7\n", + "hub_height = 2.3 # meters\n", + "pitch = 10 # meters # We will be using pitch instead of GCR for this example.\n", + "\n", + "# Traking parameters\n", + "cumulativesky = False\n", + "limit_angle = 45 # tracker rotation limit angle\n", + "angledelta = 0.01 # we will be doing hourly simulation, we want the angle to be as close to real tracking as possible.\n", + "backtrack = True \n", + "\n", + "#makeModule parameters\n", + "# x and y will be defined later on Step 4 for this tutorial!!\n", + "xgap = 0.01\n", + "ygap = 0.10\n", + "zgap = 0.05\n", + "numpanels = 2\n", + "torquetube = True\n", + "axisofrotationTorqueTube = False\n", + "diameter = 0.1\n", + "tubetype = 'Oct' # This will make an octagonal torque tube.\n", + "material = 'black' # Torque tube of this material (0% reflectivity)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 3. Create Radiance Object, Set Albedo and Weather\n", + "\n", + "Same steps as previous two tutorials, so condensing it into one step. You hopefully have this down by now! :)\n", + "\n", + "\n", + "
\n", + "Notice that we are doing bifacial_radiance.RadianceObj because we change the import method for this example!\n", + "
" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "path = C:\\Users\\sayala\\Documents\\RadianceScenes\\Tutorials\\Journal2\n", + "Getting weather file: USA_VA_Richmond.Intl.AP.724010_TMY.epw\n", + " ... OK!\n" + ] + } + ], + "source": [ + "demo = bifacial_radiance.RadianceObj(simulationName, path = testfolder) # Adding a simulation name. This is optional.\n", + "demo.setGround(albedo) \n", + "epwfile = demo.getEPW(lat = lat, lon = lon) \n", + "metdata = demo.readWeatherFile(weatherFile = epwfile) \n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 4. Make Module: Cell Level Module Example\n", + "\n", + "Instead of doing a opaque, flat single-surface module, in this tutorial we will create a module made up by cells. We can define variuos parameters to make a cell-level module, such as cell size and spacing between cells. To do this, we will pass a dicitonary with the needed parameters to makeModule, as shown below.\n", + "\n", + "
\n", + "Since we are passing a cell-level dictionary, the values for module's x and y of the module will be calculated by the software -- no need to pass them (and if you do, they'll just get ignored)\n", + "
" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Module Name: Custom_Cell-Level_Module\n", + "REWRITING pre-existing module file. \n", + "Module was shifted by 0.078 in X to avoid sensors on air\n", + "This is a Cell-Level detailed module with Packaging Factor of 0.81 %\n", + "Module Custom Cell-Level Module successfully created\n" + ] + } + ], + "source": [ + "numcellsx = 6\n", + "numcellsy = 12\n", + "xcell = 0.156\n", + "ycell = 0.156\n", + "xcellgap = 0.02\n", + "ycellgap = 0.02\n", + "\n", + "cellLevelModuleParams = {'numcellsx': numcellsx, 'numcellsy':numcellsy, \n", + " 'xcell': xcell, 'ycell': ycell, 'xcellgap': xcellgap, 'ycellgap': ycellgap}\n", + "\n", + "mymodule = demo.makeModule(name=moduletype, torquetube=torquetube, diameter=diameter, tubetype=tubetype, material=material, \n", + " xgap=xgap, ygap=ygap, zgap=zgap, numpanels=numpanels, \n", + " cellLevelModuleParams=cellLevelModuleParams, \n", + " axisofrotationTorqueTube=axisofrotationTorqueTube)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 5. Calculate GCR\n", + "\n", + "In this example we passed the parameter \"pitch\". Pitch is the spacing between rows (for example, between hub-posts) in a field.\n", + "To calculate Ground Coverage Ratio (GCR), we must relate the pitch to the collector-width by:\n", + " \n", + "![GCR = CW / pitch](../images_wiki/Journal3Pics/Equation_GCR.png)\n", + "\n", + "The collector width for our system must consider the number of panels and the y-gap:\n", + " \n", + "![CW](../images_wiki/Journal3Pics/Equation_CW.png)\n", + " \n", + "Collector Width gets saved in your module parameters (and later on your scene and trackerdict) as \"sceney\". You can calculate your collector width with the equation, or you can use this method to know your GCR:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The GCR is : 0.4284\n" + ] + } + ], + "source": [ + "# For more options on makemodule, see the help description of the function. \n", + "CW = mymodule['sceney']\n", + "gcr = CW / pitch\n", + "print (\"The GCR is :\", gcr)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 6. Set Tracking Angles\n", + "\n", + "This function will read the weather file, and based on the sun position it will calculate the angle the tracker should be at for each hour. It will create metdata files for each of the tracker angles considered.\n", + "\n", + "For doing hourly simulations, remember to set **cumulativesky = False** here!" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "C:\\ProgramData\\Anaconda3\\lib\\site-packages\\pvlib\\tracking.py:449: RuntimeWarning: invalid value encountered in arccos\n", + " wc = np.degrees(np.arccos(temp))\n", + "C:\\ProgramData\\Anaconda3\\lib\\site-packages\\pvlib\\tracking.py:473: RuntimeWarning: invalid value encountered in arccos\n", + " aoi = np.degrees(np.arccos(np.abs(np.sum(sun_vec*panel_norm, axis=0))))\n", + "C:\\ProgramData\\Anaconda3\\lib\\site-packages\\pvlib\\tracking.py:568: RuntimeWarning: invalid value encountered in remainder\n", + " surface_azimuth = surface_azimuth % 360\n", + "C:\\ProgramData\\Anaconda3\\lib\\site-packages\\pvlib\\tracking.py:572: RuntimeWarning: invalid value encountered in arccos\n", + " surface_tilt = 90 - np.degrees(np.arccos(dotproduct))\n" + ] + } + ], + "source": [ + "trackerdict = demo.set1axis(metdata = metdata, limit_angle = limit_angle, backtrack = backtrack, \n", + " gcr = gcr, cumulativesky = cumulativesky)" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Full trackerdict for all the year created by set1axis: 4245 \n" + ] + } + ], + "source": [ + "print (\"Full trackerdict for all the year created by set1axis: %s \" % (len(demo.trackerdict))) \n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "set1axis initializes the trackerdictionary Trackerdict. Trackerdict contains all hours in the year as keys. For example: trackerdict['12_16_08']. It is a return variable on many of the 1axis functions, but it is also stored inside of your Radiance Obj (i.e. demo.trackerdict). In this journal we are storing it as a variable to mute the option (otherwise it prints the returned trackerdict contents every time)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'dhi': 18, 'ghi': 18, 'surf_azm': 90.0, 'surf_tilt': 4.61, 'theta': -4.61}\n" + ] + } + ], + "source": [ + "pprint.pprint(trackerdict['12_16_08'])\n" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'dhi': 18, 'ghi': 18, 'surf_azm': 90.0, 'surf_tilt': 4.61, 'theta': -4.61}\n" + ] + } + ], + "source": [ + "pprint.pprint(demo.trackerdict['12_16_08'])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "All of the following functions add up elements to trackerdictionary to keep track (ba-dum tupzz) of the Scene and simulation parameters. In advanced journals we will explore the inner structure of trackerdict. For now, just now it exists :)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 7. Generate the Sky\n", + "\n", + "\n", + "We will create skies for each hour we want to model with the function gendaylit1axis. \n", + "\n", + "**If you don't specify a startdate and enddate, all the year will be created, which will take more time.**\n", + "\n", + "For this example we are doing just two days in January. Format has to be a 'MM_DD' or 'MM/DD'" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Creating ~4000 skyfiles. Takes 1-2 minutes\n", + "Created 20 skyfiles in /skies/\n" + ] + } + ], + "source": [ + "startdate = '01/13' \n", + "enddate = '01/14'\n", + "trackerdict = demo.gendaylit1axis(startdate=startdate, enddate=enddate) " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Since we passed startdate and enddate to gendaylit, it will prune our trackerdict to only the desired days.\n", + "Let's explore our trackerdict:" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Trimmed trackerdict by gendaylit1axis to start and enddate length: 20 \n", + "\n", + "Option of hours are: ['01_13_08', '01_13_09', '01_13_10', '01_13_11', '01_13_12', '01_13_13', '01_13_14', '01_13_15', '01_13_16', '01_13_17', '01_14_08', '01_14_09', '01_14_10', '01_14_11', '01_14_12', '01_14_13', '01_14_14', '01_14_15', '01_14_16', '01_14_17']\n", + "\n", + "Contents of trackerdict for sample hour:\n", + "{'dhi': 15,\n", + " 'ghi': 15,\n", + " 'skyfile': 'skies\\\\sky2_37.5_-77.33_01_13_08.rad',\n", + " 'surf_azm': 90.0,\n", + " 'surf_tilt': 3.62,\n", + " 'theta': -3.62}\n" + ] + } + ], + "source": [ + "print (\"\\nTrimmed trackerdict by gendaylit1axis to start and enddate length: %s \" % (len(trackerdict)))\n", + "print (\"\")\n", + "trackerkeys = sorted(trackerdict.keys())\n", + "print (\"Option of hours are: \", trackerkeys)\n", + "print (\"\")\n", + "print (\"Contents of trackerdict for sample hour:\")\n", + "pprint.pprint(trackerdict[trackerkeys[0]])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 8. Make Scene 1axis\n", + "\n", + "We can use gcr or pitch fo our scene dictionary." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Making ~20 .rad files for gendaylit 1-axis workflow (this takes a minute..)\n", + "20 Radfiles created in /objects/\n" + ] + } + ], + "source": [ + "# making the different scenes for the 1-axis tracking for the dates in trackerdict2.\n", + "\n", + "sceneDict = {'pitch': pitch,'hub_height':hub_height, 'nMods':nMods, 'nRows': nRows} \n", + "\n", + "trackerdict = demo.makeScene1axis(trackerdict = trackerdict, moduletype = moduletype, sceneDict = sceneDict) " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The scene parameteres are now stored in the trackerdict. To view them and to access them:\n", + " " + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'clearance_height': 2.1647564917391366,\n", + " 'dhi': 15,\n", + " 'ghi': 15,\n", + " 'radfile': 'objects\\\\1axis01_13_08_2.164_10_3.62_20x7_origin0,0.rad',\n", + " 'scene': ,\n", + " 'skyfile': 'skies\\\\sky2_37.5_-77.33_01_13_08.rad',\n", + " 'surf_azm': 90.0,\n", + " 'surf_tilt': 3.62,\n", + " 'theta': -3.62}\n" + ] + } + ], + "source": [ + "pprint.pprint(trackerdict[trackerkeys[0]])" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'bifi': 1,\n", + " 'gcr': 0.4284,\n", + " 'moduleDict': {'bifi': 1,\n", + " 'cellModule': {'numcellsx': 6,\n", + " 'numcellsy': 12,\n", + " 'xcell': 0.156,\n", + " 'xcellgap': 0.02,\n", + " 'ycell': 0.156,\n", + " 'ycellgap': 0.02},\n", + " 'modulefile': 'objects\\\\Custom_Cell-Level_Module.rad',\n", + " 'numpanels': 2,\n", + " 'offsetfromaxis': 0,\n", + " 'scenex': 1.046,\n", + " 'sceney': 4.284,\n", + " 'scenez': 0.1,\n", + " 'text': '! genbox black cellPVmodule 0.156 0.156 0.02 | xform '\n", + " '-t -0.44 -2.142 0 -a 6 -t 0.176 0 0 -a 12 -t 0 0.176 '\n", + " '0 -a 2 -t 0 2.192 0\\r\\n'\n", + " '! genbox black octtube1a 1.046 0.04142135623730951 '\n", + " '0.1 | xform -t -0.523 -0.020710678118654756 '\n", + " '-0.15000000000000002\\r\\n'\n", + " '! genbox black octtube1b 1.046 0.04142135623730951 '\n", + " '0.1 | xform -t -0.445 -0.020710678118654756 -0.05 -rx '\n", + " '45 -t 0 0 -0.1\\r\\n'\n", + " '! genbox black octtube1c 1.046 0.04142135623730951 '\n", + " '0.1 | xform -t -0.445 -0.020710678118654756 -0.05 -rx '\n", + " '90 -t 0 0 -0.1\\r\\n'\n", + " '! genbox black octtube1d 1.046 0.04142135623730951 '\n", + " '0.1 | xform -t -0.445 -0.020710678118654756 -0.05 -rx '\n", + " '135 -t 0 0 -0.1 ',\n", + " 'torquetube': {'bool': True,\n", + " 'diameter': 0.1,\n", + " 'material': 'black',\n", + " 'tubetype': 'Oct'},\n", + " 'x': 1.036,\n", + " 'xgap': 0.01,\n", + " 'y': 2.092,\n", + " 'ygap': 0.1,\n", + " 'zgap': 0.05},\n", + " 'modulefile': 'objects\\\\Custom_Cell-Level_Module.rad',\n", + " 'moduletype': 'Custom Cell-Level Module',\n", + " 'offsetfromaxis': 0,\n", + " 'radfiles': 'objects\\\\1axis01_13_13_2.097_10_-5.43_20x7_origin0,0.rad',\n", + " 'scene': ,\n", + " 'sceneDict': {'axis_tilt': 0,\n", + " 'azimuth': 90.0,\n", + " 'clearance_height': 2.0973034494498664,\n", + " 'nMods': 20,\n", + " 'nRows': 7,\n", + " 'originx': 0,\n", + " 'originy': 0,\n", + " 'pitch': 10,\n", + " 'tilt': -5.43},\n", + " 'scenex': 1.046,\n", + " 'sceney': 4.284,\n", + " 'text': '!xform -rx -5.43 -t 0 0 2.3 -a 20 -t 1.046 0 0 -a 7 -t 0 10 0 -i 1 '\n", + " '-t -9.414 -30.0 0 -rz 90.0 -t 0 0 0 '\n", + " 'objects\\\\Custom_Cell-Level_Module.rad',\n", + " 'x': 1.036,\n", + " 'y': 2.092}\n" + ] + } + ], + "source": [ + "pprint.pprint(demo.trackerdict[trackerkeys[5]]['scene'].__dict__)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 9. Make Oct and Analyze \n", + "\n", + "### A. Make Oct and Analyze 1Hour\n", + "\n", + "There are various options now to analyze the trackerdict hours we have defined. We will start by doing just one hour, because it's the fastest. Make sure to select an hour that exists in your trackerdict!\n", + "\n", + "Options of hours:" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "['01_13_08',\n", + " '01_13_09',\n", + " '01_13_10',\n", + " '01_13_11',\n", + " '01_13_12',\n", + " '01_13_13',\n", + " '01_13_14',\n", + " '01_13_15',\n", + " '01_13_16',\n", + " '01_13_17',\n", + " '01_14_08',\n", + " '01_14_09',\n", + " '01_14_10',\n", + " '01_14_11',\n", + " '01_14_12',\n", + " '01_14_13',\n", + " '01_14_14',\n", + " '01_14_15',\n", + " '01_14_16',\n", + " '01_14_17']\n" + ] + } + ], + "source": [ + "pprint.pprint(trackerkeys)" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Making 1 octfiles in root directory.\n", + "Created 1axis_01_13_08.oct\n", + "Linescan in process: 1axis_01_13_08_Front\n", + "Linescan in process: 1axis_01_13_08_Back\n", + "Saved: results\\irr_1axis_01_13_08.csv\n", + "Index: 01_13_08. Wm2Front: 14.201547962962962. Wm2Back: 2.0749953333333333\n", + "\n", + "Saving Cumulative results\n", + "Saved: results\\cumulative_results_.csv\n", + "\n", + "\n", + "Hourly bifi gain: 0.146\n" + ] + } + ], + "source": [ + "demo.makeOct1axis(singleindex='01_13_08')\n", + "results = demo.analysis1axis(singleindex='01_13_08')\n", + "print('\\n\\nHourly bifi gain: {:0.3}'.format(sum(demo.Wm2Back) / sum(demo.Wm2Front)))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The trackerdict now contains information about the octfile, as well as the Analysis Object results" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + " Contents of trackerdict for sample hour after analysis1axis: \n", + "{'AnalysisObj': ,\n", + " 'Wm2Back': [1.8268360000000001,\n", + " 1.75552,\n", + " 1.6788656666666666,\n", + " 1.60315,\n", + " 4.910752,\n", + " 1.6119296666666667,\n", + " 1.700885666666667,\n", + " 1.7575400000000003,\n", + " 1.829479],\n", + " 'Wm2Front': [14.864776666666666,\n", + " 14.865536666666666,\n", + " 14.866266666666666,\n", + " 14.866996666666667,\n", + " 8.873918333333334,\n", + " 14.869313333333332,\n", + " 14.869183333333334,\n", + " 14.86904,\n", + " 14.868900000000002],\n", + " 'backRatio': [0.12288870208149234,\n", + " 0.11808533751752538,\n", + " 0.11292362640072823,\n", + " 0.10782555551644596,\n", + " 0.5533292606823987,\n", + " 0.1083991729382972,\n", + " 0.11438229297778219,\n", + " 0.1181933606096554,\n", + " 0.12303236739991526],\n", + " 'clearance_height': 2.1647564917391366,\n", + " 'dhi': 15,\n", + " 'ghi': 15,\n", + " 'octfile': '1axis_01_13_08.oct',\n", + " 'radfile': 'objects\\\\1axis01_13_08_2.164_10_3.62_20x7_origin0,0.rad',\n", + " 'scene': ,\n", + " 'skyfile': 'skies\\\\sky2_37.5_-77.33_01_13_08.rad',\n", + " 'surf_azm': 90.0,\n", + " 'surf_tilt': 3.62,\n", + " 'theta': -3.62}\n" + ] + } + ], + "source": [ + "print (\"\\n Contents of trackerdict for sample hour after analysis1axis: \")\n", + "pprint.pprint(trackerdict[trackerkeys[0]])\n" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'Wm2Back': [1.8268360000000001,\n", + " 1.75552,\n", + " 1.6788656666666666,\n", + " 1.60315,\n", + " 4.910752,\n", + " 1.6119296666666667,\n", + " 1.700885666666667,\n", + " 1.7575400000000003,\n", + " 1.829479],\n", + " 'Wm2Front': [14.864776666666666,\n", + " 14.865536666666666,\n", + " 14.866266666666666,\n", + " 14.866996666666667,\n", + " 8.873918333333334,\n", + " 14.869313333333332,\n", + " 14.869183333333334,\n", + " 14.86904,\n", + " 14.868900000000002],\n", + " 'backRatio': [0.12288870208149234,\n", + " 0.11808533751752538,\n", + " 0.11292362640072823,\n", + " 0.10782555551644596,\n", + " 0.5533292606823987,\n", + " 0.1083991729382972,\n", + " 0.11438229297778219,\n", + " 0.1181933606096554,\n", + " 0.12303236739991526],\n", + " 'mattype': ['a9.3.a2.2.0.cellPVmodule.6457',\n", + " 'a9.3.a2.4.0.cellPVmodule.6457',\n", + " 'a9.3.a2.7.0.cellPVmodule.6457',\n", + " 'a9.3.a2.9.0.cellPVmodule.6457',\n", + " 'a9.3.octtube1a.6457',\n", + " 'a9.3.a2.2.1.cellPVmodule.6457',\n", + " 'a9.3.a2.4.1.cellPVmodule.6457',\n", + " 'a9.3.a2.7.1.cellPVmodule.6457',\n", + " 'a9.3.a2.9.1.cellPVmodule.6457'],\n", + " 'name': '1axis_01_13_08',\n", + " 'octfile': '1axis_01_13_08.oct',\n", + " 'rearMat': ['a9.3.a2.2.0.cellPVmodule.2310',\n", + " 'a9.3.a2.4.0.cellPVmodule.2310',\n", + " 'a9.3.a2.7.0.cellPVmodule.2310',\n", + " 'a9.3.a2.9.0.cellPVmodule.2310',\n", + " 'sky',\n", + " 'a9.3.a2.2.1.cellPVmodule.2310',\n", + " 'a9.3.a2.4.1.cellPVmodule.2310',\n", + " 'a9.3.a2.7.1.cellPVmodule.2310',\n", + " 'a9.3.a2.9.1.cellPVmodule.2310'],\n", + " 'rearX': [1.710181,\n", + " 1.282636,\n", + " 0.8550905,\n", + " 0.4275452,\n", + " -4.440892e-16,\n", + " -0.4275452,\n", + " -0.8550905,\n", + " -1.282636,\n", + " -1.710181],\n", + " 'rearY': [1.047184e-16,\n", + " 7.853879e-17,\n", + " 5.235919e-17,\n", + " 2.61796e-17,\n", + " -2.46519e-32,\n", + " -2.61796e-17,\n", + " -5.235919e-17,\n", + " -7.853879e-17,\n", + " -1.047184e-16],\n", + " 'rearZ': [2.161805,\n", + " 2.188854,\n", + " 2.215903,\n", + " 2.242951,\n", + " 2.27,\n", + " 2.297049,\n", + " 2.324097,\n", + " 2.351146,\n", + " 2.378195],\n", + " 'x': [1.710181,\n", + " 1.282636,\n", + " 0.8550905,\n", + " 0.4275452,\n", + " -4.440892e-16,\n", + " -0.4275452,\n", + " -0.8550905,\n", + " -1.282636,\n", + " -1.710181],\n", + " 'y': [1.047184e-16,\n", + " 7.853879e-17,\n", + " 5.235919e-17,\n", + " 2.61796e-17,\n", + " -2.46519e-32,\n", + " -2.61796e-17,\n", + " -5.235919e-17,\n", + " -7.853879e-17,\n", + " -1.047184e-16],\n", + " 'z': [2.251805,\n", + " 2.278854,\n", + " 2.305903,\n", + " 2.332951,\n", + " 2.36,\n", + " 2.387049,\n", + " 2.414097,\n", + " 2.441146,\n", + " 2.468195]}\n" + ] + } + ], + "source": [ + "pprint.pprint(trackerdict[trackerkeys[0]]['AnalysisObj'].__dict__)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### B. Make Oct and Analye Range of Hours" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "You could do a range of indexes following a similar procedure:" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Making 1 octfiles in root directory.\n", + "Created 1axis_01_13_09.oct\n", + "Linescan in process: 1axis_01_13_09_Front\n", + "Linescan in process: 1axis_01_13_09_Back\n", + "Saved: results\\irr_1axis_01_13_09.csv\n", + "Index: 01_13_09. Wm2Front: 97.91932592592592. Wm2Back: 12.036790925925926\n", + "\n", + "Saving Cumulative results\n", + "Saved: results\\cumulative_results_.csv\n", + "\n", + "Making 1 octfiles in root directory.\n", + "Created 1axis_01_13_13.oct\n", + "Linescan in process: 1axis_01_13_13_Front\n", + "Linescan in process: 1axis_01_13_13_Back\n", + "Saved: results\\irr_1axis_01_13_13.csv\n", + "Index: 01_13_13. Wm2Front: 422.10729629629634. Wm2Back: 38.627922222222224\n", + "\n", + "Saving Cumulative results\n", + "Saved: results\\cumulative_results_.csv\n", + "Accumulated hourly bifi gain: 0.0987\n" + ] + } + ], + "source": [ + "for time in ['01_13_09','01_13_13']: \n", + " demo.makeOct1axis(singleindex=time)\n", + " results=demo.analysis1axis(singleindex=time)\n", + "\n", + "print('Accumulated hourly bifi gain: {:0.3}'.format(sum(demo.Wm2Back) / sum(demo.Wm2Front)))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Note that the bifacial gain printed above is for the accumulated irradiance between the hours modeled so far. \n", + "That is, demo.Wm2Back and demo.Wm2Front are for January 13, 8AM to 1 AM. Compare demo.Wm2back below with what we had before:" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([61.62960867, 56.09894433, 52.26810367, 47.526593 , 37.128162 ,\n", + " 49.24325267, 52.09034567, 56.446585 , 62.22578133])" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "demo.Wm2Back" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To print the specific bifacial gain for a specific hour, you can use the following:" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "0.09151209316009438" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sum(trackerdict['01_13_13']['AnalysisObj'].Wm2Back) / sum(trackerdict['01_13_13']['AnalysisObj'].Wm2Front)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### C. Make Oct and Analyze All Tracking Dictionary\n", + "\n", + "This might considerably more time, depending on the number of entries on the trackerdictionary. If no **startdt** and **enddt** where specified on STEP **gendaylit1axis, this will run ALL of the hours in the year (~4000 hours).**\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Making 20 octfiles in root directory.\n", + "Created 1axis_01_13_08.oct\n", + "Created 1axis_01_13_09.oct\n", + "Created 1axis_01_13_10.oct\n", + "Created 1axis_01_13_11.oct\n", + "Created 1axis_01_13_12.oct\n", + "Created 1axis_01_13_13.oct\n", + "Created 1axis_01_13_14.oct\n", + "Created 1axis_01_13_15.oct\n", + "Created 1axis_01_13_16.oct\n", + "Created 1axis_01_13_17.oct\n", + "Created 1axis_01_14_08.oct\n", + "Created 1axis_01_14_09.oct\n", + "Created 1axis_01_14_10.oct\n", + "Created 1axis_01_14_11.oct\n", + "Created 1axis_01_14_12.oct\n", + "Created 1axis_01_14_13.oct\n", + "Created 1axis_01_14_14.oct\n", + "Created 1axis_01_14_15.oct\n", + "Created 1axis_01_14_16.oct\n", + "Created 1axis_01_14_17.oct\n", + "Linescan in process: 1axis_01_13_08_Front\n", + "Linescan in process: 1axis_01_13_08_Back\n", + "Saved: results\\irr_1axis_01_13_08.csv\n", + "Index: 01_13_08. Wm2Front: 12.447584333333333. Wm2Back: 3.138846148148149\n", + "Linescan in process: 1axis_01_13_09_Front\n", + "Linescan in process: 1axis_01_13_09_Back\n", + "Saved: results\\irr_1axis_01_13_09.csv\n", + "Index: 01_13_09. Wm2Front: 72.53930296296296. Wm2Back: 21.671116444444447\n", + "Linescan in process: 1axis_01_13_10_Front\n", + "Linescan in process: 1axis_01_13_10_Back\n", + "Saved: results\\irr_1axis_01_13_10.csv\n", + "Index: 01_13_10. Wm2Front: 37.70122962962963. Wm2Back: 9.912279814814816\n", + "Linescan in process: 1axis_01_13_11_Front\n", + "Linescan in process: 1axis_01_13_11_Back\n", + "Saved: results\\irr_1axis_01_13_11.csv\n", + "Index: 01_13_11. Wm2Front: 422.5411422222222. Wm2Back: 39.96500296296296\n", + "Linescan in process: 1axis_01_13_12_Front\n", + "Linescan in process: 1axis_01_13_12_Back\n" + ] + } + ], + "source": [ + "demo.makeOct1axis()\n", + "results = demo.analysis1axis()\n", + "print('Accumulated hourly bifi gain for all the trackerdict: {:0.3}'.format(sum(demo.Wm2Back) / sum(demo.Wm2Front)))\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "Remember you should clean your results first! This will have torquetube and sky results if performed this way so don't trust this simplistic bifacial_gain examples.\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Condensed Version: All Tracking Dictionary\n", + "\n", + "This is the summarized version to run gendaylit for one entries in the trackigndictionary." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import bifacial_radiance\n", + "import os \n", + "\n", + "simulationName = 'Tutorial 3'\n", + "moduletype = 'Custom Cell-Level Module' # We will define the parameters for this below in Step 4.\n", + "testfolder = os.path.abspath(r'..\\..\\bifacial_radiance\\TEMP')\n", + "albedo = \"litesoil\" # this is one of the options on ground.rad\n", + "lat = 37.5 \n", + "lon = -77.6\n", + "\n", + "# Scene variables\n", + "nMods = 20\n", + "nRows = 7\n", + "hub_height = 2.3 # meters\n", + "pitch = 10 # meters # We will be using pitch instead of GCR for this example.\n", + "\n", + "# Traking parameters\n", + "cumulativesky = False\n", + "limit_angle = 45 # tracker rotation limit angle\n", + "angledelta = 0.01 # we will be doing hourly simulation, we want the angle to be as close to real tracking as possible.\n", + "backtrack = True \n", + "\n", + "#makeModule parameters\n", + "# x and y will be defined later on Step 4 for this tutorial!!\n", + "xgap = 0.01\n", + "ygap = 0.10\n", + "zgap = 0.05\n", + "numpanels = 2\n", + "torquetube = True\n", + "axisofrotationTorqueTube = False\n", + "diameter = 0.1\n", + "tubetype = 'Oct' # This will make an octagonal torque tube.\n", + "material = 'black' # Torque tube of this material (0% reflectivity)\n", + "\n", + "startdate = '11/06' \n", + "enddate = '11/06'\n", + "demo = bifacial_radiance.RadianceObj(simulationName, path=testfolder) \n", + "demo.setGround(albedo) \n", + "epwfile = demo.getEPW(lat,lon) \n", + "metdata = demo.readWeatherFile(epwfile) \n", + "sceneDict = {'pitch':pitch,'hub_height':hub_height, 'nMods': nMods, 'nRows': nRows} \n", + "demo.set1axis(limit_angle = limit_angle, backtrack = backtrack, gcr = gcr, cumulativesky = cumulativesky)\n", + "demo.gendaylit1axis(startdate=startdate, enddate=enddate)\n", + "demo.makeScene1axis(moduletype=moduletype,sceneDict=sceneDict) #makeScene creates a .rad file with 20 modules per row, 7 rows.\n", + "demo.makeOct1axis()\n", + "demo.analysis1axis()" + ] + } + ], + "metadata": { + "anaconda-cloud": {}, + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.4" + } + }, + "nbformat": 4, + "nbformat_minor": 1 +} diff --git a/docs/tutorials/3 - Medium Level Example - Single Axis Tracking - hourly.py b/docs/tutorials/3 - Medium Level Example - Single Axis Tracking - hourly.py new file mode 100644 index 00000000..9b935754 --- /dev/null +++ b/docs/tutorials/3 - Medium Level Example - Single Axis Tracking - hourly.py @@ -0,0 +1,418 @@ +#!/usr/bin/env python +# coding: utf-8 + +# ## 3 - Medium Level Example - 1-Axis tracker by hour (gendaylit) +# +# Example demonstrating the use of doing hourly smiulations with Radiance gendaylit for 1-axis tracking. This is a medium level example because it also explores a couple subtopics: +# +# #### Subtopics: +#
    +#
  • The structure of the tracker dictionary "trackerDict".
  • +#
  • How to calculate GCR
  • +#
  • How to make a cell-level module
  • +#
  • Various methods to use the trackerdictionary for analysis.
  • +#
+# +# #### Doing full year simulations with gendaylit: +# +# Performs the simulation hour by hour requires either a good computer or some patience, since there are ~4000 daylight-hours in the year. With a 32GB RAM, Windows 10 i7-8700 CPU @ 3.2GHz with 6 cores this takes 1 day. The code also allows for multiple cores or HPC use -- there is documentation/examples inside the software at the moment, but that is an advanced topic. The procedure can be broken into shorter steps for one day or a single timestamp simulation which is exemplified below. +# +# ### Steps: +#
    +#
  1. Load bifacial_radiance
  2. +#
  3. Define all your system variables
  4. +#
  5. Create Radiance Object, Set Albedo and Weather
  6. +#
  7. Make Module: Cell Level Module Example
  8. +#
  9. Calculate GCR
  10. +#
  11. Set Tracking Angles
  12. +#
  13. Generate the Sky
  14. +#
  15. Make Scene 1axis
  16. +#
    1. Make Oct and AnalyzE 1 HOUR
    2. +#
    3. Make Oct and Analye Range of Hours
    4. +#
    5. Make Oct and Analyze All Tracking Dictionary
    +#
+# +# And finally: + +# + +# +# ## 1. Load bifacial_radiance +# +# #### Pay attention: different importing method: +# +# So far we've used "from bifacial_radiance import *" to import all the bifacial_radiance files into our working space in jupyter. For this journal we will do a "import bifacial_radiance" . This method of importing requires a different call for some functions as you'll see below. For example, instead of calling demo = RadianceObj(path = testfolder) as on Tutorial 2, in this case we will neeed to do demo = bifacial_radiance.RadianceObj(path = testfolder). + +# In[2]: + + +import bifacial_radiance +import numpy as np +import os # this operative system to do teh relative-path testfolder for this example. +import pprint # We will be pretty-printing the trackerdictionary throughout to show its structure. + + +# + +# ## 2. Define all your system variables +# +# Just like in the condensed version show at the end of Tutorial 2, for this tutorial we will be starting all of our system variables from the beginning of the jupyter journal, instead than throughout the different cells (for the most part) + +# In[3]: + + +simulationName = 'Tutorial 3' # For adding a simulation name when defning RadianceObj. This is optional. +moduletype = 'Custom Cell-Level Module' # We will define the parameters for this below in Step 4. +testfolder = os.path.abspath(r'..\..\bifacial_radiance\TEMP') +albedo = "litesoil" # this is one of the options on ground.rad +lat = 37.5 +lon = -77.6 + +# Scene variables +nMods = 20 +nRows = 7 +hub_height = 2.3 # meters +pitch = 10 # meters # We will be using pitch instead of GCR for this example. + +# Traking parameters +cumulativesky = False +limit_angle = 45 # tracker rotation limit angle +angledelta = 0.01 # we will be doing hourly simulation, we want the angle to be as close to real tracking as possible. +backtrack = True + +#makeModule parameters +# x and y will be defined later on Step 4 for this tutorial!! +xgap = 0.01 +ygap = 0.10 +zgap = 0.05 +numpanels = 2 +torquetube = True +axisofrotationTorqueTube = False +diameter = 0.1 +tubetype = 'Oct' # This will make an octagonal torque tube. +material = 'black' # Torque tube of this material (0% reflectivity) + + +# + +# ## 3. Create Radiance Object, Set Albedo and Weather +# +# Same steps as previous two tutorials, so condensing it into one step. You hopefully have this down by now! :) +# +# +#
+# Notice that we are doing bifacial_radiance.RadianceObj because we change the import method for this example! +#
+ +# In[4]: + + +demo = bifacial_radiance.RadianceObj(simulationName, path = testfolder) # Adding a simulation name. This is optional. +demo.setGround(albedo) +epwfile = demo.getEPW(lat = lat, lon = lon) +metdata = demo.readWeatherFile(weatherFile = epwfile) + + +# + +# ## 4. Make Module: Cell Level Module Example +# +# Instead of doing a opaque, flat single-surface module, in this tutorial we will create a module made up by cells. We can define variuos parameters to make a cell-level module, such as cell size and spacing between cells. To do this, we will pass a dicitonary with the needed parameters to makeModule, as shown below. +# +#
+# Since we are passing a cell-level dictionary, the values for module's x and y of the module will be calculated by the software -- no need to pass them (and if you do, they'll just get ignored) +#
+ +# In[5]: + + +numcellsx = 6 +numcellsy = 12 +xcell = 0.156 +ycell = 0.156 +xcellgap = 0.02 +ycellgap = 0.02 + +cellLevelModuleParams = {'numcellsx': numcellsx, 'numcellsy':numcellsy, + 'xcell': xcell, 'ycell': ycell, 'xcellgap': xcellgap, 'ycellgap': ycellgap} + +mymodule = demo.makeModule(name=moduletype, torquetube=torquetube, diameter=diameter, tubetype=tubetype, material=material, + xgap=xgap, ygap=ygap, zgap=zgap, numpanels=numpanels, + cellLevelModuleParams=cellLevelModuleParams, + axisofrotationTorqueTube=axisofrotationTorqueTube) + + +# + +# ## 5. Calculate GCR +# +# In this example we passed the parameter "pitch". Pitch is the spacing between rows (for example, between hub-posts) in a field. +# To calculate Ground Coverage Ratio (GCR), we must relate the pitch to the collector-width by: +# +# ![GCR = CW / pitch](../images_wiki/Journal3Pics/Equation_GCR.png) +# +# The collector width for our system must consider the number of panels and the y-gap: +# +# ![CW](../images_wiki/Journal3Pics/Equation_CW.png) +# +# Collector Width gets saved in your module parameters (and later on your scene and trackerdict) as "sceney". You can calculate your collector width with the equation, or you can use this method to know your GCR: + +# In[6]: + + +# For more options on makemodule, see the help description of the function. +CW = mymodule['sceney'] +gcr = CW / pitch +print ("The GCR is :", gcr) + + +# + +# ## 6. Set Tracking Angles +# +# This function will read the weather file, and based on the sun position it will calculate the angle the tracker should be at for each hour. It will create metdata files for each of the tracker angles considered. +# +# For doing hourly simulations, remember to set **cumulativesky = False** here! + +# In[7]: + + +trackerdict = demo.set1axis(metdata = metdata, limit_angle = limit_angle, backtrack = backtrack, + gcr = gcr, cumulativesky = cumulativesky) + + +# In[8]: + + +print ("Full trackerdict for all the year created by set1axis: %s " % (len(demo.trackerdict))) + + +# set1axis initializes the trackerdictionary Trackerdict. Trackerdict contains all hours in the year as keys. For example: trackerdict['12_16_08']. It is a return variable on many of the 1axis functions, but it is also stored inside of your Radiance Obj (i.e. demo.trackerdict). In this journal we are storing it as a variable to mute the option (otherwise it prints the returned trackerdict contents every time) +# + +# In[9]: + + +pprint.pprint(trackerdict['12_16_08']) + + +# In[10]: + + +pprint.pprint(demo.trackerdict['12_16_08']) + + +# All of the following functions add up elements to trackerdictionary to keep track (ba-dum tupzz) of the Scene and simulation parameters. In advanced journals we will explore the inner structure of trackerdict. For now, just now it exists :) + +# + +# ## 7. Generate the Sky +# +# +# We will create skies for each hour we want to model with the function gendaylit1axis. +# +# **If you don't specify a startdate and enddate, all the year will be created, which will take more time.** +# +# For this example we are doing just two days in January. Format has to be a 'MM_DD' or 'MM/DD' + +# In[11]: + + +startdate = '01/13' +enddate = '01/14' +trackerdict = demo.gendaylit1axis(startdate=startdate, enddate=enddate) + + +# Since we passed startdate and enddate to gendaylit, it will prune our trackerdict to only the desired days. +# Let's explore our trackerdict: + +# In[12]: + + +print ("\nTrimmed trackerdict by gendaylit1axis to start and enddate length: %s " % (len(trackerdict))) +print ("") +trackerkeys = sorted(trackerdict.keys()) +print ("Option of hours are: ", trackerkeys) +print ("") +print ("Contents of trackerdict for sample hour:") +pprint.pprint(trackerdict[trackerkeys[0]]) + + +# + +# ## 8. Make Scene 1axis +# +# We can use gcr or pitch fo our scene dictionary. + +# In[13]: + + +# making the different scenes for the 1-axis tracking for the dates in trackerdict2. + +sceneDict = {'pitch': pitch,'hub_height':hub_height, 'nMods':nMods, 'nRows': nRows} + +trackerdict = demo.makeScene1axis(trackerdict = trackerdict, moduletype = moduletype, sceneDict = sceneDict) + + +# The scene parameteres are now stored in the trackerdict. To view them and to access them: +# + +# In[14]: + + +pprint.pprint(trackerdict[trackerkeys[0]]) + + +# In[15]: + + +pprint.pprint(demo.trackerdict[trackerkeys[5]]['scene'].__dict__) + + +# + +# ## 9. Make Oct and Analyze +# +# ### A. Make Oct and Analyze 1Hour +# +# There are various options now to analyze the trackerdict hours we have defined. We will start by doing just one hour, because it's the fastest. Make sure to select an hour that exists in your trackerdict! +# +# Options of hours: + +# In[16]: + + +pprint.pprint(trackerkeys) + + +# In[17]: + + +demo.makeOct1axis(singleindex='01_13_08') +results = demo.analysis1axis(singleindex='01_13_08') +print('\n\nHourly bifi gain: {:0.3}'.format(sum(demo.Wm2Back) / sum(demo.Wm2Front))) + + +# The trackerdict now contains information about the octfile, as well as the Analysis Object results + +# In[18]: + + +print ("\n Contents of trackerdict for sample hour after analysis1axis: ") +pprint.pprint(trackerdict[trackerkeys[0]]) + + +# In[19]: + + +pprint.pprint(trackerdict[trackerkeys[0]]['AnalysisObj'].__dict__) + + +# + +# ### B. Make Oct and Analye Range of Hours + +# You could do a range of indexes following a similar procedure: + +# In[20]: + + +for time in ['01_13_09','01_13_13']: + demo.makeOct1axis(singleindex=time) + results=demo.analysis1axis(singleindex=time) + +print('Accumulated hourly bifi gain: {:0.3}'.format(sum(demo.Wm2Back) / sum(demo.Wm2Front))) + + +# Note that the bifacial gain printed above is for the accumulated irradiance between the hours modeled so far. +# That is, demo.Wm2Back and demo.Wm2Front are for January 13, 8AM to 1 AM. Compare demo.Wm2back below with what we had before: + +# In[21]: + + +demo.Wm2Back + + +# To print the specific bifacial gain for a specific hour, you can use the following: + +# In[22]: + + +sum(trackerdict['01_13_13']['AnalysisObj'].Wm2Back) / sum(trackerdict['01_13_13']['AnalysisObj'].Wm2Front) + + +# + +# ### C. Make Oct and Analyze All Tracking Dictionary +# +# This might considerably more time, depending on the number of entries on the trackerdictionary. If no **startdt** and **enddt** where specified on STEP **gendaylit1axis, this will run ALL of the hours in the year (~4000 hours).** +# + +# In[ ]: + + +demo.makeOct1axis() +results = demo.analysis1axis() +print('Accumulated hourly bifi gain for all the trackerdict: {:0.3}'.format(sum(demo.Wm2Back) / sum(demo.Wm2Front))) + + +#
+# Remember you should clean your results first! This will have torquetube and sky results if performed this way so don't trust this simplistic bifacial_gain examples. +#
+ +# + +# ### Condensed Version: All Tracking Dictionary +# +# This is the summarized version to run gendaylit for one entries in the trackigndictionary. + +# In[ ]: + + +import bifacial_radiance +import os + +simulationName = 'Tutorial 3' +moduletype = 'Custom Cell-Level Module' # We will define the parameters for this below in Step 4. +testfolder = os.path.abspath(r'..\..\bifacial_radiance\TEMP') +albedo = "litesoil" # this is one of the options on ground.rad +lat = 37.5 +lon = -77.6 + +# Scene variables +nMods = 20 +nRows = 7 +hub_height = 2.3 # meters +pitch = 10 # meters # We will be using pitch instead of GCR for this example. + +# Traking parameters +cumulativesky = False +limit_angle = 45 # tracker rotation limit angle +angledelta = 0.01 # we will be doing hourly simulation, we want the angle to be as close to real tracking as possible. +backtrack = True + +#makeModule parameters +# x and y will be defined later on Step 4 for this tutorial!! +xgap = 0.01 +ygap = 0.10 +zgap = 0.05 +numpanels = 2 +torquetube = True +axisofrotationTorqueTube = False +diameter = 0.1 +tubetype = 'Oct' # This will make an octagonal torque tube. +material = 'black' # Torque tube of this material (0% reflectivity) + +startdate = '11/06' +enddate = '11/06' +demo = bifacial_radiance.RadianceObj(simulationName, path=testfolder) +demo.setGround(albedo) +epwfile = demo.getEPW(lat,lon) +metdata = demo.readWeatherFile(epwfile) +sceneDict = {'pitch':pitch,'hub_height':hub_height, 'nMods': nMods, 'nRows': nRows} +demo.set1axis(limit_angle = limit_angle, backtrack = backtrack, gcr = gcr, cumulativesky = cumulativesky) +demo.gendaylit1axis(startdate=startdate, enddate=enddate) +demo.makeScene1axis(moduletype=moduletype,sceneDict=sceneDict) #makeScene creates a .rad file with 20 modules per row, 7 rows. +demo.makeOct1axis() +demo.analysis1axis() + diff --git a/docs/v3 - 2-UP with Torque Tube, Fixed Tilt Example, with CLEAN Routine.ipynb b/docs/tutorials/4 - Medium Level Example - Debugging your Scene with Custom Objects (Fixed Tilt 2-up with Torque Tube + CLEAN Routine + CustomObject).ipynb similarity index 91% rename from docs/v3 - 2-UP with Torque Tube, Fixed Tilt Example, with CLEAN Routine.ipynb rename to docs/tutorials/4 - Medium Level Example - Debugging your Scene with Custom Objects (Fixed Tilt 2-up with Torque Tube + CLEAN Routine + CustomObject).ipynb index fe261728..762758a3 100644 --- a/docs/v3 - 2-UP with Torque Tube, Fixed Tilt Example, with CLEAN Routine.ipynb +++ b/docs/tutorials/4 - Medium Level Example - Debugging your Scene with Custom Objects (Fixed Tilt 2-up with Torque Tube + CLEAN Routine + CustomObject).ipynb @@ -4,24 +4,48 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# 2-UP with Torque Tube, Fixed Tilt Example, with CLEAN Routine\n", + "# 4 - Medium Level Example - Debugging your Scene with Custom Objects\n", + "### Fixed Tilt 2-up with Torque Tube + CLEAN Routine + CustomObject\n", "\n", - "This journal has exmaples of vairous things:\n", + "This journal has examples of various things, some which hav ebeen covered before and some in more depth:\n", "\n", - "1) Running a fixed_tilt simulation beginning to end.\n", + "
    \n", + "
  • Running a fixed_tilt simulation beginning to end.
  • \n", + "
  • Creating a 2-up module with torque-tube, and detailed geometry of spacings in xgap, ygap and zgap.
  • \n", + "
  • Calculating the tracker angle for a specific time, in case you want to use that value to model a fixed_tilt setup.
  • \n", + "
  • Loading and cleaning results, particularly important when using setups with torquetubes / ygaps.
  • \n", + "
  • Adding a \"Custom Object\" or **marker** at the Origin of the Scene, to do a visual sanity-check of the geometry.
  • \n", + "
\n", "\n", - "2) Creating a 2-up module with torque-tube, and detailed geometry of spacings in xgap, ygap and zgap.\n", + "It will look something like this (without the marker in this visualization):\n", "\n", - "3) (Optional) Calculating the tracker angle for a specific time, in case you want to use that value to model a fixed_tilt setup.\n", + "![What we are trying to re-create](../images_wiki/Journal_example_torquetube.PNG)\n", "\n", - "4) Loading and cleaning results, particularly important when using setups with torquetubes / ygaps. \n", + "### STEPS:\n", "\n", - "5) (Optional) Adding a marker at the Origin of the Scene, to do a visual sanity-check of the geometry.\n", - "\n", - "\n", - "It will look something like this (without the optional steps 3 and 5):\n", - "\n", - "![What we are trying to re-create](images_wiki/Journal_example_torquetube.PNG)" + "
    \n", + "
  1. Specify Working Folder and Import Program
  2. \n", + "
  3. Specify all variables
  4. \n", + "
  5. Create the Radiance Object and generate the Sky
  6. \n", + "
  7. Calculating tracker angle/geometry for a specific timestamp
  8. \n", + "
  9. Making the Module & the Scene, Visualize and run Analysis
  10. \n", + "
  11. Calculate Bifacial Ratio (clean results)
  12. \n", + "
  13. Add Custom Elements to your Scene Example: Marker at 0,0 position
  14. \n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 1. Specify Working Folder and Import Program\n" ] }, { @@ -39,39 +63,33 @@ ], "source": [ "import os\n", - "testfolder = os.path.abspath(r'..\\bifacial_radiance\\TEMP') \n", + "testfolder = os.path.abspath(r'..\\..\\bifacial_radiance\\TEMP') \n", "\n", - "# You can alternatively point to an empty directory (it will open a load GUI Visual Interface)\n", - "# or specify any other directory in your computer. I.E.:\n", - "# testfolder = r'C:\\Users\\sayala\\Documents\\RadianceScenes\\Demo'\n", + "print (\"Your simulation will be stored in %s\" % testfolder)\n", "\n", - "print (\"Your simulation will be stored in %s\" % testfolder)" + "import bifacial_radiance\n", + "import numpy as np" ] }, { - "cell_type": "code", - "execution_count": 2, + "cell_type": "markdown", "metadata": {}, - "outputs": [], "source": [ - "try:\n", - " import bifacial_radiance\n", - "except ImportError:\n", - " raise RuntimeError('bifacial_radiance is required. download distribution')\n", - " # Simple example system using Radiance.\n", - "\n", - "import numpy as np" + "" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "### Specify all variables for the module and scene\n", + "### 2. Specify all variables for the module and scene\n", "\n", "Below find a list of all of the possible parameters for makeModule. \n", "scene and simulation parameters are also organized below. \n", - "This simulation will be a complete simulation in terms of parameters that you can modify." + "This simulation will be a complete simulation in terms of parameters that you can modify.\n", + "\n", + "The below routine creates a HEXAGONAL torque tube, for a 2-UP configuration of a specific module size. Parameters for the module, the torque tube, and the scene are below.\n", + "This is being run with gendaylit, for one specific timestamp" ] }, { @@ -80,9 +98,6 @@ "metadata": {}, "outputs": [], "source": [ - "# The below routine creates a HEXAGONAL torque tube, for a 2-UP configuration of a specific module size. Parameters for the module, the torque tube, and the scene are below.\n", - "# This is being run with gendaylit, for one specific timestamp\n", - "\n", "timestamp = 4020 # Noon, June 17th. \n", "simulationname = 'Torque_tube_hex_test'\n", "\n", @@ -122,7 +137,14 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Create the Radiance Object and generate the Sky" + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 3. Create the Radiance Object and generate the Sky" ] }, { @@ -163,23 +185,29 @@ "demo.setGround(albedo) # input albedo number or material name like 'concrete'. To see options, run this without any input.\n", "epwfile = demo.getEPW(lat,lon) # pull TMY data for any global lat/lon\n", "metdata = demo.readEPW(epwfile) # read in the EPW weather data from above\n", - "demo.gendaylit(metdata,timestamp) # Noon, June 17th\n", - "\n" + "demo.gendaylit(metdata,timestamp) # Noon, June 17th" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "### Slight detour: Calculating tracker angle/geometry for a specific timestamp\n", - "\n", - "This is useful if you are trying to use the fixed-tilt steps in bifacial_radiance to model a tracker for one specific point in time (if you take a picture of a tracker, it looks fixed, right? Well then). \n", + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 4. Calculating tracker angle/geometry for a specific timestamp\n", "\n", - "We assigned a 10 degree tilt at the beginning, but if we were to model a tracker as a fixed-tilt element because we are interested in only one point in time,\n", - "this routine will tell us what tilt to use. Please note that to model a tracker as fixed tilt, we suggest passing a hub_height, otherwise you will have to calculate the clearance_height manually.\n", + "This trick is useful if you are trying to use the fixed-tilt steps in bifacial_radiance to model a tracker for one specific point in time (if you take a picture of a tracker, it looks fixed, right? Well then). \n", "\n", + "We assigned a 10 degree tilt at the beginning, but if we were to model a tracker as a fixed-tilt element because we are interested in only one point in time, this routine will tell us what tilt to use. *Please note that to model a tracker as fixed tilt, we suggest passing a hub_height, otherwise you will have to calculate the clearance_height manually.*\n", "\n", - "Details: If the tracker is N-S axis azimuth, the surface azimuth of the modules will be set to 90 always, with a tilt that is either positive (for the early morning, facing East), or negative (for the afternoon, facing west).\n" + "
\n", + "Details: you might have noticed in the previoust tutorial looking at the tracker dictionary, but the way that bifacial_radiance handles tracking: If the tracker is N-S axis azimuth, the surface azimuth of the modules will be set to 90 always, with a tilt that is either positive (for the early morning, facing East), or negative (for the afternoon, facing west).\n", + "
\n" ] }, { @@ -213,7 +241,14 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Making the Module" + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 5. Making the Module & the Scene, Visualize and run Analysis" ] }, { @@ -237,16 +272,18 @@ "# Making module with all the variables\n", "moduledict=demo.makeModule(name=module_type,x=x,y=y,bifi=1, \n", " torquetube=torqueTube, diameter = diameter, tubetype = tubetype, material = torqueTubeMaterial, zgap = zgap, numpanels = numpanels, ygap = ygap, rewriteModulefile = True, xgap = xgap)\n", + "\n", "# create a scene with all the variables. \n", - "# Specifying the pitch automatically with the collector width (sceney) returend by moduledict.\n", + "# Specifying the pitch automatically with the collector width (sceney) returned by moduledict.\n", "# Height has been deprecated as an input. pass clearance_height or hub_height in the scenedict.\n", + "\n", "sceneDict = {'tilt':tilt,'pitch': np.round(moduledict['sceney'] / gcr,3),\n", " 'hub_height':hub_height,'azimuth':azimuth_ang, \n", " 'module_type':module_type, 'nMods': nMods, 'nRows': nRows} \n", "\n", "scene = demo.makeScene(moduletype=module_type, sceneDict=sceneDict) #makeScene creates a .rad file of the Scene\n", "\n", - "octfile = demo.makeOct(demo.getfilelist()) # makeOct combines all of the ground, sky and object files into a .oct file.\n" + "octfile = demo.makeOct(demo.getfilelist()) # makeOct combines all of the ground, sky and object files into a .oct file." ] }, { @@ -278,7 +315,7 @@ "source": [ "analysis = bifacial_radiance.AnalysisObj(octfile, demo.name) # return an analysis object including the scan dimensions for back irradiance\n", "\n", - "sensorsy = 200 # setting this veyr high to see a detailed profile of the irradiance, including\n", + "sensorsy = 200 # setting this very high to see a detailed profile of the irradiance, including\n", "#the shadow of the torque tube on the rear side of the module.\n", "frontscan, backscan = analysis.moduleAnalysis(scene, modWanted = 2, rowWanted = 1, sensorsy = 200)\n", "frontDict, backDict = analysis.analysis(octfile, demo.name, frontscan, backscan) # compare the back vs front irradiance \n", @@ -287,14 +324,21 @@ "# See comment below of why this line is commented out." ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] + }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", - "## Calculate Bifacial Ratio (clean results)\n", + "### 6. Calculate Bifacial Ratio (clean results)\n", "\n", - "Altough we could a bifacial ratio average at this point, this value would be misleading, since some of the sensors generated will fall on the torque tube, the sky, and/or the ground since we have torquetube and ygap in the scene. To calculate the real bifacial ratio average, we must use the clean routines.\n" + "Although we could calculate a bifacial ratio average at this point, this value would be misleading, since some of the sensors generated will fall on the torque tube, the sky, and/or the ground since we have torquetube and ygap in the scene. To calculate the real bifacial ratio average, we must use the clean routines.\n" ] }, { @@ -1215,7 +1259,7 @@ } ], "source": [ - "resultFile=r'C:\\Users\\sayala\\Documents\\GitHub\\bifacial_radiance\\bifacial_radiance\\TEMP\\results\\irr_Torque_tube_hex_test.csv'\n", + "resultFile='results/irr_Torque_tube_hex_test.csv'\n", "results_loaded = bifacial_radiance.load.read1Result(resultFile)\n", "print(\"Printing the dataframe containing the results just calculated in %s: \" % resultFile)\n", "results_loaded" @@ -1353,11 +1397,17 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Add Custom Elements to your Scene Example \n", - "## Marker at 0,0 position\n", + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 7. Add Custom Elements to your Scene Example: Marker at 0,0 position\n", "This shows how to add a custom element, in this case a Cube, that will be placed in the center of your already created scene to mark the 0,0 location. \n", "\n", - "This can be added at any point after makeScene has been runned once. Notice that if this extra element is in the scene and the analysis sensors fall on this element, they will measure irradiance at this element and no the modules." + "This can be added at any point after makeScene has been run once. Notice that if this extra element is in the scene and the analysis sensors fall on this element, they will measure irradiance at this element and no the modules." ] }, { @@ -1420,7 +1470,7 @@ "source": [ "demo.appendtoScene(scene.radfiles, customObject, '!xform -rz 0')\n", "# makeOct combines all of the ground, sky and object files into a .oct file.\n", - "octfile = demo.makeOct(demo.getfilelist()) \n" + "octfile = demo.makeOct(demo.getfilelist()) " ] }, { @@ -1435,11 +1485,11 @@ " \n", "If you ran the getTrackerAngle detour and appended the marker, it should look like this:\n", "\n", - "![Marker position at 0,0](images_wiki\\Journal_example_marker_origin.PNG)\n", + "![Marker position at 0,0](..\\images_wiki\\Journal_example_marker_origin.PNG)\n", "\n", "If you do an analysis and any of the sensors hits the Box object we just created, the list of materials in the result.csv file should say something with \"CenterMarker\" on it. \n", "\n", - "#### See more examples of the use of makeCustomObject and appendtoScene on the Bifacial Carport/Canopies Journal for version 3.0." + "#### See more examples of the use of makeCustomObject and appendtoScene on the Bifacial Carport/Canopies Tutorial" ] } ], @@ -1459,7 +1509,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.3" + "version": "3.7.4" } }, "nbformat": 4, diff --git a/docs/tutorials/4 - Medium Level Example - Debugging your Scene with Custom Objects (Fixed Tilt 2-up with Torque Tube + CLEAN Routine + CustomObject).py b/docs/tutorials/4 - Medium Level Example - Debugging your Scene with Custom Objects (Fixed Tilt 2-up with Torque Tube + CLEAN Routine + CustomObject).py new file mode 100644 index 00000000..6992a048 --- /dev/null +++ b/docs/tutorials/4 - Medium Level Example - Debugging your Scene with Custom Objects (Fixed Tilt 2-up with Torque Tube + CLEAN Routine + CustomObject).py @@ -0,0 +1,281 @@ +#!/usr/bin/env python +# coding: utf-8 + +# # 4 - Medium Level Example - Debugging your Scene with Custom Objects +# ### Fixed Tilt 2-up with Torque Tube + CLEAN Routine + CustomObject +# +# This journal has examples of various things, some which hav ebeen covered before and some in more depth: +# +#
    +#
  • Running a fixed_tilt simulation beginning to end.
  • +#
  • Creating a 2-up module with torque-tube, and detailed geometry of spacings in xgap, ygap and zgap.
  • +#
  • Calculating the tracker angle for a specific time, in case you want to use that value to model a fixed_tilt setup.
  • +#
  • Loading and cleaning results, particularly important when using setups with torquetubes / ygaps.
  • +#
  • Adding a "Custom Object" or **marker** at the Origin of the Scene, to do a visual sanity-check of the geometry.
  • +#
+# +# It will look something like this (without the marker in this visualization): +# +# ![What we are trying to re-create](../images_wiki/Journal_example_torquetube.PNG) +# +# ### STEPS: +# +#
    +#
  1. Specify Working Folder and Import Program
  2. +#
  3. Specify all variables
  4. +#
  5. Create the Radiance Object and generate the Sky
  6. +#
  7. Calculating tracker angle/geometry for a specific timestamp
  8. +#
  9. Making the Module & the Scene, Visualize and run Analysis
  10. +#
  11. Calculate Bifacial Ratio (clean results)
  12. +#
  13. Add Custom Elements to your Scene Example: Marker at 0,0 position
  14. +#
+ +# + +# ### 1. Specify Working Folder and Import Program +# + +# In[1]: + + +import os +testfolder = os.path.abspath(r'..\..\bifacial_radiance\TEMP') + +print ("Your simulation will be stored in %s" % testfolder) + +import bifacial_radiance +import numpy as np + + +# + +# ### 2. Specify all variables for the module and scene +# +# Below find a list of all of the possible parameters for makeModule. +# scene and simulation parameters are also organized below. +# This simulation will be a complete simulation in terms of parameters that you can modify. +# +# The below routine creates a HEXAGONAL torque tube, for a 2-UP configuration of a specific module size. Parameters for the module, the torque tube, and the scene are below. +# This is being run with gendaylit, for one specific timestamp + +# In[3]: + + +timestamp = 4020 # Noon, June 17th. +simulationname = 'Torque_tube_hex_test' + +## SceneDict Parameters +gcr = 0.33 # ground cover ratio, = module_height / pitch +albedo = 0.28 #'concrete' # ground albedo +hub_height = 2.35 # we could also pass clearance_height. +azimuth_ang=90 # Modules will be facing East. +lat = 37.5 +lon = -77.6 +nMods = 4 # doing a smaller array for better visualization on this example. +nRows = 2 + +# MakeModule Parameters +module_type='my_custom_panel' +x = 1.996 # landscape, sinze x > y. Remember that orientation has been deprecated. +y = 0.991 +tilt = 10 +numpanels = 2 # doing a 2-up system! +cellLevelModule = False # not doing a cell level module on this example. + +# Gaps: +xgap = 0.05 # distance between modules in the row. +ygap = 0.15 # distance between the 2 modules along the collector slope. +zgap = 0.175 # if there is a torquetube, this is the distance between the torquetube and the modules. +# If there is not a module, zgap is the distance between the module and the axis of rotation (relevant for +# tracking systems. + +# TorqueTube Parameters +torqueTube = True +tubetype = 'Hex' +diameter = 0.15 +torqueTubeMaterial = 'Metal_Grey' # IT's NOT GRAY, IT's GREY. + + +# + +# ### 3. Create the Radiance Object and generate the Sky + +# In[4]: + + +demo = bifacial_radiance.RadianceObj(simulationname,path = testfolder) # Create a RadianceObj 'object' +demo.setGround(albedo) # input albedo number or material name like 'concrete'. To see options, run this without any input. +epwfile = demo.getEPW(lat,lon) # pull TMY data for any global lat/lon +metdata = demo.readEPW(epwfile) # read in the EPW weather data from above +demo.gendaylit(metdata,timestamp) # Noon, June 17th + + +# + +# ## 4. Calculating tracker angle/geometry for a specific timestamp +# +# This trick is useful if you are trying to use the fixed-tilt steps in bifacial_radiance to model a tracker for one specific point in time (if you take a picture of a tracker, it looks fixed, right? Well then). +# +# We assigned a 10 degree tilt at the beginning, but if we were to model a tracker as a fixed-tilt element because we are interested in only one point in time, this routine will tell us what tilt to use. *Please note that to model a tracker as fixed tilt, we suggest passing a hub_height, otherwise you will have to calculate the clearance_height manually.* +# +#
+# Details: you might have noticed in the previoust tutorial looking at the tracker dictionary, but the way that bifacial_radiance handles tracking: If the tracker is N-S axis azimuth, the surface azimuth of the modules will be set to 90 always, with a tilt that is either positive (for the early morning, facing East), or negative (for the afternoon, facing west). +#
+# + +# In[5]: + + +# Some tracking parameters that won't be needed after getting this angle: +axis_azimuth = 180 +axis_tilt = 0 +limit_angle = 60 +backtrack = True +tilt = demo.getSingleTimestampTrackerAngle(metdata, timestamp, gcr, axis_azimuth, axis_tilt,limit_angle, backtrack) + +print ("\n NEW Calculated Tilt: %s " % tilt) + + +# + +# ### 5. Making the Module & the Scene, Visualize and run Analysis + +# In[6]: + + +# Making module with all the variables +moduledict=demo.makeModule(name=module_type,x=x,y=y,bifi=1, + torquetube=torqueTube, diameter = diameter, tubetype = tubetype, material = torqueTubeMaterial, zgap = zgap, numpanels = numpanels, ygap = ygap, rewriteModulefile = True, xgap = xgap) + +# create a scene with all the variables. +# Specifying the pitch automatically with the collector width (sceney) returned by moduledict. +# Height has been deprecated as an input. pass clearance_height or hub_height in the scenedict. + +sceneDict = {'tilt':tilt,'pitch': np.round(moduledict['sceney'] / gcr,3), + 'hub_height':hub_height,'azimuth':azimuth_ang, + 'module_type':module_type, 'nMods': nMods, 'nRows': nRows} + +scene = demo.makeScene(moduletype=module_type, sceneDict=sceneDict) #makeScene creates a .rad file of the Scene + +octfile = demo.makeOct(demo.getfilelist()) # makeOct combines all of the ground, sky and object files into a .oct file. + + +# At this point you should be able to go into a command window (cmd.exe) and check the geometry. It should look like the image at the beginning of the journal. Example: +# +# #### rvu -vf views\front.vp -e .01 -pe 0.02 -vp -2 -12 14.5 Torque_tube_hex_test.oct +# +# And then proceed happily with your analysis: + +# In[7]: + + +analysis = bifacial_radiance.AnalysisObj(octfile, demo.name) # return an analysis object including the scan dimensions for back irradiance + +sensorsy = 200 # setting this very high to see a detailed profile of the irradiance, including +#the shadow of the torque tube on the rear side of the module. +frontscan, backscan = analysis.moduleAnalysis(scene, modWanted = 2, rowWanted = 1, sensorsy = 200) +frontDict, backDict = analysis.analysis(octfile, demo.name, frontscan, backscan) # compare the back vs front irradiance + +# print('"Annual" bifacial ratio average: %0.3f' %( sum(analysis.Wm2Back) / sum(analysis.Wm2Front) ) ) +# See comment below of why this line is commented out. + + +# + +# +# ### 6. Calculate Bifacial Ratio (clean results) +# +# Although we could calculate a bifacial ratio average at this point, this value would be misleading, since some of the sensors generated will fall on the torque tube, the sky, and/or the ground since we have torquetube and ygap in the scene. To calculate the real bifacial ratio average, we must use the clean routines. +# + +# In[8]: + + +resultFile='results/irr_Torque_tube_hex_test.csv' +results_loaded = bifacial_radiance.load.read1Result(resultFile) +print("Printing the dataframe containing the results just calculated in %s: " % resultFile) +results_loaded + + +# In[9]: + + +print("Looking at only 1 sensor in the middle -- position 100 out of the 200 sensors sampled:") +results_loaded.loc[100] + + +# As an example, we can see above that sensor 100 falls in the hextube, and in the sky. We need to remove this to calculate the real bifacial_gain from the irradiance falling into the modules. To do this we use cleanResult form the load.py module in bifacial_radiance. This finds the invalid materials and sets the irradiance values for those materials to NaN +# +# This might take some time in the current version. + +# In[10]: + + +# Cleaning Results: +# remove invalid materials and sets the irradiance values to NaN +clean_results = bifacial_radiance.load.cleanResult(results_loaded) + + +# In[11]: + + +print("Sampling the same location as before to see what the results are now:") +clean_results.loc[100] + + +# In[12]: + + +print('CORRECT Annual bifacial ratio average: %0.3f' %( clean_results['Wm2Back'].sum() / clean_results['Wm2Front'].sum() )) + +print ("\n(If we had not done the cleaning routine, the bifacial ratio would have been ", "calculated to %0.3f <-- THIS VALUE IS WRONG)" %( sum(analysis.Wm2Back) / sum(analysis.Wm2Front) )) + + +# + +# ### 7. Add Custom Elements to your Scene Example: Marker at 0,0 position +# This shows how to add a custom element, in this case a Cube, that will be placed in the center of your already created scene to mark the 0,0 location. +# +# This can be added at any point after makeScene has been run once. Notice that if this extra element is in the scene and the analysis sensors fall on this element, they will measure irradiance at this element and no the modules. + +# We are going to create a "MyMarker.rad" file in the objects folder, right after we make the Module. +# This is a prism (so we use 'genbox'), that is black from the ground.rad list of materials ('black') +# We are naming it 'CenterMarker' +# Its sides are going to be 0.5x0.5x0.5 m +# and We are going to leave its bottom surface coincident with the plane z=0, but going to center on X and Y. + +# In[13]: + + +name='MyMarker' +text='! genbox black CenterMarker 0.1 0.1 4 | xform -t -0.05 -0.05 0' +customObject = demo.makeCustomObject(name,text) + + +# This should have created a MyMarker.rad object on your objects folder. +# +# But creating the object does not automatically adds it to the seen. So let's now add the customObject to the Scene. We are not going to translate it or anything because we want it at the center, but you can pass translation, rotation, and any other XFORM command from Radiance. +# +# I am passing a rotation 0 because xform has to have something (I think) otherwise it gets confused. + +# In[14]: + + +demo.appendtoScene(scene.radfiles, customObject, '!xform -rz 0') +# makeOct combines all of the ground, sky and object files into a .oct file. +octfile = demo.makeOct(demo.getfilelist()) + + +# appendtoScene appended to the Scene.rad file the name of the custom object we created and the xform transformation we included as text. Then octfile merged this new scene with the ground and sky files. +# +# At this point you should be able to go into a command window (cmd.exe) and check the geometry, and the marker should be there. Example: +# +# #### rvu -vf views\front.vp -e .01 Torque_tube_hex_test.oct +# +# If you ran the getTrackerAngle detour and appended the marker, it should look like this: +# +# ![Marker position at 0,0](..\images_wiki\Journal_example_marker_origin.PNG) +# +# If you do an analysis and any of the sensors hits the Box object we just created, the list of materials in the result.csv file should say something with "CenterMarker" on it. +# +# #### See more examples of the use of makeCustomObject and appendtoScene on the Bifacial Carport/Canopies Tutorial diff --git a/docs/v3 - Bifacial Carports and Canopies.ipynb b/docs/tutorials/5 - Medium Level Example - Bifacial Carports and Canopies + sampling across a module!.ipynb similarity index 66% rename from docs/v3 - Bifacial Carports and Canopies.ipynb rename to docs/tutorials/5 - Medium Level Example - Bifacial Carports and Canopies + sampling across a module!.ipynb index d06dd4cc..3e661883 100644 --- a/docs/v3 - Bifacial Carports and Canopies.ipynb +++ b/docs/tutorials/5 - Medium Level Example - Bifacial Carports and Canopies + sampling across a module!.ipynb @@ -4,7 +4,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Modeling Carports and Canopies\n", + "# 5 - Medium Level Example - Modeling Carports and Canopies + Sampling accross a Module!\n", "\n", "This journal shows how to model a carport or canopy ~ a fixed structure, usually at a high clearance from the ground, with more than one bifacial solar module in the same inclined-plane to create a \"shade\" for the cars/people below.\n", "\n", @@ -12,15 +12,44 @@ "\n", "These journal outlines 4 useful uses of bifacial_radiance and some tricks: \n", "\n", - "1) Creating the modules in the canopy/carport\n", - "\n", - "2) Adding extra geometry for the pillars/posts supporting the carport/canopy\n", - "\n", - "3) Sampling the rear irradiance and hacking the sensor position to obtain an irradiance map of rear-irradiance. And,\n", + "
    \n", + "
  • Creating the modules in the canopy/carport
  • \n", + "
  • Adding extra geometry for the pillars/posts supporting the carport/canopy
  • \n", + "
  • Sampling the rear irradiance with more resolution (more sensors)
  • \n", + "
  • and hacking the sensor position to obtain an irradiance map of rear-irradiance.
  • \n", + "
  • Adding an object to simulate a car with a specific reflectivity.
  • \n", + "
\n", + "\n", + "This is what we will create:\n", + "![Carport Image We will create](../images_wiki/Carport.png)\n", + "\n", + "### Steps:\n", + "\n", + "
    \n", + "
  1. Setup of Variables through Making OCT Axis
  2. \n", + "
  3. Adding the pillars
  4. \n", + "
  5. Analysis of the collector width
  6. \n", + "
  7. Mapping the irradiance througout all the Carport
  8. \n", + "
  9. Adding a \"Car\"
  10. \n", + "
      \n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 1. Setup of Variables through Making OCT Axis\n", "\n", - "4) Adding a flat surface to simulate a car with a specific reflectivity.\n", + "We've done this before a couple times, no new stuff here. \n", "\n", - "![title](images_wiki/Carport.png)\n" + "The magic is that, for doing the carport we see in the figure, we are going to do a 4-up configuration of modules (**numpanels**), and we are going to repeat that 4-UP 7 times (**nMods**)" ] }, { @@ -29,12 +58,7 @@ "metadata": {}, "outputs": [], "source": [ - "try:\n", - " from bifacial_radiance import *\n", - "except ImportError:\n", - " raise RuntimeError('bifacial_radiance is required. download distribution')\n", - " # Simple example system using Radiance.\n", - " \n", + "from bifacial_radiance import * \n", "import numpy as np" ] }, @@ -116,7 +140,6 @@ "# timestam 4020 : Noon, June 17th.\n", "#demo.genCumSky(demo.epwfile) # Use this instead of gendaylit to simulate the whole year\n", "\n", - "\n", "# Making module with all the variables\n", "moduleDict=demo.makeModule(name=moduletype,x=x,y=y,numpanels = numpanels, xgap=xgap, ygap=ygap)\n", "# create a scene with all the variables\n", @@ -129,46 +152,45 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "If you view the Oct file at this point, you should see the array of 7 modules, with 4 moudles each along the collector widt.\n", + "If you view the Oct file at this point, you should see the array of 7 modules, with 4 modules each along the collector widt." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 2. Adding the pillars\n", "\n", - "### Adding the pillars/posts\n", + "We will add 4 pillars at roughly the back and front corners of the structure. Some of the code below is to calculate the positions of where the pillars will be at.\n", "\n", - "We will add 4 posts at roughly the back and front corners of the structure. Some of the code below is to calculate the positions of where the posts will be at." + "We are calculating the location with some math geometry" ] }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 2, "metadata": {}, "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "! genbox black cuteBox 0.5 0.5 4.966939279485054 | xform -t -0.25 -0.25 0 -t -3.8 -1.8324006105325215 0\n", - "\n", - "Custom Object Name objects\\Post1.rad\n", - "\n", - "!xform -rz 0 objects\\Post1.rad\n", - "\n", - "Custom Object Name objects\\Post2.rad\n", - "\n", - "!xform -rz 0 objects\\Post2.rad\n", - "\n", - "Custom Object Name objects\\Post3.rad\n", - "\n", - "!xform -rz 0 objects\\Post3.rad\n", - "\n", - "Custom Object Name objects\\Post4.rad\n", - "\n", - "!xform -rz 0 objects\\Post4.rad\n", - "Created HotelCarport.oct\n" + "ename": "NameError", + "evalue": "name 'x' is not defined", + "output_type": "error", + "traceback": [ + "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[1;31mNameError\u001b[0m Traceback (most recent call last)", + "\u001b[1;32m\u001b[0m in \u001b[0;36m\u001b[1;34m\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[0mxright\u001b[0m\u001b[1;33m=\u001b[0m \u001b[0mx\u001b[0m\u001b[1;33m*\u001b[0m\u001b[1;36m4\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 2\u001b[0m \u001b[0mxleft\u001b[0m\u001b[1;33m=\u001b[0m \u001b[1;33m-\u001b[0m\u001b[0mxright\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 3\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 4\u001b[0m \u001b[1;31m#centerhubheight = (1.9*3+1.9/2)*np.sin(tilt*np.pi/180)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 5\u001b[0m \u001b[0my2nd\u001b[0m \u001b[1;33m=\u001b[0m \u001b[1;33m-\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0my\u001b[0m\u001b[1;33m*\u001b[0m\u001b[0mnumpanels\u001b[0m\u001b[1;33m/\u001b[0m\u001b[1;36m2\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m*\u001b[0m\u001b[0mnp\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mcos\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mtilt\u001b[0m\u001b[1;33m*\u001b[0m\u001b[0mnp\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mpi\u001b[0m\u001b[1;33m/\u001b[0m\u001b[1;36m180\u001b[0m\u001b[1;33m)\u001b[0m \u001b[1;33m+\u001b[0m \u001b[1;33m(\u001b[0m\u001b[0my\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m*\u001b[0m\u001b[0mnp\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mcos\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mtilt\u001b[0m\u001b[1;33m*\u001b[0m\u001b[0mnp\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mpi\u001b[0m\u001b[1;33m/\u001b[0m\u001b[1;36m180\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", + "\u001b[1;31mNameError\u001b[0m: name 'x' is not defined" ] } ], "source": [ - "import numpy as np\n", - "\n", "xright= x*4\n", "xleft= -xright\n", "\n", @@ -199,7 +221,7 @@ "customObject = demo.makeCustomObject(name,text)\n", "demo.appendtoScene(scene.radfiles, customObject, '!xform -rz 0')\n", "\n", - "octfile = demo.makeOct(demo.getfilelist()) # makeOct combines all of the ground, sky and object files into a .oct file.\n" + "octfile = demo.makeOct(demo.getfilelist()) # makeOct combines all of the ground, sky and object files into a .oct file." ] }, { @@ -212,11 +234,28 @@ "\n", "-pe sets the exposure levels, and -vp sets the view point so the carport is centered (at least on my screen. you can play with the values). It should look like this:\n", "\n", - "![title](images_wiki/Carport.png)\n", + "![Carpport with posts](../images_wiki/Carport.png)\n", "\n", "The post should be coindient with the corners of the array on the high-end of the carport, and on the low end of the carport they should be between the lowest module and the next one. Cute! \n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 3. Analysis of the collector width\n", "\n", - "Now let's do some analysis along the slope of the modules. Each result file will contain irradiance for the 4 modules that make up the slope of the carport. You can select which \"module\" along the row you sample too." + "Now let's do some analysis along the slope of the modules. Each result file will contain irradiance for the 4 modules that make up the slope of the carport. You can select which \"module\" along the row you sample too.\n", + "\n", + "We are also increasign the number of points sampled accross the collector width, with the variable **sensorsy** passed to **moduleanalysis**" ] }, { @@ -253,9 +292,13 @@ "metadata": {}, "source": [ "This is the module analysis and an image of the results file\n", - "![This is the module analysed.](images_wiki/Carport_analysis.png)\n", + "![This is the module analysed.](../images_wiki/Carport_analysis.png)\n", + "\n", + "You can repeat the analysis for any other module in the row:\n", "\n", - "You can repeat the analysis for any other module in the row." + "
      \n", + "Notice we are passing a CUSTOM simulation name so the results are generated in separate csv files.\n", + "
      \n" ] }, { @@ -279,7 +322,6 @@ } ], "source": [ - "# You canr\n", "modWanted = 2\n", "rowWanted = 1\n", "frontscan, backscan = analysis.moduleAnalysis(scene, modWanted=modWanted, rowWanted=rowWanted, sensorsy=sensorsy)\n", @@ -300,8 +342,16 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# How to get an Irradiance Map of the Carport:\n", - "You can hack the sensors starting locations to obtain an irradinace distribution map. This is easier when the modules are facing South, or East/West. Below is an example, you'll have to repeat over all the modules/ all the surface area with as much resolution as you have patience to see edge-effects." + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 4. Mapping the irradiance througout all the Carport (\"Hack\" the sensors) \n", + "\n", + "You can \"hack\" the sensors starting locations to obtain an irradinace distribution map. This is easier when the modules are facing South, or East/West. Below is an example, you'll have to repeat over all the modules/ all the surface area with as much resolution as you have patience to see edge-effects." ] }, { @@ -332,20 +382,28 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# NOTE:\n", - "\n", + "
      \n", "The printed Annual bifacial ratio does not include cleaning of the sensors for the material. Some of the sensros might fall in the spacing between the modules (ygaps) or in the structures added, torquetubes, etc. For a real bifacial ratio gain, use the load and clean functions in load.py. \n", "\n", - "(This process will be automated in a future release TBD)" + "(This process might be automated in a future release TBD)\n", + "
      \n", + "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "# Adding a \"Car\"\n", + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 5. Adding a \"Car\"\n", "\n", - "Add a surface (just like we added the posts) with a specific reflectivity to represent a car. If you are doing hourly simulation you can compare how much the irradiance increases with and without the car, and if you keep track of your parking lot comings/goings this could make an interesting toy-problem: how much are your employees contributing to your rear irradiance production? " + "Add a surface (just like we added the pillars) with a specific reflectivity to represent a car. If you are doing hourly simulation you can compare how much the irradiance increases with and without the car, and if you keep track of your parking lot comings/goings this could make an interesting toy-problem: how much are your employees contributing to your rear irradiance production? " ] }, { @@ -383,7 +441,8 @@ "Viewing with:\n", "## rvu -vf views\\front.vp -e .01 -pe 0.019 -vp 1.5 -14 15 HotelCarport.oct\n", "\n", - "![Behold the Honda-fit sized cube](images_wiki/Carport_with_car.png)" + "\n", + "![Behold the Honda-fit sized cube](../images_wiki/Carport_with_car.png)" ] } ], diff --git a/docs/tutorials/5 - Medium Level Example - Bifacial Carports and Canopies + sampling across a module!.py b/docs/tutorials/5 - Medium Level Example - Bifacial Carports and Canopies + sampling across a module!.py new file mode 100644 index 00000000..d951e067 --- /dev/null +++ b/docs/tutorials/5 - Medium Level Example - Bifacial Carports and Canopies + sampling across a module!.py @@ -0,0 +1,259 @@ +#!/usr/bin/env python +# coding: utf-8 + +# # 5 - Medium Level Example - Modeling Carports and Canopies + Sampling accross a Module! +# +# This journal shows how to model a carport or canopy ~ a fixed structure, usually at a high clearance from the ground, with more than one bifacial solar module in the same inclined-plane to create a "shade" for the cars/people below. +# +# We assume that bifacia_radiacne is already installed in yoru computer. This works for bifacial_radiance v.3 release. +# +# These journal outlines 4 useful uses of bifacial_radiance and some tricks: +# +#
        +#
      • Creating the modules in the canopy/carport
      • +#
      • Adding extra geometry for the pillars/posts supporting the carport/canopy
      • +#
      • Sampling the rear irradiance with more resolution (more sensors)
      • +#
      • and hacking the sensor position to obtain an irradiance map of rear-irradiance.
      • +#
      • Adding an object to simulate a car with a specific reflectivity.
      • +#
      +# +# This is what we will create: +# ![Carport Image We will create](../images_wiki/Carport.png) +# +# ### Steps: +# +#
        +#
      1. Setup of Variables through Making OCT Axis
      2. +#
      3. Adding the pillars
      4. +#
      5. Analysis of the collector width
      6. +#
      7. Mapping the irradiance througout all the Carport
      8. +#
      9. Adding a "Car"
      10. +#
          +# + +# + +# ### 1. Setup of Variables through Making OCT Axis +# +# We've done this before a couple times, no new stuff here. +# +# The magic is that, for doing the carport we see in the figure, we are going to do a 4-up configuration of modules (**numpanels**), and we are going to repeat that 4-UP 7 times (**nMods**) + +# In[1]: + + +from bifacial_radiance import * +import numpy as np + + +# In[2]: + + +testfolder = r'C:\Users\sayala\Documents\RadianceScenes\Demo3' + +timestamp = 4020 # Noon, June 17th. +simulationname = 'HotelCarport' + +# MakeModule Parameters +moduletype='PrismSolar' +numpanels = 4 # Carport will have 4 modules along the y direction (N-S since we are facing it to the south) . +x = 0.95 +y = 1.95 +xgap = 0.15 # Leaving 15 centimeters between modules on x direction +ygap = 0.10 # Leaving 10 centimeters between modules on y direction +zgap = 0 # no gap to torquetube. +sensorsy = 10*numpanels # this will give 70 sensors per module. + +# Other default values: + +# TorqueTube Parameters +axisofrotationTorqueTube=False +torqueTube = False +cellLevelModule = False + +# SceneDict Parameters +gcr = 0.33 # We are only doing 1 row so this doesn't matter +albedo = 0.28 #'concrete' # ground albedo +clearance_height = 4.3 # m +nMods = 7 # six modules length. +nRows = 1 # only 1 row + +azimuth_ang=180 # Facing south +tilt =20 # tilt. + +# Now let's run the example + +demo = RadianceObj(simulationname,path = testfolder) # Create a RadianceObj 'object' +demo.setGround(albedo) # input albedo number or material name like 'concrete'. To see options, run this without any input. +epwfile = demo.getEPW(40.0583,-74.4057) # NJ lat/lon 40.0583° N, 74.4057 +metdata = demo.readEPW(epwfile) # read in the EPW weather data from above +demo.gendaylit(metdata,4020) # Use this to simulate only one hour at a time. +# This allows you to "view" the scene on RVU (see instructions below) +# timestam 4020 : Noon, June 17th. +#demo.genCumSky(demo.epwfile) # Use this instead of gendaylit to simulate the whole year + +# Making module with all the variables +moduleDict=demo.makeModule(name=moduletype,x=x,y=y,numpanels = numpanels, xgap=xgap, ygap=ygap) +# create a scene with all the variables +sceneDict = {'tilt':tilt,'pitch': round(gcr/moduleDict['sceney'],3),'clearance_height':clearance_height,'azimuth':azimuth_ang, 'module_type':moduletype, 'nMods': nMods, 'nRows': nRows} +scene = demo.makeScene(moduletype=moduletype, sceneDict=sceneDict) #makeScene creates a .rad file with 20 modules per row, 7 rows. +octfile = demo.makeOct(demo.getfilelist()) # makeOct combines all of the ground, sky and object fil|es into a .oct file. + + +# If you view the Oct file at this point, you should see the array of 7 modules, with 4 modules each along the collector widt. + +# + +# ### 2. Adding the pillars +# +# We will add 4 pillars at roughly the back and front corners of the structure. Some of the code below is to calculate the positions of where the pillars will be at. +# +# We are calculating the location with some math geometry + +# In[2]: + + +xright= x*4 +xleft= -xright + +#centerhubheight = (1.9*3+1.9/2)*np.sin(tilt*np.pi/180) +y2nd = -(y*numpanels/2)*np.cos(tilt*np.pi/180) + (y)*np.cos(tilt*np.pi/180) +y6th= -(y*numpanels/2)*np.cos(tilt*np.pi/180) + (y*numpanels)*np.cos(tilt*np.pi/180) +z2nd = (y*np.sin(tilt*np.pi/180))+clearance_height +z6th = (y*numpanels)*np.sin(tilt*np.pi/180)+clearance_height + +name='Post1' +text='! genbox black cuteBox 0.5 0.5 {} | xform -t -0.25 -0.25 0 -t {} {} 0'.format(z2nd, xleft, y2nd) +print (text) +customObject = demo.makeCustomObject(name,text) +demo.appendtoScene(radfile=scene.radfiles, customObject=customObject, text="!xform -rz 0") + +name='Post2' +text='! genbox black cuteBox 0.5 0.5 {} | xform -t -0.25 -0.25 0 -t {} {} 0'.format(z2nd, xright, y2nd) +customObject = demo.makeCustomObject(name,text) +demo.appendtoScene(scene.radfiles, customObject, '!xform -rz 0') + +name='Post3' +text='! genbox black cuteBox 0.5 0.5 {} | xform -t -0.25 -0.25 0 -t {} {} 0'.format(z6th, xright, y6th) +customObject = demo.makeCustomObject(name,text) +demo.appendtoScene(scene.radfiles, customObject, '!xform -rz 0') + +name='Post4' +text='! genbox black cuteBox 0.5 0.5 {} | xform -t -0.25 -0.25 0 -t {} {} 0'.format(z6th, xleft, y6th) +customObject = demo.makeCustomObject(name,text) +demo.appendtoScene(scene.radfiles, customObject, '!xform -rz 0') + +octfile = demo.makeOct(demo.getfilelist()) # makeOct combines all of the ground, sky and object files into a .oct file. + + +# ### View the geometry with the posts on : +# +# ## rvu -vf views\front.vp -e .01 -pe 0.4 -vp 3.5 -20 22 HotelCarport.oct +# +# -pe sets the exposure levels, and -vp sets the view point so the carport is centered (at least on my screen. you can play with the values). It should look like this: +# +# ![Carpport with posts](../images_wiki/Carport.png) +# +# The post should be coindient with the corners of the array on the high-end of the carport, and on the low end of the carport they should be between the lowest module and the next one. Cute! +# +# + +# +# + +# ### 3. Analysis of the collector width +# +# Now let's do some analysis along the slope of the modules. Each result file will contain irradiance for the 4 modules that make up the slope of the carport. You can select which "module" along the row you sample too. +# +# We are also increasign the number of points sampled accross the collector width, with the variable **sensorsy** passed to **moduleanalysis** + +# In[4]: + + +analysis = AnalysisObj(octfile, demo.name) # return an analysis object including the scan dimensions for back irradiance +modWanted = 1 +rowWanted = 1 +frontscan, backscan = analysis.moduleAnalysis(scene, modWanted=modWanted, rowWanted=rowWanted, sensorsy=sensorsy) + +analysis.analysis(octfile, simulationname+"Mod1", frontscan, backscan) # compare the back vs front irradiance +print('Annual bifacial ratio average: %0.3f' %( sum(analysis.Wm2Back) / sum(analysis.Wm2Front) ) ) +print("") + + +# This is the module analysis and an image of the results file +# ![This is the module analysed.](../images_wiki/Carport_analysis.png) +# +# You can repeat the analysis for any other module in the row: +# +#
          +# Notice we are passing a CUSTOM simulation name so the results are generated in separate csv files. +#
          +# + +# In[5]: + + +modWanted = 2 +rowWanted = 1 +frontscan, backscan = analysis.moduleAnalysis(scene, modWanted=modWanted, rowWanted=rowWanted, sensorsy=sensorsy) + +analysis.analysis(octfile, simulationname+"Mod2", frontscan, backscan) # compare the back vs front irradiance +print('Annual bifacial ratio average: %0.3f' %( sum(analysis.Wm2Back) / sum(analysis.Wm2Front) ) ) + + +modWanted = 3 +rowWanted = 1 +frontscan, backscan = analysis.moduleAnalysis(scene, modWanted=modWanted, rowWanted=rowWanted, sensorsy=sensorsy) + +analysis.analysis(octfile, simulationname+"Mod3", frontscan, backscan) # compare the back vs front irradiance +print('Annual bifacial ratio average: %0.3f' %( sum(analysis.Wm2Back) / sum(analysis.Wm2Front) ) ) + + +# + +# ### 4. Mapping the irradiance througout all the Carport ("Hack" the sensors) +# +# You can "hack" the sensors starting locations to obtain an irradinace distribution map. This is easier when the modules are facing South, or East/West. Below is an example, you'll have to repeat over all the modules/ all the surface area with as much resolution as you have patience to see edge-effects. + +# In[6]: + + +# HACK Frontscan and Backscan +frontscan['xstart']=-1.2 + +analysis.analysis(octfile, simulationname+"Mod3_point2", frontscan, backscan) # compare the back vs front irradiance +print('Annual bifacial ratio average: %0.3f' %( sum(analysis.Wm2Back) / sum(analysis.Wm2Front) ) ) + + +#
          +# The printed Annual bifacial ratio does not include cleaning of the sensors for the material. Some of the sensros might fall in the spacing between the modules (ygaps) or in the structures added, torquetubes, etc. For a real bifacial ratio gain, use the load and clean functions in load.py. +# +# (This process might be automated in a future release TBD) +#
          +# +# + +# + +# ### 5. Adding a "Car" +# +# Add a surface (just like we added the pillars) with a specific reflectivity to represent a car. If you are doing hourly simulation you can compare how much the irradiance increases with and without the car, and if you keep track of your parking lot comings/goings this could make an interesting toy-problem: how much are your employees contributing to your rear irradiance production? + +# In[7]: + + +name='Car_1' +carpositionx=-2 +carpositiony=-1 +text='! genbox white_EPDM HondaFit 1.6 4.5 1.5 | xform -t -0.8 -2.25 0 -t {} {} 0'.format(carpositionx, carpositiony) +customObject = demo.makeCustomObject(name,text) +demo.appendtoScene(scene.radfiles, customObject, '!xform -rz 0') + +octfile = demo.makeOct(demo.getfilelist()) # makeOct combines all of the ground, sky and object files into a .oct file. + + +# Viewing with: +# ## rvu -vf views\front.vp -e .01 -pe 0.019 -vp 1.5 -14 15 HotelCarport.oct +# +# +# ![Behold the Honda-fit sized cube](../images_wiki/Carport_with_car.png) diff --git a/docs/tutorials/6 - Advanced topics - Understanding trackerdict structure.ipynb b/docs/tutorials/6 - Advanced topics - Understanding trackerdict structure.ipynb new file mode 100644 index 00000000..6e4d35a2 --- /dev/null +++ b/docs/tutorials/6 - Advanced topics - Understanding trackerdict structure.ipynb @@ -0,0 +1,202 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# 6 - Advanced topics: Understanding trackerdict structure\n", + "\n", + "Tutorial 3 gives a good, detailed introduction to the trackerdict structure step by step.\n", + "Here is a condensed summary of functions you can use to explore the tracker dictionary.\n", + "\n", + "\n", + "### Steps:\n", + "\n", + "
            \n", + "
          1. Create a short Simulation + tracker dictionary beginning to end for 1 day
          2. \n", + "
          3. Explore the tracker dictionary
          4. \n", + "
          5. Explore Save Options
          6. \n", + "
          " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 1. Create a short Simulation + tracker dictionary beginning to end for 1 day" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import bifacial_radiance\n", + "\n", + "simulationName = 'Tutorial 3'\n", + "moduletype = 'Custom Cell-Level Module' # We will define the parameters for this below in Step 4.\n", + "testfolder = r'C:\\Users\\sayala\\Documents\\RadianceScenes\\Tutorials\\Journal2'\n", + "albedo = \"litesoil\" # this is one of the options on ground.rad\n", + "lat = 37.5 \n", + "lon = -77.6\n", + "\n", + "# Scene variables\n", + "nMods = 20\n", + "nRows = 7\n", + "hub_height = 2.3 # meters\n", + "pitch = 10 # meters # We will be using pitch instead of GCR for this example.\n", + "\n", + "# Traking parameters\n", + "cumulativesky = False\n", + "limit_angle = 45 # tracker rotation limit angle\n", + "angledelta = 0.01 # we will be doing hourly simulation, we want the angle to be as close to real tracking as possible.\n", + "backtrack = True \n", + "\n", + "#makeModule parameters\n", + "# x and y will be defined later on Step 4 for this tutorial!!\n", + "xgap = 0.01\n", + "ygap = 0.10\n", + "zgap = 0.05\n", + "numpanels = 2\n", + "torquetube = True\n", + "axisofrotationTorqueTube = False\n", + "diameter = 0.1\n", + "tubetype = 'Oct' # This will make an octagonal torque tube.\n", + "material = 'black' # Torque tube of this material (0% reflectivity)\n", + "\n", + "# Simulation range days\n", + "startdate = '11/06' \n", + "enddate = '11/06'\n", + "\n", + "# Cell Parameters\n", + "numcellsx = 6\n", + "numcellsy = 12\n", + "xcell = 0.156\n", + "ycell = 0.156\n", + "xcellgap = 0.02\n", + "ycellgap = 0.02\n", + "\n", + "demo = bifacial_radiance.RadianceObj(simulationName, path=testfolder) \n", + "demo.setGround(albedo) \n", + "epwfile = demo.getEPW(lat,lon) \n", + "metdata = demo.readWeatherFile(epwfile) \n", + "cellLevelModuleParams = {'numcellsx': numcellsx, 'numcellsy':numcellsy, \n", + " 'xcell': xcell, 'ycell': ycell, 'xcellgap': xcellgap, 'ycellgap': ycellgap}\n", + "mymodule = demo.makeModule(name=moduletype, torquetube=torquetube, diameter=diameter, tubetype=tubetype, material=material, \n", + " xgap=xgap, ygap=ygap, zgap=zgap, numpanels=numpanels, \n", + " cellLevelModuleParams=cellLevelModuleParams, \n", + " axisofrotationTorqueTube=axisofrotationTorqueTube)\n", + "sceneDict = {'pitch':pitch,'hub_height':hub_height, 'nMods': nMods, 'nRows': nRows} \n", + "demo.set1axis(limit_angle = limit_angle, backtrack = backtrack, gcr = mymodule['sceney'] / pitch, cumulativesky = cumulativesky)\n", + "demo.gendaylit1axis(startdate=startdate, enddate=enddate)\n", + "demo.makeScene1axis(moduletype=moduletype,sceneDict=sceneDict) #makeScene creates a .rad file with 20 modules per row, 7 rows.\n", + "demo.makeOct1axis()\n", + "demo.analysis1axis()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 2. Explore the tracker dictionary\n", + "\n", + "You can use any of the below options to explore the tracking dictionary. Copy it into an empty cell to see their contents." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "demo.__dict__ # Shows all keys \n", + "\n", + "trackerkeys = sorted(trackerdict.keys()) # get the trackerdict keys to see a specific hour.\n", + "\n", + "demo.trackerdict[trackerkeys[0]] # This prints all trackerdict content\n", + "demo.trackerdict[trackerkeys[0]]['scene'] # This just prints that scene is a Scene object\n", + "demo.trackerdict[trackerkeys[0]]['scene'].__dict__ # This shows the Scene Object contents\n", + "demo.trackerdict[trackerkeys[0]]['scene'].scenex # Addressing one of the variables in the Scene object\n", + "demo.trackerdict[trackerkeys[0]]['scene'].sceneDict # Printing the scene dictionary saved in the Scene Object\n", + "demo.trackerdict[trackerkeys[0]]['scene'].sceneDict['tilt'] # Addressing one of the variables in the scene dictionary\n", + "demo.trackerdict[trackerkeys[0]]['scene'].scene.__dict__ # Swhoing the scene dictionary inside the Scene Object values \n", + "\n", + "# Looking at the AnalysisObj results indivudally\n", + "demo.trackerdict[trackerkeys[0]]['AnalysisObj'] # This just prints that AnalysisObj is an Analysis object\n", + "demo.trackerdict[trackerkeys[0]]['AnalysisObj'].__dict__ # This shows the Analysis Object contents\n", + "demo.trackerdict[trackerkeys[0]]['AnalysisObj'].mattype # Addressing one of the variables in the Analysis Object\n", + "\n", + "# Looking at the Analysis results Accumulated for the day:\n", + "demo.Wm2Back # this value is the addition of every individual irradiance result for each hour simulated.\n", + "\n", + "# THREE WAYS OF CALLING THE SAME THING:\n", + "# (this might be cleaned up/streamlined in following releases.\n", + "demo.trackerdict[trackerkeys[0]]['scene'].scenex\n", + "demo.trackerdict[trackerkeys[0]]['scene'].moduleDict['scenex']\n", + "demo.trackerdict[trackerkeys[0]]['scene'].scene.scenex" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 3. Explore Save Options\n", + "\n", + "The following lines offer ways to save your trackerdict or your demo object." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "demo.exportTrackerDict(trackerdict = demo.trackerdict, savefile = 'results\\\\test_reindexTrue.csv', reindex = False)\n", + "demo.save(savefile = 'results\\\\demopickle.pickle')\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.4" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/tutorials/6 - Advanced topics - Understanding trackerdict structure.py b/docs/tutorials/6 - Advanced topics - Understanding trackerdict structure.py new file mode 100644 index 00000000..7b0efacc --- /dev/null +++ b/docs/tutorials/6 - Advanced topics - Understanding trackerdict structure.py @@ -0,0 +1,135 @@ +#!/usr/bin/env python +# coding: utf-8 + +# # 6 - Advanced topics: Understanding trackerdict structure +# +# Tutorial 3 gives a good, detailed introduction to the trackerdict structure step by step. +# Here is a condensed summary of functions you can use to explore the tracker dictionary. +# +# +# ### Steps: +# +#
            +#
          1. Create a short Simulation + tracker dictionary beginning to end for 1 day
          2. +#
          3. Explore the tracker dictionary
          4. +#
          5. Explore Save Options
          6. +#
          + +# + +# ### 1. Create a short Simulation + tracker dictionary beginning to end for 1 day + +# In[ ]: + + +import bifacial_radiance + +simulationName = 'Tutorial 3' +moduletype = 'Custom Cell-Level Module' # We will define the parameters for this below in Step 4. +testfolder = r'C:\Users\sayala\Documents\RadianceScenes\Tutorials\Journal2' +albedo = "litesoil" # this is one of the options on ground.rad +lat = 37.5 +lon = -77.6 + +# Scene variables +nMods = 20 +nRows = 7 +hub_height = 2.3 # meters +pitch = 10 # meters # We will be using pitch instead of GCR for this example. + +# Traking parameters +cumulativesky = False +limit_angle = 45 # tracker rotation limit angle +angledelta = 0.01 # we will be doing hourly simulation, we want the angle to be as close to real tracking as possible. +backtrack = True + +#makeModule parameters +# x and y will be defined later on Step 4 for this tutorial!! +xgap = 0.01 +ygap = 0.10 +zgap = 0.05 +numpanels = 2 +torquetube = True +axisofrotationTorqueTube = False +diameter = 0.1 +tubetype = 'Oct' # This will make an octagonal torque tube. +material = 'black' # Torque tube of this material (0% reflectivity) + +# Simulation range days +startdate = '11/06' +enddate = '11/06' + +# Cell Parameters +numcellsx = 6 +numcellsy = 12 +xcell = 0.156 +ycell = 0.156 +xcellgap = 0.02 +ycellgap = 0.02 + +demo = bifacial_radiance.RadianceObj(simulationName, path=testfolder) +demo.setGround(albedo) +epwfile = demo.getEPW(lat,lon) +metdata = demo.readWeatherFile(epwfile) +cellLevelModuleParams = {'numcellsx': numcellsx, 'numcellsy':numcellsy, + 'xcell': xcell, 'ycell': ycell, 'xcellgap': xcellgap, 'ycellgap': ycellgap} +mymodule = demo.makeModule(name=moduletype, torquetube=torquetube, diameter=diameter, tubetype=tubetype, material=material, + xgap=xgap, ygap=ygap, zgap=zgap, numpanels=numpanels, + cellLevelModuleParams=cellLevelModuleParams, + axisofrotationTorqueTube=axisofrotationTorqueTube) +sceneDict = {'pitch':pitch,'hub_height':hub_height, 'nMods': nMods, 'nRows': nRows} +demo.set1axis(limit_angle = limit_angle, backtrack = backtrack, gcr = mymodule['sceney'] / pitch, cumulativesky = cumulativesky) +demo.gendaylit1axis(startdate=startdate, enddate=enddate) +demo.makeScene1axis(moduletype=moduletype,sceneDict=sceneDict) #makeScene creates a .rad file with 20 modules per row, 7 rows. +demo.makeOct1axis() +demo.analysis1axis() + + +# + +# ### 2. Explore the tracker dictionary +# +# You can use any of the below options to explore the tracking dictionary. Copy it into an empty cell to see their contents. + +# In[ ]: + + +demo.__dict__ # Shows all keys + +trackerkeys = sorted(trackerdict.keys()) # get the trackerdict keys to see a specific hour. + +demo.trackerdict[trackerkeys[0]] # This prints all trackerdict content +demo.trackerdict[trackerkeys[0]]['scene'] # This just prints that scene is a Scene object +demo.trackerdict[trackerkeys[0]]['scene'].__dict__ # This shows the Scene Object contents +demo.trackerdict[trackerkeys[0]]['scene'].scenex # Addressing one of the variables in the Scene object +demo.trackerdict[trackerkeys[0]]['scene'].sceneDict # Printing the scene dictionary saved in the Scene Object +demo.trackerdict[trackerkeys[0]]['scene'].sceneDict['tilt'] # Addressing one of the variables in the scene dictionary +demo.trackerdict[trackerkeys[0]]['scene'].scene.__dict__ # Swhoing the scene dictionary inside the Scene Object values + +# Looking at the AnalysisObj results indivudally +demo.trackerdict[trackerkeys[0]]['AnalysisObj'] # This just prints that AnalysisObj is an Analysis object +demo.trackerdict[trackerkeys[0]]['AnalysisObj'].__dict__ # This shows the Analysis Object contents +demo.trackerdict[trackerkeys[0]]['AnalysisObj'].mattype # Addressing one of the variables in the Analysis Object + +# Looking at the Analysis results Accumulated for the day: +demo.Wm2Back # this value is the addition of every individual irradiance result for each hour simulated. + +# THREE WAYS OF CALLING THE SAME THING: +# (this might be cleaned up/streamlined in following releases. +demo.trackerdict[trackerkeys[0]]['scene'].scenex +demo.trackerdict[trackerkeys[0]]['scene'].moduleDict['scenex'] +demo.trackerdict[trackerkeys[0]]['scene'].scene.scenex + + +# + +# ### 3. Explore Save Options +# +# The following lines offer ways to save your trackerdict or your demo object. + +# In[ ]: + + +demo.exportTrackerDict(trackerdict = demo.trackerdict, savefile = 'results\\test_reindexTrue.csv', reindex = False) +demo.save(savefile = 'results\\demopickle.pickle') + diff --git a/docs/v3 - Multiple SceneObjects Example.ipynb b/docs/tutorials/7 - Advanced topics - Multiple SceneObjects Example.ipynb similarity index 61% rename from docs/v3 - Multiple SceneObjects Example.ipynb rename to docs/tutorials/7 - Advanced topics - Multiple SceneObjects Example.ipynb index ffe93104..720898e9 100644 --- a/docs/v3 - Multiple SceneObjects Example.ipynb +++ b/docs/tutorials/7 - Advanced topics - Multiple SceneObjects Example.ipynb @@ -1,28 +1,54 @@ { "cells": [ { - "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ - "## Multiple SceneObjects\n", + "# 7 - Advanced topics - Multiple SceneObjects Example\n", "\n", "This journal shows how to:\n", "\n", - "1) Create multiple scene objects in the same scene.\n", + "
            \n", + "
          • Create multiple scene objects in the same scene.
          • \n", + "
          • Analyze multiple scene objects in the same scene
          • \n", + "
          • Add a marker to find the origin (0,0) on a scene (for sanity-checks/visualization).
          • \n", "\n", - "2) Analyze multiple scene objects in the same scene\n", + "A scene Object is defined as an array of modules, with whatever parameters you want to give it. In this case, we are modeling one array of 2 rows of 5 modules in landscape, and one array of 1 row of 5 modules in 2-UP, portrait configuration, as the image below:\n", "\n", - "3) Add a marker to find the origin (0,0) on a scene (for sanity-checks/visualization).\n", + "![multiple Scene Objects Example](..\\images_wiki\\Journal_example_multiple_objects.PNG)\n", "\n", - "A scene Object is defined as an array of modules, with whatever paremeters you want to give it. In this case, we are modeling one array of 2 rows of 5 modules in landscape, and one array of 1 row of 5 modules in 2-UP, portrait configuration, as the image below:\n", "\n", - "![multiple Scene Objects Example](images_wiki\\Journal_example_multiple_objects.PNG)\n" + "### Steps:\n", + "\n", + "
              \n", + "
            1. Generating the setups
            2. \n", + "
                \n", + "
              1. Generating the firt scene object
              2. \n", + "
              3. Generating the second scene object.
              4. \n", + "
              \n", + "
            3. Add a Marker at the Origin (coordinates 0,0) for help with visualization
            4. \n", + "
            5. Combine all scene Objects into one OCT file & Visualize
            6. \n", + "
            7. Analysis for Each sceneObject
            8. \n", + "
            " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 1. Generating the Setups" ] }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 3, "metadata": {}, "outputs": [ { @@ -35,34 +61,35 @@ ], "source": [ "import os\n", - "testfolder = os.path.abspath(r'..\\bifacial_radiance\\TEMP') \n", - "\n", - "# You can alternatively point to an empty directory (it will open a load GUI Visual Interface)\n", - "# or specify any other directory in your computer. I.E.:\n", - "# testfolder = r'C:\\Users\\sayala\\Documents\\RadianceScenes\\Demo'\n", + "import numpy as np\n", + "testfolder = os.path.abspath(r'..\\..\\bifacial_radiance\\TEMP') \n", "\n", - "print (\"Your simulation will be stored in %s\" % testfolder)" + "print (\"Your simulation will be stored in %s\" % testfolder)\n", + " \n", + "from bifacial_radiance import RadianceObj, AnalysisObj " ] }, { - "cell_type": "code", - "execution_count": 2, + "cell_type": "markdown", "metadata": {}, - "outputs": [], "source": [ - "try:\n", - " from bifacial_radiance import RadianceObj, AnalysisObj\n", - "except ImportError:\n", - " raise RuntimeError('bifacial_radiance is required. download distribution')\n", - " # Simple example system using Radiance.\n", - "import numpy as np\n", + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### A. Generating the firt scene object\n", "\n", - "\n" + "This is a standard fixed-tilt setup for one hour. Gencumsky could be used too for the whole year.\n", + "\n", + "The key here is that we are setting in sceneDict the variable **appendRadfile** to true." ] }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 4, "metadata": {}, "outputs": [ { @@ -71,47 +98,37 @@ "text": [ "path = C:\\Users\\sayala\\Documents\\GitHub\\bifacial_radiance\\bifacial_radiance\\TEMP\n", "Getting weather file: USA_VA_Richmond.Intl.AP.724010_TMY.epw\n", - " ... OK!\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "c:\\users\\sayala\\documents\\github\\bifacial_radiance\\bifacial_radiance\\main.py:2447: pvlibDeprecationWarning: The get_sun_rise_set_transit function was deprecated in pvlib 0.6.1 and will be removed in 0.7. Use sun_rise_set_transit_spa instead.\n", - " sunup= pvlib.irradiance.solarposition.get_sun_rise_set_transit(datetimetz, lat, lon) #only for pvlib <0.6.1\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ + " ... OK!\n", "\n", "Module Name: Prism_Solar_Bi60_landscape\n", - "REWRITING pre-existing module file. \n", + "Module file did not exist before, creating new module file\n", "Module Prism Solar Bi60 landscape successfully created\n" ] } ], "source": [ - "# GEnerating Standard FIXED SETUP\n", "demo = RadianceObj(\"MultipleObj\", path = testfolder) # Create a RadianceObj 'object'\n", "demo.setGround(0.62)\n", "epwfile = demo.getEPW(lat = 37.5, lon = -77.6) \n", - "metdata = demo.readEPW('EPWs\\\\USA_VA_Richmond.Intl.AP.724010_TMY.epw') \n", - "#metdata = demo.readEPW('EPWs\\\\USA_VA_Richmond.Intl.AP.724010_TMY.epw') # read in the weather data directly\n", + "metdata = demo.readWeatherFile('EPWs\\\\USA_VA_Richmond.Intl.AP.724010_TMY.epw') \n", "fullYear = True\n", - "#demo.genCumSky(demo.epwfile) # entire year.\n", - "demo.gendaylit(metdata,4020) # Noon, June 17th \n", + "demo.gendaylit(metdata,4020) # Noon, June 17th . # Gencumsky could be used too.\n", "module_type = 'Prism Solar Bi60 landscape' \n", - "demo.makeModule(name=module_type,y=1,x=1.7,bifi = 0.90)\n", + "demo.makeModule(name=module_type,y=1,x=1.7)\n", "sceneDict = {'tilt':10,'pitch':1.5,'clearance_height':0.2,'azimuth':180, 'nMods': 5, 'nRows': 2, 'appendRadfile':True} \n", - "sceneObj1 = demo.makeScene(module_type,sceneDict) \n" + "sceneObj1 = demo.makeScene(module_type,sceneDict) " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Checking values after Scene for the scene Object created" ] }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 5, "metadata": {}, "outputs": [ { @@ -119,15 +136,14 @@ "output_type": "stream", "text": [ "SceneObj1 modulefile: objects\\Prism_Solar_Bi60_landscape.rad\n", - "SceneObj1 SceneFile: objects\\Prism_Solar_Bi60_landscape_0.2_1.5_10_5x2.rad\n", + "SceneObj1 SceneFile: objects\\Prism_Solar_Bi60_landscape_0.2_1.5_10_5x2_origin0,0.rad\n", "SceneObj1 GCR: 0.67\n", "FileLists: \n", - " ['materials\\\\ground.rad', 'skies\\\\sky2_37.5_-77.33_06_17_13.rad', 'objects\\\\Prism_Solar_Bi60_landscape_0.2_1.5_10_5x2.rad']\n" + " ['materials\\\\ground.rad', 'skies\\\\sky2_37.5_-77.33_06_17_13.rad', 'objects\\\\Prism_Solar_Bi60_landscape_0.2_1.5_10_5x2_origin0,0.rad']\n" ] } ], "source": [ - "# Checking values after Scene\n", "print (\"SceneObj1 modulefile: %s\" % sceneObj1.modulefile)\n", "print (\"SceneObj1 SceneFile: %s\" %sceneObj1.radfiles)\n", "print (\"SceneObj1 GCR: %s\" % round(sceneObj1.gcr,2))\n", @@ -138,13 +154,22 @@ "cell_type": "markdown", "metadata": {}, "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### B. Generating the second scene object.\n", + "\n", "Creating a different Scene. Same Module, different values.\n", - "Notice we are passing a originx and originy to displace the center of this new sceneObj to that location.\n" + "Notice we are passing a different **originx** and **originy** to displace the center of this new sceneObj to that location.\n" ] }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 6, "metadata": {}, "outputs": [ { @@ -153,21 +178,22 @@ "text": [ "\n", "Module Name: Module2\n", - "REWRITING pre-existing module file. \n", + "Module file did not exist before, creating new module file\n", "Module Module2 successfully created\n" ] } ], "source": [ - "sceneDict2 = {'tilt':30,'pitch':5,'clearance_height':1,'azimuth':180, 'nMods': 5, 'nRows': 1, 'originx': 0, 'originy': 3.5, 'appendRadfile':True} \n", - "module_type2='Module2'\n", + "sceneDict2 = {'tilt':30,'pitch':5,'clearance_height':1,'azimuth':180, \n", + " 'nMods': 5, 'nRows': 1, 'originx': 0, 'originy': 3.5, 'appendRadfile':True} \n", + "module_type2='Longi'\n", "demo.makeModule(name=module_type2,x=1,y=1.6, numpanels=2, ygap=0.15)\n", "sceneObj2 = demo.makeScene(module_type2,sceneDict2) \n" ] }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 7, "metadata": {}, "outputs": [ { @@ -175,14 +201,14 @@ "output_type": "stream", "text": [ "SceneObj1 modulefile: objects\\Prism_Solar_Bi60_landscape.rad\n", - "SceneObj1 SceneFile: objects\\Prism_Solar_Bi60_landscape_0.2_1.5_10_5x2.rad\n", + "SceneObj1 SceneFile: objects\\Prism_Solar_Bi60_landscape_0.2_1.5_10_5x2_origin0,0.rad\n", "SceneObj1 GCR: 0.67\n", "\n", "SceneObj2 modulefile: objects\\Module2.rad\n", - "SceneObj2 SceneFile: objects\\Module2_1.0_5_30_5x1.rad\n", + "SceneObj2 SceneFile: objects\\Module2_1.0_5_30_5x1_origin0,3.5.rad\n", "SceneObj2 GCR: 0.67\n", "NEW FileLists: \n", - " ['materials\\\\ground.rad', 'skies\\\\sky2_37.5_-77.33_06_17_13.rad', 'objects\\\\Prism_Solar_Bi60_landscape_0.2_1.5_10_5x2.rad', 'objects\\\\Module2_1.0_5_30_5x1.rad']\n" + " ['materials\\\\ground.rad', 'skies\\\\sky2_37.5_-77.33_06_17_13.rad', 'objects\\\\Prism_Solar_Bi60_landscape_0.2_1.5_10_5x2_origin0,0.rad', 'objects\\\\Module2_1.0_5_30_5x1_origin0,3.5.rad']\n" ] } ], @@ -204,13 +230,25 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "#### Optional: Add a Marker at the Origin (coordinates 0,0) for help with visualization\n", - "I usually use this to create \"markers\" for the geometry to orient myself when doing sanity-checks (for example, marke where 0,0 is, or where 5,0 coordinate is). That is what I am doing here, for the image I'm attaching." + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 2. Add a Marker at the Origin (coordinates 0,0) for help with visualization\n", + "\n", + "Creating a \"markers\" for the geometry is useful to orient one-self when doing sanity-checks (for example, marke where 0,0 is, or where 5,0 coordinate is).\n", + "\n", + "
            \n", + "Note that if you analyze the module that intersects with the marker, some of the sensors will be wrong. To perform valid analysis, do so without markers, as they are 'real' objects on your scene. \n", + "
            \n" ] }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 8, "metadata": { "scrolled": true }, @@ -220,14 +258,12 @@ "output_type": "stream", "text": [ "\n", - "Custom Object Name objects\\Post1.rad\n", - "\n", - "!xform -rz 0 objects\\Post1.rad\n" + "Custom Object Name objects\\Post1.rad\n" ] } ], "source": [ - "# NOTE: offseting translation by 0.1 so the center of the marker is at the desired coordinate.\n", + "# NOTE: offsetting translation by 0.1 so the center of the marker (with sides of 0.2) is at the desired coordinate.\n", "name='Post1'\n", "text='! genbox black originMarker 0.2 0.2 1 | xform -t -0.1 -0.1 0'\n", "customObject = demo.makeCustomObject(name,text)\n", @@ -238,13 +274,21 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Combine all scene Objects into one OCT file \n", + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 3. Combine all scene Objects into one OCT file & Visualize\n", + "Marking this as its own steps because this is the step that joins our Scene Objects 1, 2 and the appended Post.\n", "Run makeOCT to make the scene with both scene objects AND the marker in it, the ground and the skies." ] }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 9, "metadata": {}, "outputs": [ { @@ -267,15 +311,30 @@ "\n", "##### rvu -vf views\\front.vp -e .01 -pe 0.3 -vp 1 -7.5 12 MultipleObj.oct\n", "\n", - "And then proceed happily with your analysis. \n", + "It should look something like this:\n", + "\n", + "![multiple Scene Objects Example](..\\images_wiki\\Journal_example_multiple_objects.PNG)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 4. Analysis for Each sceneObject\n", "\n", - "### Analysis for Each sceneObject:\n", - "sceneDict is saved for each scene. When calling the Analysis, you should reference the scene object you want.\n" + "a **sceneDict** is saved for each scene. When calling the Analysis, you should reference the scene object you want." ] }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 10, "metadata": {}, "outputs": [ { @@ -293,7 +352,7 @@ " 'originy': 0}" ] }, - "execution_count": 9, + "execution_count": 10, "metadata": {}, "output_type": "execute_result" } @@ -304,7 +363,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 11, "metadata": {}, "outputs": [ { @@ -322,7 +381,7 @@ " 'axis_tilt': 0}" ] }, - "execution_count": 10, + "execution_count": 11, "metadata": {}, "output_type": "execute_result" } @@ -333,7 +392,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 13, "metadata": {}, "outputs": [ { @@ -343,7 +402,7 @@ "Linescan in process: FirstObj_Front\n", "Linescan in process: FirstObj_Back\n", "Saved: results\\irr_FirstObj.csv\n", - "Annual bifacial ratio First Set of Panels: 0.163 \n" + "Annual bifacial ratio First Set of Panels: 0.162 \n" ] } ], @@ -354,9 +413,17 @@ "print('Annual bifacial ratio First Set of Panels: %0.3f ' %( np.mean(analysis.Wm2Back) / np.mean(analysis.Wm2Front)) )" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's do a Sanity check for first object:\n", + "Since we didn't pass any desired module, it should grab the center module of the center row (rounding down). For 2 rows and 5 modules, that is row 1, module 3 ~ indexed at 0, a2.0.a0.PVmodule.....\"\"" + ] + }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 14, "metadata": {}, "outputs": [ { @@ -372,8 +439,6 @@ } ], "source": [ - "# Sanity check for first obect. Since we didn't pass any desired module, it should grab the center module of the center row (rounding down)\n", - "# for 2 rows, that is row 1, module 5 ~ indexed at 0, a4.0.a0.PVmodule.....\"\"\n", "print (frontdict['x'])\n", "print (\"\")\n", "print (frontdict['y'])\n", @@ -381,9 +446,16 @@ "print (frontdict['mattype'])" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's analyze a module in sceneobject 2 now. Remember we can specify which module/row we want. We only have one row in this Object though.\n" + ] + }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 19, "metadata": {}, "outputs": [ { @@ -393,14 +465,13 @@ "Linescan in process: SecondObj_Front\n", "Linescan in process: SecondObj_Back\n", "Saved: results\\irr_SecondObj.csv\n", - "Annual bifacial ratio Second Set of Panels: 0.330 \n" + "Annual bifacial ratio Second Set of Panels: 0.322 \n" ] } ], "source": [ "analysis = AnalysisObj(octfile, demo.basename) \n", - "# Remember we can specify which module/row we want. We only have one row in this Object though.\n", - "modWanted = 2\n", + "modWanted = 4\n", "rowWanted = 1\n", "sensorsy=4\n", "frontscan, backscan = analysis.moduleAnalysis(sceneObj2, modWanted = modWanted, rowWanted = rowWanted, sensorsy=sensorsy)\n", @@ -412,32 +483,41 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Sanity check for first obect. Since we didn't pass any desired module, it should grab the center module of the center row (rounding down). For 1 rows, that is row 0, module 3 ~ indexed at 0, a2.0.a0.PVmodule... and a2.0.a1.PVmodule since it is a 2-UP system.\n" + "Sanity check for first object. Since we didn't pass any desired module, it should grab the center module of the center row (rounding down). For 1 rows, that is row 0, module 4 ~ indexed at 0, a3.0.a0.Longi... and a3.0.a1.Longi since it is a 2-UP system.\n" ] }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 23, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "[-1.01, -1.01, -1.01, -1.01]\n", + "x coordinate points: [1.01, 1.01, 1.01, 1.01]\n", "\n", - "[2.629644, 3.209881, 3.790119, 4.370356]\n", + "y coordinate points: [2.629644, 3.209881, 3.790119, 4.370356]\n", "\n", - "['a1.0.a0.Module2.6457', 'a1.0.a0.Module2.6457', 'a1.0.a1.Module2.6457', 'a1.0.a1.Module2.6457']\n" + "Elements intersected at each point: ['a3.0.a0.Module2.6457', 'a3.0.a0.Module2.6457', 'a3.0.a1.Module2.6457', 'a3.0.a1.Module2.6457']\n" ] } ], "source": [ - "print (frontdict2['x'])\n", + "print (\"x coordinate points:\" , frontdict2['x'])\n", "print (\"\")\n", - "print (frontdict2['y'])\n", + "print (\"y coordinate points:\", frontdict2['y'])\n", "print (\"\")\n", - "print (frontdict2['mattype'])" + "print (\"Elements intersected at each point: \", frontdict2['mattype'])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Visualizing the coordinates and module analyzed with an image:\n", + " \n", + "![multiple Scene Objects Example](..\\images_wiki\\AdvancedJournals\\MultipleSceneObject_AnalysingSceneObj2_Row1_Module4.PNG)\n" ] } ], diff --git a/docs/tutorials/7 - Advanced topics - Multiple SceneObjects Example.py b/docs/tutorials/7 - Advanced topics - Multiple SceneObjects Example.py new file mode 100644 index 00000000..553350ed --- /dev/null +++ b/docs/tutorials/7 - Advanced topics - Multiple SceneObjects Example.py @@ -0,0 +1,228 @@ +#!/usr/bin/env python +# coding: utf-8 + +# # 7 - Advanced topics - Multiple SceneObjects Example +# +# This journal shows how to: +# +#
              +#
            • Create multiple scene objects in the same scene.
            • +#
            • Analyze multiple scene objects in the same scene
            • +#
            • Add a marker to find the origin (0,0) on a scene (for sanity-checks/visualization).
            • +# +# A scene Object is defined as an array of modules, with whatever parameters you want to give it. In this case, we are modeling one array of 2 rows of 5 modules in landscape, and one array of 1 row of 5 modules in 2-UP, portrait configuration, as the image below: +# +# ![multiple Scene Objects Example](..\images_wiki\Journal_example_multiple_objects.PNG) +# +# +# ### Steps: +# +#
                +#
              1. Generating the setups
              2. +#
                  +#
                1. Generating the firt scene object
                2. +#
                3. Generating the second scene object.
                4. +#
                +#
              3. Add a Marker at the Origin (coordinates 0,0) for help with visualization
              4. +#
              5. Combine all scene Objects into one OCT file & Visualize
              6. +#
              7. Analysis for Each sceneObject
              8. +#
              + +# + +# ### 1. Generating the Setups + +# In[3]: + + +import os +import numpy as np +testfolder = os.path.abspath(r'..\..\bifacial_radiance\TEMP') + +print ("Your simulation will be stored in %s" % testfolder) + +from bifacial_radiance import RadianceObj, AnalysisObj + + +# + +# ### A. Generating the firt scene object +# +# This is a standard fixed-tilt setup for one hour. Gencumsky could be used too for the whole year. +# +# The key here is that we are setting in sceneDict the variable **appendRadfile** to true. + +# In[4]: + + +demo = RadianceObj("MultipleObj", path = testfolder) # Create a RadianceObj 'object' +demo.setGround(0.62) +epwfile = demo.getEPW(lat = 37.5, lon = -77.6) +metdata = demo.readWeatherFile('EPWs\\USA_VA_Richmond.Intl.AP.724010_TMY.epw') +fullYear = True +demo.gendaylit(metdata,4020) # Noon, June 17th . # Gencumsky could be used too. +module_type = 'Prism Solar Bi60 landscape' +demo.makeModule(name=module_type,y=1,x=1.7) +sceneDict = {'tilt':10,'pitch':1.5,'clearance_height':0.2,'azimuth':180, 'nMods': 5, 'nRows': 2, 'appendRadfile':True} +sceneObj1 = demo.makeScene(module_type,sceneDict) + + +# Checking values after Scene for the scene Object created + +# In[5]: + + +print ("SceneObj1 modulefile: %s" % sceneObj1.modulefile) +print ("SceneObj1 SceneFile: %s" %sceneObj1.radfiles) +print ("SceneObj1 GCR: %s" % round(sceneObj1.gcr,2)) +print ("FileLists: \n %s" % demo.getfilelist()) + + +# + +# ### B. Generating the second scene object. +# +# Creating a different Scene. Same Module, different values. +# Notice we are passing a different **originx** and **originy** to displace the center of this new sceneObj to that location. +# + +# In[6]: + + +sceneDict2 = {'tilt':30,'pitch':5,'clearance_height':1,'azimuth':180, + 'nMods': 5, 'nRows': 1, 'originx': 0, 'originy': 3.5, 'appendRadfile':True} +module_type2='Longi' +demo.makeModule(name=module_type2,x=1,y=1.6, numpanels=2, ygap=0.15) +sceneObj2 = demo.makeScene(module_type2,sceneDict2) + + +# In[7]: + + +# Checking values for both scenes after creating new SceneObj +print ("SceneObj1 modulefile: %s" % sceneObj1.modulefile) +print ("SceneObj1 SceneFile: %s" %sceneObj1.radfiles) +print ("SceneObj1 GCR: %s" % round(sceneObj1.gcr,2)) + +print ("\nSceneObj2 modulefile: %s" % sceneObj2.modulefile) +print ("SceneObj2 SceneFile: %s" %sceneObj2.radfiles) +print ("SceneObj2 GCR: %s" % round(sceneObj2.gcr,2)) + +#getfilelist should have info for the rad file created by BOTH scene objects. +print ("NEW FileLists: \n %s" % demo.getfilelist()) + + +# + +# ### 2. Add a Marker at the Origin (coordinates 0,0) for help with visualization +# +# Creating a "markers" for the geometry is useful to orient one-self when doing sanity-checks (for example, marke where 0,0 is, or where 5,0 coordinate is). +# +#
              +# Note that if you analyze the module that intersects with the marker, some of the sensors will be wrong. To perform valid analysis, do so without markers, as they are 'real' objects on your scene. +#
              +# + +# In[8]: + + +# NOTE: offsetting translation by 0.1 so the center of the marker (with sides of 0.2) is at the desired coordinate. +name='Post1' +text='! genbox black originMarker 0.2 0.2 1 | xform -t -0.1 -0.1 0' +customObject = demo.makeCustomObject(name,text) +demo.appendtoScene(sceneObj1.radfiles, customObject, '!xform -rz 0') + + +# + +# ### 3. Combine all scene Objects into one OCT file & Visualize +# Marking this as its own steps because this is the step that joins our Scene Objects 1, 2 and the appended Post. +# Run makeOCT to make the scene with both scene objects AND the marker in it, the ground and the skies. + +# In[9]: + + +octfile = demo.makeOct(demo.getfilelist()) + + +# At this point you should be able to go into a command window (cmd.exe) and check the geometry. Example: +# +# ##### rvu -vf views\front.vp -e .01 -pe 0.3 -vp 1 -7.5 12 MultipleObj.oct +# +# It should look something like this: +# +# ![multiple Scene Objects Example](..\images_wiki\Journal_example_multiple_objects.PNG) +# + +# + +# ### 4. Analysis for Each sceneObject +# +# a **sceneDict** is saved for each scene. When calling the Analysis, you should reference the scene object you want. + +# In[10]: + + +sceneObj1.sceneDict + + +# In[11]: + + +sceneObj2.sceneDict + + +# In[13]: + + +analysis = AnalysisObj(octfile, demo.basename) +frontscan, backscan = analysis.moduleAnalysis(sceneObj1) +frontdict, backdict = analysis.analysis(octfile, "FirstObj", frontscan, backscan) # compare the back vs front irradiance +print('Annual bifacial ratio First Set of Panels: %0.3f ' %( np.mean(analysis.Wm2Back) / np.mean(analysis.Wm2Front)) ) + + +# Let's do a Sanity check for first object: +# Since we didn't pass any desired module, it should grab the center module of the center row (rounding down). For 2 rows and 5 modules, that is row 1, module 3 ~ indexed at 0, a2.0.a0.PVmodule....."" + +# In[14]: + + +print (frontdict['x']) +print ("") +print (frontdict['y']) +print ("") +print (frontdict['mattype']) + + +# Let's analyze a module in sceneobject 2 now. Remember we can specify which module/row we want. We only have one row in this Object though. +# + +# In[19]: + + +analysis = AnalysisObj(octfile, demo.basename) +modWanted = 4 +rowWanted = 1 +sensorsy=4 +frontscan, backscan = analysis.moduleAnalysis(sceneObj2, modWanted = modWanted, rowWanted = rowWanted, sensorsy=sensorsy) +frontdict2, backdict2 = analysis.analysis(octfile, "SecondObj", frontscan, backscan) # compare the back vs front irradiance +print('Annual bifacial ratio Second Set of Panels: %0.3f ' %( np.mean(analysis.Wm2Back) / np.mean(analysis.Wm2Front)) ) + + +# Sanity check for first object. Since we didn't pass any desired module, it should grab the center module of the center row (rounding down). For 1 rows, that is row 0, module 4 ~ indexed at 0, a3.0.a0.Longi... and a3.0.a1.Longi since it is a 2-UP system. +# + +# In[23]: + + +print ("x coordinate points:" , frontdict2['x']) +print ("") +print ("y coordinate points:", frontdict2['y']) +print ("") +print ("Elements intersected at each point: ", frontdict2['mattype']) + + +# Visualizing the coordinates and module analyzed with an image: +# +# ![multiple Scene Objects Example](..\images_wiki\AdvancedJournals\MultipleSceneObject_AnalysingSceneObj2_Row1_Module4.PNG) +# diff --git a/docs/tutorials/8 - Advanced topics - Calculating Power Output and Electrical Mismatch.ipynb b/docs/tutorials/8 - Advanced topics - Calculating Power Output and Electrical Mismatch.ipynb new file mode 100644 index 00000000..359e07ed --- /dev/null +++ b/docs/tutorials/8 - Advanced topics - Calculating Power Output and Electrical Mismatch.ipynb @@ -0,0 +1,201 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# 8 - Advanced topics - Calculating Power Output and Electrical Mismatch\n", + "\n", + "Nonuniform rear-irradiance on bifacial PV systems can cause additional mismatch loss, which may not be appropriately captured in PV energy production estimates and software.\n", + "\n", + "\n", + "\n", + "The **analysis.py** module in bifacial_radiance comes with functions to calculate power output, electrical mismatch, and some other irradiance calculations. This is the procedure used for this proceedings and submitted journals, which have much more detail on the procedure. \n", + "\n", + " •\tDeline, C., Ayala Pelaez, S., MacAlpine, S., Olalla, C. Estimating and Parameterizing Mismatch Power Loss in Bifacial Photovoltaic Systems. (submitted Progress in PV on Sept. 30, 2019)\n", + "\n", + " •\tDeline C, Ayala Pelaez S, MacAlpine S, Olalla C. Bifacial PV System Mismatch Loss Estimation & Parameterization. Presented in: 36th EU PVSEC, Marseille Fr. Slides: https://www.nrel.gov/docs/fy19osti/74885.pdf. Proceedings: https://www.nrel.gov/docs/fy20osti/73541.pdf\n", + "\n", + " •\tAyala Pelaez S, Deline C, MacAlpine S, Olalla C. Bifacial PV system mismatch loss estimation. Poster presented at the 6th BifiPV Workshop, Amsterdam 2019. https://www.nrel.gov/docs/fy19osti/74831.pdf and http://bifipv-workshop.com/index.php?id=amsterdam-2019-program \n", + "\n", + "Ideally **mismatch losses M** should be calculated for the whole year, and then the **mismatch loss factor to apply to Grear \"Lrear\"** required by due diligence softwares can be calculated:\n", + "\n", + "\n", + "\n", + "In this journal we will explore calculating mismatch loss M for a reduced set of hours. A procedure similar to that in Tutorial 3 will be used to generate various hourly irradiance measurements in the results folder, which the mismatch.py module will load and analyze. Analysis is done with PVMismatch, so this must be installed.\n", + "\n", + "## STEPS:\n", + " 1. Run an hourly simulation\n", + " 2. Do mismatch analysis on the results.\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 1. Run an hourly simulation\n", + "\n", + "This will generate the results over which we will perform the mismatch analysis. Here we are doing only 1 day to make this 'fater'." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import bifacial_radiance\n", + "import os \n", + "\n", + "simulationName = 'Tutorial 8'\n", + "testfolder = os.path.abspath(r'..\\..\\bifacial_radiance\\TEMP')\n", + "moduletype = \"Canadian Solar\"\n", + "albedo = 0.25 \n", + "lat = 37.5 \n", + "lon = -77.6\n", + "\n", + "# Scene variables\n", + "nMods = 20\n", + "nRows = 7\n", + "hub_height = 1.5 # meters\n", + "gcr = 0.33\n", + "\n", + "# Traking parameters\n", + "cumulativesky = False\n", + "limit_angle = 60 \n", + "angledelta = 0.01 \n", + "backtrack = True \n", + "\n", + "#makeModule parameters\n", + "x = 1\n", + "y = 2\n", + "xgap = 0.01\n", + "zgap = 0.05\n", + "ygap = 0.0 # numpanels=1 anyways so it doesnt matter anyway\n", + "numpanels = 1\n", + "torquetube = True\n", + "axisofrotationTorqueTube = True\n", + "diameter = 0.1\n", + "tubetype = 'Oct' \n", + "material = 'black'\n", + "\n", + "# Analysis parmaeters\n", + "startdate = '11/06' \n", + "enddate = '11/06'\n", + "sensorsy = 12\n", + "\n", + "demo = bifacial_radiance.RadianceObj(simulationName, path=testfolder) \n", + "demo.setGround(albedo) \n", + "epwfile = demo.getEPW(lat,lon) \n", + "metdata = demo.readWeatherFile(epwfile) \n", + "mymodule = demo.makeModule(name=moduletype, torquetube=torquetube, diameter=diameter, tubetype=tubetype, material=material, \n", + " x=x, y=y, xgap=xgap, ygap = ygap, zgap=zgap, numpanels=numpanels, \n", + " axisofrotationTorqueTube=axisofrotationTorqueTube)\n", + "pitch = mymodule['sceney']/gcr\n", + "sceneDict = {'pitch':pitch,'hub_height':hub_height, 'nMods': nMods, 'nRows': nRows} \n", + "demo.set1axis(limit_angle = limit_angle, backtrack = backtrack, gcr = gcr, cumulativesky = cumulativesky)\n", + "demo.gendaylit1axis(startdate=startdate, enddate=enddate)\n", + "demo.makeScene1axis(moduletype=moduletype,sceneDict=sceneDict) \n", + "demo.makeOct1axis()\n", + "demo.analysis1axis(sensorsy = sensorsy)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 2. Do mismatch analysis on the results\n", + "\n", + "There are various things that we need to know about the module at this stage.\n", + "\n", + "
                \n", + "
              • Orientation: If it was simulated in portrait or landscape orientation.
              • \n", + "
              • Number of cells in the module: options right now are 72 or 96
              • \n", + "
              • Bifaciality factor: this is how well the rear of the module performs compared to the front of the module, and is a spec usually found in the datasheet.
              • \n", + "
              \n", + "\n", + "Also, if the number of sampling points (**sensorsy**) from the result files does not match the number of cells along the panel orientation, downsampling or upsamplinb will be peformed. For this example, the module is in portrait mode (y > x), so there will be 12 cells along the collector width (**numcellsy**), and that's why we set **sensorsy = 12** during the analysis above. \n", + "\n", + "These are the re-sampling options. To downsample, we suggest sensorsy >> numcellsy (for example, we've tested sensorsy = 100,120 and 200)\n", + " - Downsamping by Center - Find the center points of all the sensors passed \n", + " - Downsampling by Average - averages irradiances that fall on what would consist on the cell\n", + " - Upsample\n" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2 files in the directory\n", + "Same number of sensorsy and cellsy for your module.\n", + "Saved Results to Mismatch_Results.csv\n" + ] + } + ], + "source": [ + "resultfolder = os.path.join(testfolder, 'results')\n", + "writefiletitle = \"Mismatch_Results.csv\" \n", + "\n", + "portraitorlandscape='portrait' # Options are 'portrait' or 'landscape'\n", + "bififactor=0.9 # Bifaciality factor DOES matter now, as the rear irradiance values will be multiplied by this factor.\n", + "numcells=72 # Options are 72 or 96 at the moment.\n", + "downsamplingmethod = 'byCenter' # Options are 'byCenter' or 'byAverage'.\n", + "bifacial_radiance.mismatch.analysisIrradianceandPowerMismatch(testfolder=resultfolder, writefiletitle=writefiletitle, portraitorlandscape=portraitorlandscape, \n", + " bififactor=bififactor, numcells=numcells)\n", + "\n", + "print (\"Your hourly mismatch values are now saved in the file above! :D\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
              \n", + "We hope to add more content to this journal for next release so check back! Particularly how to use the Mad_fn to make the mismatch calculation faster, as per the proceedings and publication above!\n", + "
              " + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.3" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/tutorials/8 - Advanced topics - Calculating Power Output and Electrical Mismatch.py b/docs/tutorials/8 - Advanced topics - Calculating Power Output and Electrical Mismatch.py new file mode 100644 index 00000000..4aa962c3 --- /dev/null +++ b/docs/tutorials/8 - Advanced topics - Calculating Power Output and Electrical Mismatch.py @@ -0,0 +1,133 @@ +#!/usr/bin/env python +# coding: utf-8 + +# # 8 - Advanced topics - Calculating Power Output and Electrical Mismatch +# +# Nonuniform rear-irradiance on bifacial PV systems can cause additional mismatch loss, which may not be appropriately captured in PV energy production estimates and software. +# +# +# +# The **analysis.py** module in bifacial_radiance comes with functions to calculate power output, electrical mismatch, and some other irradiance calculations. This is the procedure used for this proceedings and submitted journals, which have much more detail on the procedure. +# +# • Deline, C., Ayala Pelaez, S., MacAlpine, S., Olalla, C. Estimating and Parameterizing Mismatch Power Loss in Bifacial Photovoltaic Systems. (submitted Progress in PV on Sept. 30, 2019) +# +# • Deline C, Ayala Pelaez S, MacAlpine S, Olalla C. Bifacial PV System Mismatch Loss Estimation & Parameterization. Presented in: 36th EU PVSEC, Marseille Fr. Slides: https://www.nrel.gov/docs/fy19osti/74885.pdf. Proceedings: https://www.nrel.gov/docs/fy20osti/73541.pdf +# +# • Ayala Pelaez S, Deline C, MacAlpine S, Olalla C. Bifacial PV system mismatch loss estimation. Poster presented at the 6th BifiPV Workshop, Amsterdam 2019. https://www.nrel.gov/docs/fy19osti/74831.pdf and http://bifipv-workshop.com/index.php?id=amsterdam-2019-program +# +# Ideally **mismatch losses M** should be calculated for the whole year, and then the **mismatch loss factor to apply to Grear "Lrear"** required by due diligence softwares can be calculated: +# +# +# +# In this journal we will explore calculating mismatch loss M for a reduced set of hours. A procedure similar to that in Tutorial 3 will be used to generate various hourly irradiance measurements in the results folder, which the mismatch.py module will load and analyze. Analysis is done with PVMismatch, so this must be installed. +# +# ## STEPS: +# 1. Run an hourly simulation +# 2. Do mismatch analysis on the results. +# +# + +# + +# ### 1. Run an hourly simulation +# +# This will generate the results over which we will perform the mismatch analysis. Here we are doing only 1 day to make this 'fater'. + +# In[ ]: + + +import bifacial_radiance +import os + +simulationName = 'Tutorial 8' +testfolder = os.path.abspath(r'..\..\bifacial_radiance\TEMP') +moduletype = "Canadian Solar" +albedo = 0.25 +lat = 37.5 +lon = -77.6 + +# Scene variables +nMods = 20 +nRows = 7 +hub_height = 1.5 # meters +gcr = 0.33 + +# Traking parameters +cumulativesky = False +limit_angle = 60 +angledelta = 0.01 +backtrack = True + +#makeModule parameters +x = 1 +y = 2 +xgap = 0.01 +zgap = 0.05 +ygap = 0.0 # numpanels=1 anyways so it doesnt matter anyway +numpanels = 1 +torquetube = True +axisofrotationTorqueTube = True +diameter = 0.1 +tubetype = 'Oct' +material = 'black' + +# Analysis parmaeters +startdate = '11/06' +enddate = '11/06' +sensorsy = 12 + +demo = bifacial_radiance.RadianceObj(simulationName, path=testfolder) +demo.setGround(albedo) +epwfile = demo.getEPW(lat,lon) +metdata = demo.readWeatherFile(epwfile) +mymodule = demo.makeModule(name=moduletype, torquetube=torquetube, diameter=diameter, tubetype=tubetype, material=material, + x=x, y=y, xgap=xgap, ygap = ygap, zgap=zgap, numpanels=numpanels, + axisofrotationTorqueTube=axisofrotationTorqueTube) +pitch = mymodule['sceney']/gcr +sceneDict = {'pitch':pitch,'hub_height':hub_height, 'nMods': nMods, 'nRows': nRows} +demo.set1axis(limit_angle = limit_angle, backtrack = backtrack, gcr = gcr, cumulativesky = cumulativesky) +demo.gendaylit1axis(startdate=startdate, enddate=enddate) +demo.makeScene1axis(moduletype=moduletype,sceneDict=sceneDict) +demo.makeOct1axis() +demo.analysis1axis(sensorsy = sensorsy) + + +# + +# ### 2. Do mismatch analysis on the results +# +# There are various things that we need to know about the module at this stage. +# +#
                +#
              • Orientation: If it was simulated in portrait or landscape orientation.
              • +#
              • Number of cells in the module: options right now are 72 or 96
              • +#
              • Bifaciality factor: this is how well the rear of the module performs compared to the front of the module, and is a spec usually found in the datasheet.
              • +#
              +# +# Also, if the number of sampling points (**sensorsy**) from the result files does not match the number of cells along the panel orientation, downsampling or upsamplinb will be peformed. For this example, the module is in portrait mode (y > x), so there will be 12 cells along the collector width (**numcellsy**), and that's why we set **sensorsy = 12** during the analysis above. +# +# These are the re-sampling options. To downsample, we suggest sensorsy >> numcellsy (for example, we've tested sensorsy = 100,120 and 200) +# - Downsamping by Center - Find the center points of all the sensors passed +# - Downsampling by Average - averages irradiances that fall on what would consist on the cell +# - Upsample +# + +# In[1]: + + +resultfolder = os.path.join(testfolder, 'results') +writefiletitle = "Mismatch_Results.csv" + +portraitorlandscape='portrait' # Options are 'portrait' or 'landscape' +bififactor=0.9 # Bifaciality factor DOES matter now, as the rear irradiance values will be multiplied by this factor. +numcells=72 # Options are 72 or 96 at the moment. +downsamplingmethod = 'byCenter' # Options are 'byCenter' or 'byAverage'. +bifacial_radiance.mismatch.analysisIrradianceandPowerMismatch(testfolder=resultfolder, writefiletitle=writefiletitle, portraitorlandscape=portraitorlandscape, + bififactor=bififactor, numcells=numcells) + +print ("Your hourly mismatch values are now saved in the file above! :D") + + +#
              +# We hope to add more content to this journal for next release so check back! Particularly how to use the Mad_fn to make the mismatch calculation faster, as per the proceedings and publication above! +#
              diff --git a/docs/tutorials/9 - Advanced topics - 1 axis torque tube Shading for 1 day (Research documentation).ipynb b/docs/tutorials/9 - Advanced topics - 1 axis torque tube Shading for 1 day (Research documentation).ipynb new file mode 100644 index 00000000..bf5d8103 --- /dev/null +++ b/docs/tutorials/9 - Advanced topics - 1 axis torque tube Shading for 1 day (Research documentation).ipynb @@ -0,0 +1,783 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# 9 - Advanced topics - 1 axis torque tube Shading for 1 day (Research Documentation)\n", + "## Recreating JPV 2019 / PVSC 2018 Fig. 13\n", + "\n", + "\n", + "Calculating and plotting shading from torque tube on 1-axis tracking for 1 day, which is figure 13 in: \n", + "\n", + " Ayala Pelaez S, Deline C, Greenberg P, Stein JS, Kostuk RK. Model and validation of single-axis tracking with bifacial PV. IEEE J Photovoltaics. 2019;9(3):715–21. https://ieeexplore.ieee.org/document/8644027 and https://www.nrel.gov/docs/fy19osti/72039.pdf (pre-print, conference version)\n", + "\n", + "\n", + "This is what we will re-create:\n", + "![Ayala JPV-2](../images_wiki/JPV_Ayala_Fig13.png)\n", + "\n", + "Use bifacial_radiance minimum v. 0.3.1 or higher. Many things have been updated since this paper, simplifying the generation of this plot:\n", + "\n", + "
                \n", + "
              • Sensor position is now always generated E to W on N-S tracking systems, so same sensor positions can just be added for this calculation at the end without needing to flip the sensors.
              • \n", + "
              • Torquetubes get automatically generated in makeModule. Following PVSC 2018 paper, rotation is around the modules and not around the torque tube axis (which is a new feature)
              • \n", + "
              • Simulating only 1 day on single-axis tracking easier with cumulativesky = False and gendaylit1axis(startdate='06/24', enddate='06/24'
              • \n", + "
              • Sensors get generated very close to surface, so all results are from the module surface and not the torquetube for this 1-UP case.
              • \n", + "
              \n", + "\n", + "## Steps:\n", + "\n", + "
                \n", + "
              1. Running the simulations for all the cases:
              2. \n", + "
                  \n", + "
                1. Baseline Case: No Torque Tube
                2. \n", + "
                3. Zgap = 0.1
                4. \n", + "
                5. Zgap = 0.2
                6. \n", + "
                7. Zgap = 0.3
                8. \n", + "
                \n", + "
              3. Read-back the values and tabulate average values for unshaded, 10cm gap and 30cm gap
              4. \n", + "
              5. Plot spatial loss values for 10cm and 30cm data
              6. \n", + "
              \n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 1. Running the simulations for all the cases" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Your simulation will be stored in F:\\Documents\\Python Scripts\\bifacial_radiance\\TEMP\n" + ] + } + ], + "source": [ + "import os\n", + "testfolder = os.path.abspath(r'..\\bifacial_radiance\\TEMP') \n", + "\n", + "# You can alternatively point to an empty directory (it will open a load GUI Visual Interface)\n", + "# or specify any other directory in your computer. I.E.:\n", + "# testfolder = r'C:\\Users\\sayala\\Documents\\RadianceScenes\\Demo'\n", + "\n", + "print (\"Your simulation will be stored in %s\" % testfolder)" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "# VARIABLES of the simulation: \n", + "lat = 35.1 # ABQ\n", + "lon = -106.7 # ABQ\n", + "x=1\n", + "y = 2 \n", + "numpanels=1\n", + "limit_angle = 45 # tracker rotation limit angle\n", + "albedo = 'concrete' # ground albedo\n", + "hub_height = y*0.75 # H = 0.75 \n", + "gcr = 0.35 \n", + "pitch = y/gcr\n", + "#pitch = 1.0/gcr # Check from 1Axis_Shading_PVSC2018 file\n", + "cumulativesky = False # needed for set1axis and makeScene1axis so simulation is done hourly not with gencumsky.\n", + "limit_angle = 45 # tracker rotation limit angle\n", + "nMods=10\n", + "nRows=3\n", + "sensorsy = 200\n", + "module_type='2m_panel'\n", + "datewanted='06/24' # sunny day 6/24/1972 (index 4180 - 4195)\n", + "\n", + "## Torque tube info\n", + "torquetube = False # redefined on each simulation below, since we need to compare with and without torque tube.\n", + "tubetype='round'\n", + "material = 'Metal_Grey'\n", + "diameter = 0.1\n", + "axisofrotationTorqueTube = False # Original PVSC version rotated around the modules like most other software.\n", + "# Variables that will get defined on each iteration below:\n", + "zgap = 0 # 0.2, 0.3 values tested. Re-defined on each simulation.\n", + "torquetube = False # baseline is no torque tube. " + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0.3.2\n", + "path = F:\\Documents\\Python Scripts\\bifacial_radiance\\TEMP\n", + "Getting weather file: USA_NM_Albuquerque.723650_TMY2.epw\n", + " ... OK!\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "C:\\Users\\cdeline\\anaconda3\\envs\\py27\\lib\\site-packages\\bifacial_radiance\\main.py:2465: pvlibDeprecationWarning: The get_sun_rise_set_transit function was deprecated in pvlib 0.6.1 and will be removed in 0.7. Use sun_rise_set_transit_spa instead.\n", + " sunup= pvlib.irradiance.solarposition.get_sun_rise_set_transit(datetimetz, lat, lon) #only for pvlib <0.6.1\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Creating ~4000 skyfiles. Takes 1-2 minutes\n", + "Created 14 skyfiles in /skies/\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "C:\\Users\\cdeline\\anaconda3\\envs\\py27\\lib\\site-packages\\pvlib\\tracking.py:431: RuntimeWarning: invalid value encountered in less\n", + " tracker_theta = np.where(wid < 0, wid + wc, wid - wc)\n", + "C:\\Users\\cdeline\\anaconda3\\envs\\py27\\lib\\site-packages\\pvlib\\tracking.py:435: RuntimeWarning: invalid value encountered in greater\n", + " tracker_theta[tracker_theta > max_angle] = max_angle\n", + "C:\\Users\\cdeline\\anaconda3\\envs\\py27\\lib\\site-packages\\pvlib\\tracking.py:436: RuntimeWarning: invalid value encountered in less\n", + " tracker_theta[tracker_theta < -max_angle] = -max_angle\n", + "C:\\Users\\cdeline\\anaconda3\\envs\\py27\\lib\\site-packages\\pvlib\\tracking.py:543: RuntimeWarning: invalid value encountered in less\n", + " surface_azimuth[surface_azimuth < 0] += 360\n", + "C:\\Users\\cdeline\\anaconda3\\envs\\py27\\lib\\site-packages\\pvlib\\tracking.py:544: RuntimeWarning: invalid value encountered in greater_equal\n", + " surface_azimuth[surface_azimuth >= 360] -= 360\n", + "C:\\Users\\cdeline\\anaconda3\\envs\\py27\\lib\\site-packages\\pandas\\core\\series.py:1999: RuntimeWarning: invalid value encountered in rint\n", + " result = com.values_from_object(self).round(decimals)\n" + ] + } + ], + "source": [ + "# Simulation Start.\n", + "\n", + "try:\n", + " import bifacial_radiance\n", + "except ImportError:\n", + " raise RuntimeError('bifacial_radiance is required. download distribution')\n", + "import numpy as np\n", + "\n", + "print(bifacial_radiance.__version__)\n", + "\n", + "demo = bifacial_radiance.RadianceObj(path = testfolder) \n", + "demo.setGround(albedo)\n", + "epwfile = demo.getEPW(lat, lon) \n", + "metdata = demo.readEPW(epwfile) \n", + "trackerdict = demo.set1axis(metdata, limit_angle = limit_angle, backtrack = True, gcr = gcr, cumulativesky = False)\n", + "trackerdict = demo.gendaylit1axis(startdate=datewanted, enddate=datewanted) \n", + "sceneDict = {'pitch':pitch,'hub_height':hub_height, 'nMods': nMods, 'nRows': nRows} " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### A. Baseline Case: No Torque Tube\n", + "\n", + "When torquetube is False, zgap is the distance from axis of torque tube to module surface, but since we are rotating from the module's axis, this Zgap doesn't matter for this baseline case." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "('\\nModule Name:', '2m_panel')\n", + "REWRITING pre-existing module file. \n", + "Module 2m_panel successfully created\n", + "\n", + "Making ~4000 .rad files for gendaylit 1-axis workflow (this takes a minute..)\n", + "14 Radfiles created in /objects/\n", + "\n", + "Making 14 octfiles for 1-axis tracking in root directory.\n", + "Created 1axis_06_24_06.oct\n", + "Created 1axis_06_24_07.oct\n", + "Created 1axis_06_24_08.oct\n", + "Created 1axis_06_24_09.oct\n", + "Created 1axis_06_24_19.oct\n", + "Created 1axis_06_24_18.oct\n", + "Created 1axis_06_24_11.oct\n", + "Created 1axis_06_24_10.oct\n", + "Created 1axis_06_24_13.oct\n", + "Created 1axis_06_24_12.oct\n", + "Created 1axis_06_24_15.oct\n", + "Created 1axis_06_24_14.oct\n", + "Created 1axis_06_24_17.oct\n", + "Created 1axis_06_24_16.oct\n", + "Linescan in process: 1axis_06_24_06_NoTT_Front\n", + "Linescan in process: 1axis_06_24_06_NoTT_Back\n", + "Saved: results\\irr_1axis_06_24_06_NoTT.csv\n", + "Index: 06_24_06. Wm2Front: 161.420425167. Wm2Back: 17.1202325333\n", + "Linescan in process: 1axis_06_24_07_NoTT_Front\n", + "Linescan in process: 1axis_06_24_07_NoTT_Back\n", + "Saved: results\\irr_1axis_06_24_07_NoTT.csv\n", + "Index: 06_24_07. Wm2Front: 709.602221167. Wm2Back: 15.7013397667\n", + "Linescan in process: 1axis_06_24_08_NoTT_Front\n", + "Linescan in process: 1axis_06_24_08_NoTT_Back\n", + "Saved: results\\irr_1axis_06_24_08_NoTT.csv\n", + "Index: 06_24_08. Wm2Front: 915.88824355. Wm2Back: 69.6114017\n", + "Linescan in process: 1axis_06_24_09_NoTT_Front\n", + "Linescan in process: 1axis_06_24_09_NoTT_Back\n", + "Saved: results\\irr_1axis_06_24_09_NoTT.csv\n", + "Index: 06_24_09. Wm2Front: 1029.27485377. Wm2Back: 84.6579091833\n", + "Linescan in process: 1axis_06_24_10_NoTT_Front\n", + "Linescan in process: 1axis_06_24_10_NoTT_Back\n", + "Saved: results\\irr_1axis_06_24_10_NoTT.csv\n", + "Index: 06_24_10. Wm2Front: 1072.49606678. Wm2Back: 107.5137435\n", + "Linescan in process: 1axis_06_24_11_NoTT_Front\n", + "Linescan in process: 1axis_06_24_11_NoTT_Back\n", + "Saved: results\\irr_1axis_06_24_11_NoTT.csv\n", + "Index: 06_24_11. Wm2Front: 1083.79217833. Wm2Back: 133.168364833\n", + "Linescan in process: 1axis_06_24_12_NoTT_Front\n", + "Linescan in process: 1axis_06_24_12_NoTT_Back\n", + "Saved: results\\irr_1axis_06_24_12_NoTT.csv\n", + "Index: 06_24_12. Wm2Front: 1085.31024. Wm2Back: 150.868799167\n", + "Linescan in process: 1axis_06_24_13_NoTT_Front\n", + "Linescan in process: 1axis_06_24_13_NoTT_Back\n", + "Saved: results\\irr_1axis_06_24_13_NoTT.csv\n", + "Index: 06_24_13. Wm2Front: 1083.48679167. Wm2Back: 152.514981333\n", + "Linescan in process: 1axis_06_24_14_NoTT_Front\n", + "Linescan in process: 1axis_06_24_14_NoTT_Back\n", + "Saved: results\\irr_1axis_06_24_14_NoTT.csv\n", + "Index: 06_24_14. Wm2Front: 1090.24465. Wm2Back: 139.7909667\n", + "Linescan in process: 1axis_06_24_15_NoTT_Front\n", + "Linescan in process: 1axis_06_24_15_NoTT_Back\n", + "Saved: results\\irr_1axis_06_24_15_NoTT.csv\n", + "Index: 06_24_15. Wm2Front: 1084.79985515. Wm2Back: 115.557837667\n", + "Linescan in process: 1axis_06_24_16_NoTT_Front\n", + "Linescan in process: 1axis_06_24_16_NoTT_Back\n", + "Saved: results\\irr_1axis_06_24_16_NoTT.csv\n", + "Index: 06_24_16. Wm2Front: 1053.66710257. Wm2Back: 88.5369673\n", + "Linescan in process: 1axis_06_24_17_NoTT_Front\n", + "Linescan in process: 1axis_06_24_17_NoTT_Back\n", + "Saved: results\\irr_1axis_06_24_17_NoTT.csv\n", + "Index: 06_24_17. Wm2Front: 964.345925617. Wm2Back: 74.4237025333\n", + "Linescan in process: 1axis_06_24_18_NoTT_Front\n", + "Linescan in process: 1axis_06_24_18_NoTT_Back\n", + "Saved: results\\irr_1axis_06_24_18_NoTT.csv\n", + "Index: 06_24_18. Wm2Front: 787.42443225. Wm2Back: 29.1930746167\n", + "Linescan in process: 1axis_06_24_19_NoTT_Front\n", + "Linescan in process: 1axis_06_24_19_NoTT_Back\n", + "Saved: results\\irr_1axis_06_24_19_NoTT.csv\n", + "Index: 06_24_19. Wm2Front: 308.701412667. Wm2Back: 18.0430466333\n" + ] + } + ], + "source": [ + "#CASE 0 No torque tube\n", + "# When torquetube is False, zgap is the distance from axis of torque tube to module surface, but since we are rotating from the module's axis, this Zgap doesn't matter.\n", + "# zgap = 0.1 + diameter/2.0 \n", + "torquetube = False \n", + "customname = '_NoTT'\n", + "demo.makeModule(name=module_type,x=x,y=y, numpanels=numpanels, torquetube=torquetube, axisofrotationTorqueTube=axisofrotationTorqueTube)\n", + "trackerdict = demo.makeScene1axis(trackerdict,module_type,sceneDict, cumulativesky = cumulativesky) \n", + "trackerdict = demo.makeOct1axis(trackerdict)\n", + "trackerdict = demo.analysis1axis(trackerdict, sensorsy = sensorsy, customname = customname)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### B. ZGAP = 0.1" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "('\\nModule Name:', '2m_panel')\n", + "REWRITING pre-existing module file. \n", + "Module 2m_panel successfully created\n", + "\n", + "Making ~4000 .rad files for gendaylit 1-axis workflow (this takes a minute..)\n", + "14 Radfiles created in /objects/\n", + "\n", + "Making 14 octfiles for 1-axis tracking in root directory.\n", + "Created 1axis_06_24_06.oct\n", + "Created 1axis_06_24_07.oct\n", + "Created 1axis_06_24_08.oct\n", + "Created 1axis_06_24_09.oct\n", + "Created 1axis_06_24_19.oct\n", + "Created 1axis_06_24_18.oct\n", + "Created 1axis_06_24_11.oct\n", + "Created 1axis_06_24_10.oct\n", + "Created 1axis_06_24_13.oct\n", + "Created 1axis_06_24_12.oct\n", + "Created 1axis_06_24_15.oct\n", + "Created 1axis_06_24_14.oct\n", + "Created 1axis_06_24_17.oct\n", + "Created 1axis_06_24_16.oct\n", + "Linescan in process: 1axis_06_24_06_zgap0.1_Front\n", + "Linescan in process: 1axis_06_24_06_zgap0.1_Back\n", + "Saved: results\\irr_1axis_06_24_06_zgap0.1.csv\n", + "Index: 06_24_06. Wm2Front: 161.539838667. Wm2Back: 16.3351245717\n", + "Linescan in process: 1axis_06_24_07_zgap0.1_Front\n", + "Linescan in process: 1axis_06_24_07_zgap0.1_Back\n", + "Saved: results\\irr_1axis_06_24_07_zgap0.1.csv\n", + "Index: 06_24_07. Wm2Front: 709.727649117. Wm2Back: 14.8131407383\n", + "Linescan in process: 1axis_06_24_08_zgap0.1_Front\n", + "Linescan in process: 1axis_06_24_08_zgap0.1_Back\n", + "Saved: results\\irr_1axis_06_24_08_zgap0.1.csv\n", + "Index: 06_24_08. Wm2Front: 916.1876703. Wm2Back: 66.1856585667\n", + "Linescan in process: 1axis_06_24_09_zgap0.1_Front\n", + "Linescan in process: 1axis_06_24_09_zgap0.1_Back\n", + "Saved: results\\irr_1axis_06_24_09_zgap0.1.csv\n", + "Index: 06_24_09. Wm2Front: 1029.22358065. Wm2Back: 80.27420805\n", + "Linescan in process: 1axis_06_24_10_zgap0.1_Front\n", + "Linescan in process: 1axis_06_24_10_zgap0.1_Back\n", + "Saved: results\\irr_1axis_06_24_10_zgap0.1.csv\n", + "Index: 06_24_10. Wm2Front: 1073.16896435. Wm2Back: 102.447089133\n", + "Linescan in process: 1axis_06_24_11_zgap0.1_Front\n", + "Linescan in process: 1axis_06_24_11_zgap0.1_Back\n", + "Saved: results\\irr_1axis_06_24_11_zgap0.1.csv\n", + "Index: 06_24_11. Wm2Front: 1084.01090167. Wm2Back: 126.65973125\n", + "Linescan in process: 1axis_06_24_12_zgap0.1_Front\n", + "Linescan in process: 1axis_06_24_12_zgap0.1_Back\n", + "Saved: results\\irr_1axis_06_24_12_zgap0.1.csv\n", + "Index: 06_24_12. Wm2Front: 1085.21908167. Wm2Back: 142.255893283\n", + "Linescan in process: 1axis_06_24_13_zgap0.1_Front\n", + "Linescan in process: 1axis_06_24_13_zgap0.1_Back\n", + "Saved: results\\irr_1axis_06_24_13_zgap0.1.csv\n", + "Index: 06_24_13. Wm2Front: 1083.64125333. Wm2Back: 145.492577083\n", + "Linescan in process: 1axis_06_24_14_zgap0.1_Front\n", + "Linescan in process: 1axis_06_24_14_zgap0.1_Back\n", + "Saved: results\\irr_1axis_06_24_14_zgap0.1.csv\n", + "Index: 06_24_14. Wm2Front: 1090.04228833. Wm2Back: 133.830940017\n", + "Linescan in process: 1axis_06_24_15_zgap0.1_Front\n", + "Linescan in process: 1axis_06_24_15_zgap0.1_Back\n", + "Saved: results\\irr_1axis_06_24_15_zgap0.1.csv\n", + "Index: 06_24_15. Wm2Front: 1084.30238268. Wm2Back: 110.2235721\n", + "Linescan in process: 1axis_06_24_16_zgap0.1_Front\n", + "Linescan in process: 1axis_06_24_16_zgap0.1_Back\n", + "Saved: results\\irr_1axis_06_24_16_zgap0.1.csv\n", + "Index: 06_24_16. Wm2Front: 1053.77910728. Wm2Back: 85.2022533667\n", + "Linescan in process: 1axis_06_24_17_zgap0.1_Front\n", + "Linescan in process: 1axis_06_24_17_zgap0.1_Back\n", + "Saved: results\\irr_1axis_06_24_17_zgap0.1.csv\n", + "Index: 06_24_17. Wm2Front: 964.657343267. Wm2Back: 70.6240591833\n", + "Linescan in process: 1axis_06_24_18_zgap0.1_Front\n", + "Linescan in process: 1axis_06_24_18_zgap0.1_Back\n", + "Saved: results\\irr_1axis_06_24_18_zgap0.1.csv\n", + "Index: 06_24_18. Wm2Front: 787.585597117. Wm2Back: 27.74708595\n", + "Linescan in process: 1axis_06_24_19_zgap0.1_Front\n", + "Linescan in process: 1axis_06_24_19_zgap0.1_Back\n", + "Saved: results\\irr_1axis_06_24_19_zgap0.1.csv\n", + "Index: 06_24_19. Wm2Front: 308.723043167. Wm2Back: 17.05199522\n" + ] + } + ], + "source": [ + "#ZGAP 0.1 \n", + "zgap = 0.1\n", + "torquetube = True\n", + "customname = '_zgap0.1'\n", + "demo.makeModule(name=module_type,x=x,y=y, numpanels=numpanels,tubetype=tubetype, zgap=zgap, torquetube=torquetube, diameter=diameter, material=material, axisofrotationTorqueTube=axisofrotationTorqueTube)\n", + "trackerdict = demo.makeScene1axis(trackerdict,module_type,sceneDict, cumulativesky = cumulativesky) \n", + "trackerdict = demo.makeOct1axis(trackerdict)\n", + "trackerdict = demo.analysis1axis(trackerdict, sensorsy = sensorsy, customname = customname)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### C. ZGAP = 0.2" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "('\\nModule Name:', '2m_panel')\n", + "REWRITING pre-existing module file. \n", + "Module 2m_panel successfully created\n", + "\n", + "Making ~4000 .rad files for gendaylit 1-axis workflow (this takes a minute..)\n", + "14 Radfiles created in /objects/\n", + "\n", + "Making 14 octfiles for 1-axis tracking in root directory.\n", + "Created 1axis_06_24_06.oct\n", + "Created 1axis_06_24_07.oct\n", + "Created 1axis_06_24_08.oct\n", + "Created 1axis_06_24_09.oct\n", + "Created 1axis_06_24_19.oct\n", + "Created 1axis_06_24_18.oct\n", + "Created 1axis_06_24_11.oct\n", + "Created 1axis_06_24_10.oct\n", + "Created 1axis_06_24_13.oct\n", + "Created 1axis_06_24_12.oct\n", + "Created 1axis_06_24_15.oct\n", + "Created 1axis_06_24_14.oct\n", + "Created 1axis_06_24_17.oct\n", + "Created 1axis_06_24_16.oct\n", + "Linescan in process: 1axis_06_24_06_zgap0.2_Front\n", + "Linescan in process: 1axis_06_24_06_zgap0.2_Back\n", + "Saved: results\\irr_1axis_06_24_06_zgap0.2.csv\n", + "Index: 06_24_06. Wm2Front: 161.427369333. Wm2Back: 16.4130201667\n", + "Linescan in process: 1axis_06_24_07_zgap0.2_Front\n", + "Linescan in process: 1axis_06_24_07_zgap0.2_Back\n", + "Saved: results\\irr_1axis_06_24_07_zgap0.2.csv\n", + "Index: 06_24_07. Wm2Front: 709.626210467. Wm2Back: 15.09115024\n", + "Linescan in process: 1axis_06_24_08_zgap0.2_Front\n", + "Linescan in process: 1axis_06_24_08_zgap0.2_Back\n", + "Saved: results\\irr_1axis_06_24_08_zgap0.2.csv\n", + "Index: 06_24_08. Wm2Front: 916.151318617. Wm2Back: 66.8851610667\n", + "Linescan in process: 1axis_06_24_09_zgap0.2_Front\n", + "Linescan in process: 1axis_06_24_09_zgap0.2_Back\n", + "Saved: results\\irr_1axis_06_24_09_zgap0.2.csv\n", + "Index: 06_24_09. Wm2Front: 1029.24920978. Wm2Back: 81.52676585\n", + "Linescan in process: 1axis_06_24_10_zgap0.2_Front\n", + "Linescan in process: 1axis_06_24_10_zgap0.2_Back\n", + "Saved: results\\irr_1axis_06_24_10_zgap0.2.csv\n", + "Index: 06_24_10. Wm2Front: 1073.18811838. Wm2Back: 103.177670733\n", + "Linescan in process: 1axis_06_24_11_zgap0.2_Front\n", + "Linescan in process: 1axis_06_24_11_zgap0.2_Back\n", + "Saved: results\\irr_1axis_06_24_11_zgap0.2.csv\n", + "Index: 06_24_11. Wm2Front: 1083.66462. Wm2Back: 127.524964733\n", + "Linescan in process: 1axis_06_24_12_zgap0.2_Front\n", + "Linescan in process: 1axis_06_24_12_zgap0.2_Back\n", + "Saved: results\\irr_1axis_06_24_12_zgap0.2.csv\n", + "Index: 06_24_12. Wm2Front: 1085.64442833. Wm2Back: 145.115639617\n", + "Linescan in process: 1axis_06_24_13_zgap0.2_Front\n", + "Linescan in process: 1axis_06_24_13_zgap0.2_Back\n", + "Saved: results\\irr_1axis_06_24_13_zgap0.2.csv\n", + "Index: 06_24_13. Wm2Front: 1083.53121333. Wm2Back: 146.434190817\n", + "Linescan in process: 1axis_06_24_14_zgap0.2_Front\n", + "Linescan in process: 1axis_06_24_14_zgap0.2_Back\n", + "Saved: results\\irr_1axis_06_24_14_zgap0.2.csv\n", + "Index: 06_24_14. Wm2Front: 1090.01627667. Wm2Back: 134.783261883\n", + "Linescan in process: 1axis_06_24_15_zgap0.2_Front\n", + "Linescan in process: 1axis_06_24_15_zgap0.2_Back\n", + "Saved: results\\irr_1axis_06_24_15_zgap0.2.csv\n", + "Index: 06_24_15. Wm2Front: 1084.93202833. Wm2Back: 112.328697217\n", + "Linescan in process: 1axis_06_24_16_zgap0.2_Front\n", + "Linescan in process: 1axis_06_24_16_zgap0.2_Back\n", + "Saved: results\\irr_1axis_06_24_16_zgap0.2.csv\n", + "Index: 06_24_16. Wm2Front: 1053.98065525. Wm2Back: 85.8667412333\n", + "Linescan in process: 1axis_06_24_17_zgap0.2_Front\n", + "Linescan in process: 1axis_06_24_17_zgap0.2_Back\n", + "Saved: results\\irr_1axis_06_24_17_zgap0.2.csv\n", + "Index: 06_24_17. Wm2Front: 964.83240965. Wm2Back: 72.2948218167\n", + "Linescan in process: 1axis_06_24_18_zgap0.2_Front\n", + "Linescan in process: 1axis_06_24_18_zgap0.2_Back\n", + "Saved: results\\irr_1axis_06_24_18_zgap0.2.csv\n", + "Index: 06_24_18. Wm2Front: 787.528989467. Wm2Back: 27.6617148833\n", + "Linescan in process: 1axis_06_24_19_zgap0.2_Front\n", + "Linescan in process: 1axis_06_24_19_zgap0.2_Back\n", + "Saved: results\\irr_1axis_06_24_19_zgap0.2.csv\n", + "Index: 06_24_19. Wm2Front: 308.728144833. Wm2Back: 17.272205\n" + ] + } + ], + "source": [ + "#ZGAP 0.2\n", + "zgap = 0.2\n", + "torquetube = True\n", + "customname = '_zgap0.2'\n", + "demo.makeModule(name=module_type,x=x,y=y, numpanels=numpanels,tubetype=tubetype, zgap=zgap, torquetube=torquetube, diameter=diameter, material=material, axisofrotationTorqueTube=axisofrotationTorqueTube)\n", + "trackerdict = demo.makeScene1axis(trackerdict,module_type,sceneDict, cumulativesky = cumulativesky) \n", + "trackerdict = demo.makeOct1axis(trackerdict)\n", + "trackerdict = demo.analysis1axis(trackerdict, sensorsy = sensorsy, customname = customname)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### D. ZGAP = 0.3" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "('\\nModule Name:', '2m_panel')\n", + "REWRITING pre-existing module file. \n", + "Module 2m_panel successfully created\n", + "\n", + "Making ~4000 .rad files for gendaylit 1-axis workflow (this takes a minute..)\n", + "14 Radfiles created in /objects/\n", + "\n", + "Making 14 octfiles for 1-axis tracking in root directory.\n", + "Created 1axis_06_24_06.oct\n", + "Created 1axis_06_24_07.oct\n", + "Created 1axis_06_24_08.oct\n", + "Created 1axis_06_24_09.oct\n", + "Created 1axis_06_24_19.oct\n", + "Created 1axis_06_24_18.oct\n", + "Created 1axis_06_24_11.oct\n", + "Created 1axis_06_24_10.oct\n", + "Created 1axis_06_24_13.oct\n", + "Created 1axis_06_24_12.oct\n", + "Created 1axis_06_24_15.oct\n", + "Created 1axis_06_24_14.oct\n", + "Created 1axis_06_24_17.oct\n", + "Created 1axis_06_24_16.oct\n", + "Linescan in process: 1axis_06_24_06_zgap0.3_Front\n", + "Linescan in process: 1axis_06_24_06_zgap0.3_Back\n", + "Saved: results\\irr_1axis_06_24_06_zgap0.3.csv\n", + "Index: 06_24_06. Wm2Front: 161.425258833. Wm2Back: 16.48608815\n", + "Linescan in process: 1axis_06_24_07_zgap0.3_Front\n", + "Linescan in process: 1axis_06_24_07_zgap0.3_Back\n", + "Saved: results\\irr_1axis_06_24_07_zgap0.3.csv\n", + "Index: 06_24_07. Wm2Front: 709.733419233. Wm2Back: 15.200422965\n", + "Linescan in process: 1axis_06_24_08_zgap0.3_Front\n", + "Linescan in process: 1axis_06_24_08_zgap0.3_Back\n", + "Saved: results\\irr_1axis_06_24_08_zgap0.3.csv\n", + "Index: 06_24_08. Wm2Front: 916.305202467. Wm2Back: 66.5236612833\n", + "Linescan in process: 1axis_06_24_09_zgap0.3_Front\n", + "Linescan in process: 1axis_06_24_09_zgap0.3_Back\n", + "Saved: results\\irr_1axis_06_24_09_zgap0.3.csv\n", + "Index: 06_24_09. Wm2Front: 1029.36178673. Wm2Back: 82.2897241333\n", + "Linescan in process: 1axis_06_24_10_zgap0.3_Front\n", + "Linescan in process: 1axis_06_24_10_zgap0.3_Back\n", + "Saved: results\\irr_1axis_06_24_10_zgap0.3.csv\n", + "Index: 06_24_10. Wm2Front: 1072.78537597. Wm2Back: 104.666836617\n", + "Linescan in process: 1axis_06_24_11_zgap0.3_Front\n", + "Linescan in process: 1axis_06_24_11_zgap0.3_Back\n", + "Saved: results\\irr_1axis_06_24_11_zgap0.3.csv\n", + "Index: 06_24_11. Wm2Front: 1083.289255. Wm2Back: 129.644350433\n", + "Linescan in process: 1axis_06_24_12_zgap0.3_Front\n", + "Linescan in process: 1axis_06_24_12_zgap0.3_Back\n", + "Saved: results\\irr_1axis_06_24_12_zgap0.3.csv\n", + "Index: 06_24_12. Wm2Front: 1085.42463667. Wm2Back: 146.597674667\n", + "Linescan in process: 1axis_06_24_13_zgap0.3_Front\n", + "Linescan in process: 1axis_06_24_13_zgap0.3_Back\n", + "Saved: results\\irr_1axis_06_24_13_zgap0.3.csv\n", + "Index: 06_24_13. Wm2Front: 1083.53411333. Wm2Back: 146.490793283\n", + "Linescan in process: 1axis_06_24_14_zgap0.3_Front\n", + "Linescan in process: 1axis_06_24_14_zgap0.3_Back\n", + "Saved: results\\irr_1axis_06_24_14_zgap0.3.csv\n", + "Index: 06_24_14. Wm2Front: 1089.69777167. Wm2Back: 135.612068717\n", + "Linescan in process: 1axis_06_24_15_zgap0.3_Front\n", + "Linescan in process: 1axis_06_24_15_zgap0.3_Back\n", + "Saved: results\\irr_1axis_06_24_15_zgap0.3.csv\n", + "Index: 06_24_15. Wm2Front: 1085.01026138. Wm2Back: 112.461951867\n", + "Linescan in process: 1axis_06_24_16_zgap0.3_Front\n", + "Linescan in process: 1axis_06_24_16_zgap0.3_Back\n", + "Saved: results\\irr_1axis_06_24_16_zgap0.3.csv\n", + "Index: 06_24_16. Wm2Front: 1053.6527237. Wm2Back: 86.6029814333\n", + "Linescan in process: 1axis_06_24_17_zgap0.3_Front\n", + "Linescan in process: 1axis_06_24_17_zgap0.3_Back\n", + "Saved: results\\irr_1axis_06_24_17_zgap0.3.csv\n", + "Index: 06_24_17. Wm2Front: 964.7943688. Wm2Back: 72.28008065\n", + "Linescan in process: 1axis_06_24_18_zgap0.3_Front\n", + "Linescan in process: 1axis_06_24_18_zgap0.3_Back\n", + "Saved: results\\irr_1axis_06_24_18_zgap0.3.csv\n", + "Index: 06_24_18. Wm2Front: 787.441863467. Wm2Back: 27.8219388167\n", + "Linescan in process: 1axis_06_24_19_zgap0.3_Front\n", + "Linescan in process: 1axis_06_24_19_zgap0.3_Back\n", + "Saved: results\\irr_1axis_06_24_19_zgap0.3.csv\n", + "Index: 06_24_19. Wm2Front: 308.707182667. Wm2Back: 17.5099916\n" + ] + } + ], + "source": [ + "#ZGAP 0.3\n", + "zgap = 0.3\n", + "torquetube = True\n", + "customname = '_zgap0.3'\n", + "demo.makeModule(name=module_type,x=x,y=y, numpanels=numpanels,tubetype=tubetype, zgap=zgap, torquetube=torquetube, diameter=diameter, material=material, axisofrotationTorqueTube=axisofrotationTorqueTube)\n", + "trackerdict = demo.makeScene1axis(trackerdict,module_type,sceneDict, cumulativesky = cumulativesky) \n", + "trackerdict = demo.makeOct1axis(trackerdict)\n", + "trackerdict = demo.analysis1axis(trackerdict, sensorsy = sensorsy, customname = customname)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 2. Read-back the values and tabulate average values for unshaded, 10cm gap and 30cm gap\n" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "F:\\Documents\\Python Scripts\\bifacial_radiance_PVSC2018_rerun\\results\n" + ] + } + ], + "source": [ + "import glob\n", + "import pandas as pd\n", + "\n", + "resultsfolder = os.path.join(testfolder, 'results')\n", + "print (resultsfolder)\n", + "filenames = glob.glob(os.path.join(resultsfolder,'*.csv'))\n", + "noTTlist = [k for k in filenames if 'NoTT' in k]\n", + "zgap10cmlist = [k for k in filenames if 'zgap0.1' in k]\n", + "zgap20cmlist = [k for k in filenames if 'zgap0.2' in k]\n", + "zgap30cmlist = [k for k in filenames if 'zgap0.3' in k]\n", + "\n", + "# sum across all hours for each case\n", + "unsh_front = np.array([pd.read_csv(f, engine='python')['Wm2Front'] for f in noTTlist]).sum(axis = 0)\n", + "cm10_front = np.array([pd.read_csv(f, engine='python')['Wm2Front'] for f in zgap10cmlist]).sum(axis = 0)\n", + "cm20_front = np.array([pd.read_csv(f, engine='python')['Wm2Front'] for f in zgap20cmlist]).sum(axis = 0)\n", + "cm30_front = np.array([pd.read_csv(f, engine='python')['Wm2Front'] for f in zgap30cmlist]).sum(axis = 0)\n", + "unsh_back = np.array([pd.read_csv(f, engine='python')['Wm2Back'] for f in noTTlist]).sum(axis = 0)\n", + "cm10_back = np.array([pd.read_csv(f, engine='python')['Wm2Back'] for f in zgap10cmlist]).sum(axis = 0)\n", + "cm20_back = np.array([pd.read_csv(f, engine='python')['Wm2Back'] for f in zgap20cmlist]).sum(axis = 0)\n", + "cm30_back = np.array([pd.read_csv(f, engine='python')['Wm2Back'] for f in zgap30cmlist]).sum(axis = 0)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 3. plot spatial loss values for 10cm and 30cm data" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAARoAAAC/CAYAAAAhFRNTAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi40LCBodHRwOi8vbWF0cGxvdGxpYi5vcmcv7US4rQAAIABJREFUeJzsnXdYVMfawH9Dr0qRIoigghXFWKLYsGvsRmNsMWqqiUnMjTH5klxvctNjYqq5uenFaKLGq9HYNRbsFVGxoNhAUBGQusDufH+cpS/LsuwK6vk9zz67e87MnDkL++4777xFSClRUVFRsSY2tT0BFRWVOx9V0KioqFgdVdCoqKhYHVXQqKioWB1V0KioqFgdVdCoqKhYHVXQ1FGEEPWFEFv1j3QhxG7967G1MJe+QojwKtqECiHWWeh6/YUQX5nZN17/3FEI8Y/amodKWexqewIqhpFSZgC9AYQQW4HJUsrLVfUTQthKKbUWnk5f4Jj+cVsgpTwIHKzteagoqBrNbYYQYpQQYq9ew3lFf6y/EGKtEGIp8G8hRLgQYp8Q4i8hxC9CiNf07eJLjfOjEKKH/vUsIcQO/ZhTy13PB3gImKvXqOyEECdLnd8qhGikf+shhFgkhDgohJipP+8phFgmhNgihNgshGhabnw3IcT6UtpbM/2pICHEb0KIY0KI+/VtHxJC/K2f539LjTFfCLFLCPEl+h/P0tqIEGKhEOIzIcQafd8G+uMvCiEO6D+jg6Xuw9Dn3l1/jWghxBf6Y22FEHv0c1qtPzZb//fZUvQZqKgazW2FEMIOmAd0AjKBzUKIlfrTDYFOUspCIcRfwNNSyv1CiB+qGLMtisbSC+WHZ6cQYoWUMh1ASnlNCPELcExK+Zt+DpXRBOgHFAAHhBCLgf8DfpNSLhNCdATeAcaX6tMaSJFSTtHPx0Y/TgNgONAIWAIsB5ZLKX/Rt/tDCNFNf63mUspueiE2vZK5nZZSPiuEmAuM1X9u44EugBtwztjnBHwOjJZSXhBC/CyEGAKEA19LKb/XzxtgAtBLSpld6thdjypobi/8gET9sgohxF6gBXAT2C+lLNS3CwUO6F/vRfnSlkfon8P1j7/1791QvtzplcyhfMyKKPX6hJQyWz+3E0AI0BboVurXPa9c//1ArBBiIXAdmKs/fkRKqQMuCiG89cf6CCFeQBGIIUAQyv/wfgAp5TkhxPVK5l20jLoIBAJNgaP6zyxdCHG6kn5FuEkpL+hf7wJaAt8CrwohfgUOAx8CzwNf6AXyl8DuKsa9K1AFze1FChAohKiPotF0ARYC/kBpu8xZoAPKl6szkKA/nq1fCt0AIvTHTgAHpZQPAAgh7KWUBeWum4/+f0VKqRVCOAohHAB7FEFXRCshhAuKltEaOA8cB/6WUq7Sj+9QbmxH4EMppRRCvA5MAs5QVqAVCbP3gL5SyqtCiD/0x88AD+rHLtKEDFF+vASgrRDCFkW4Nq+kXxHZQohgvbDpBvwO5EkpX9Bfe6tekzwgpdwuhAjRt+lSxbh3BaqguY3QL4teAjYCOmC1lPKYEMK/XNOXge/0v+6ppY6/D2xBMepe1Y8ZI4TYLoTYjiKscoUQw/TaRBEbgI+EEKOklGNRfqn3oPyKJ5ZqdwH4HggDvpVSpgoh3gS+EkI8j/IF/xP4uFSftsDHQogCFE1lir6/IX5BWS6e1I+FlHKfECJBCLELiAWSK/8ES5BSJultWnuB08BlFIFaGc8CvwshtEAMsAZ4TAjxEIoQSwLigaVCCE/ACeVzUgGEGr19Z6M37jaSUr5V23OpaxRpb0IIDxTtL1SqXwiroGo0Knczrwoh+gD1gFdUIWM9VI1GRUXF6qjbbyoqKlanzgkaIUSIECJFb8XfUNvzUVFRqTl11UazUUo52VgDIYQ9il+Jzlg7FRUVq2GD4mxZ3h2iAnVV0PQRQuxA8QT9uJI2fgcPHrzk719+Z1dFReVWkJycTMeOHYNQXAOMUhcFzRUU5ykNsFIIsVlKebTopBDiceBxwH7Tpk3MmTOnlqapoqKCiSuKOr3rJISYAWRIKRcZOBeQmJiYGBAQUAszU1FRSUpKIjAwMFBKmVRV27poDHYv9bY7iju9iorKbUydEzRAT33I/i4gSUq5t7YnpKKiUjPqnI1GSrkGJY5E5Q5n2/EkDidc57mhbRFCVN1B5balLmo0Kncw3333HXPmzEGrk7yz/DBrD18iPvlmbU9LxcqogkbllvLZZ58xf/58kq9dJ6p1QwA2xFyq5VmpWBtV0KjcMtLS0oiNjUWr1bLj7828MqYDvdsEsO34FYp2PxctWkSbNm3Iysqq5dmqWBJV0KjcMnbu3FksUB555kU639uFgRGB3N+lCYU6yYULF3j++ec5ceIEa9aoZro7CVXQqNwSMjMzWbhwIQ4ODkx/7HG6Pb2ANPfmZFw4xvgeobz79luEhIRw9epV3N3dWbp0aW1PWcWCqIJG5ZbwyCOP8PvvvzNx4kRm//MdbGxt0WWmMG/ePL79aREffPofRowYwZ49e3jooYdYvX4L7dq149y5qnKGq9wO1LntbZU7D61Wy4YNG5g+fTqfLvgPD360CYD7B/bko7fnktt2Eq0HT2PMtAm0u6cVL/j4kOjRkYtnTjD6/jHs3bMbJyenWr4LlZqgajQqVuf48eNkZGQQHtmfl37ZS6FOsdO8/+9XOXHiBK1C/PBs2Z1fd19k2/EkmjRpwtiBPfBp24fMemH85z//qeU7UKkpqkajYnWio6MBkF5NSYhL5e2J95KdV4CtjQ2tWrWixdkCzqVeYninYAa1D0IIwaReYZy/lomUU1h68hRt/t7OgN49Vce+2xRV0KhYlYULFzJv3jyCmoSy82w6nZv50KmZT5k2U/u0oHtLfzo18ykWJEIInh/WDiddDhtozlNv/hft9If58MMPGTNmTG3cikoNUJdOKlblrbfeQqPRMPLpN8nOK+ThPi0qtPFwdaRzqC8iKxFivy8+7upkz+xxPfm/ke2YPaEfnp6ejB07lq+++upW3oKKBVAFjYrVyM3N5cyZMzz22GNkOvrROcyXpn71Ku/wa2fY8Ajkl3XW6xMRzGOPTOPtb5Zz36QZzJw5k/3791t59iqWRBU0KlbjxIkT6HQ6wsPbMqlnGKPuDTHeIVtf+y0/s8IpGwHL912gWe/xeHt7M2fOHOpyLiWVsqiCRsVi7Nq1i/Pnzxe/j42NBaBdu7YMiGhEx6Y+lfSkrBZTUDH8QAjBgIhGnL6SyfOvvMHWrVtZv369paauYmVUQaNiETIzM+nevTv9+vUrPhYbG4uTkxOZth4kpmYbH+BKqbRDBjQagN5tlGyKje/pQ9OmTXn55ZfR6dTc9LcDRnedhBD/NnI6zUjicJW7jBUrVgBw4cIFAHQ6HdHR0bRu3Zr5q2MJD/LilTEdKh/gyu6S15UIGt/6zoQ1rM/e+Ou8+eabTJo0iRUrVnD//fdb7D5UrENVGs1wYBOw2cBjnHWnpnI7sXDhQgAaN24MwKuvvsq+fft4cOqTpGZqaN+kgfEBrh8veW1g6VREtxZ+FGp1jB4zlkaNGvH111/XeO4q1qcqQTNWSrldSrlNSrkNSAJuAtsBo3WXaoIQ4mMhxA4hxKfWuoaK5Th16hQbN24ElITVcXFxzJs3j+nTp9MqcgAA7UO8DXc+vx62zYEbcVAvRDlWiUYDML5HKAse64mzowPTp09nw4YNxVqUSt3FqKCRUhYnBhdCvAI8CAwBVpQ+Z0mEEB0AVyllT8BBCNHZGtdRsRzz5s3D0dGR1157DY1Gw4wZM3BxceG9994j5nwqvvWdaejpUrFjQTasnw4H5kHqcfDX/6nzK9dobPQOfVJKpkyZgpSS1atXW+O2VCxIVTaab4Fv9AnC/YClgEQRNtYiEmW5hv65K1BzpwkpAQlSV/WDUq91WuW9sy/cPA/XjoKNLXi2AO9WkHsdnLzA1qHGU7wd0Wg0/Prrrzz00EN06KDYYLZt28bTTz9NgwYNOHrhCPeG+RoOHTjyJWTpK3XoChVBc3opFFSu0QD8+PcpDidc55Np3fDx8eHgwYOWvq27i+T9yiP9HGRegqZDoPUUsGC4R1UhCDOAR4UQ04DFQGvAFZhosRlUxIOSEisZQJvSJ0sXkFu4cGHVBeRSDsLCzijysQbYOoJWU/aYY33QZIBbAIxeA74RNbvGbcihQ4fIy8tj8ODBBAUFFR/v2VOJS/p2RhR5BVrDna/FgHtjyL8JmvRSGo1xQeNob8vJxHQycwvo3Lmz6rxXE86vhz8GAyBtndA6eGJ3eonyt+k932KXMSpopJQFQoi9wG6gN0oFyc+llNZcFKcDRe6j9fTvS8/pa+BrIUTA5MmTE6sczbUhdH0NhE3ZBzYVjxU/bMu1RVHtHT0gdDQgIX6louH4tIf9H8DibtD2UXDxhY7Pg72BpcIdSFHAZPfu3cs40HXr1g2Aei4OVOoLnJ0MboHgcg+cXQkN2oKdk9GlE0Dbxl4A7D6dQqdOnVi3bh3Z2dm4urrW+H7uZLQ6yZ7TKaSk55Cek0/WzTSeSX4E4d2a3/y/5If9mQgk4+tvZ2LLh7Ckjl7V0ul/wF7ADaX05YvATCHEDCnlsxacR2l2A08AS4D+wI81Gs0tALob26U3E592Ja/D7ldc5498oSy3Ti+FSfvB1t7y161jREdHExYWhp+fHzqdDnt7e/z8/AgKCmLhttP4ejgzMCLIcOfsK8oSNGIGuPqBszfYu1W5dGod5Emofz1+3XGG+zt0QqfTceTIEbp3726FO7wzyNEU8t7/DrP3zFUAbG0EDzqvRshEGP47EbI1Mxtk4GBnS3p2Kxz8Qy16/aqWTg2ArYAzMEVKmQ28L4QwErBSM6SUh4QQeUKIHUCMlHKfta5VE3Jycjhz5gwNGzbE1zcQxqxT7Axxi2Ddw5CwBkJH1vY0rcYXX3xB//79iY6OZsSIEQDY2NjQokWLYlvNhqOXad3I04igSYZGURAyQHkAOLhXqdHYCMHUPi14bfF+tO2bAopdSBU0lWMj4ODZFN5on0B7n3wcdZmI2JVQvycEdqcV0KqRp9WuX5WgmQSMBbKBWUUHpZRWLcQjpXzOkuNl5ORzLSMXTaGW/EIdmgLluWtzXxzsbDl+6QaxF26UOa8p0PJ431Byc7L569BFdMKenJSz7Iu7QHZuAQc2LOFS3EFc3dx54u3vcfMN5rEBrWjUaiLa7S+Rtv0jPP3uxdbNH/LS4MIGsHVS7Dq2jlCYA0G9web2y9Rx/vx5nnnmGYKDg7lx4wbDhg0rPrdx40ZcXV3R6nRcy8jDP7ySJWShBvJuKEvb0ti7VWmjAejUzIcnB7ame0t/OnTowNq1a3nllVdqclu3J2dXwbHvleW6Tqtoht6tKzRzcrBj2YhMnDc8C6f1B50bQI+3itvs2LGD48ePM3jwYEJCQiw6zar+y4OllAYtQkKInlLKHRadjRVITU1l/u9bOZBaMRWk19kV5KRdJds7HNsmik1BV5iPtkCDtkDDSw9Eos3PpUnUg4T1ewhwBJfm4AJtBkzkg7mz+TY6kWMZzrjkXCP2oh+NvBtzLfgh/OPmwdcBFNo4IYTAVptbcXINwuHmRWg+Fnw7QPwKcHCDVpOgcX9FKNXBRE/bt28HFC9gd3d3hgwp2YT09/cHIDktB52U+Hs4Gx4kR1HhcfUve9zB3ajDXhFCCEZ3aQLAkCFDeOedd0hLS8PT03q/ylZFm6/86AgTooJ0WmX3M/cqrJuqCOz4FSXnQ0cpQqTZCFLrd+Wfy2J5akh7wuO+hnrBMPmg8r9V6kcuOjqaXr16AeDq6soXX3zB1KlTLXZ7VQmab4QQHxg4LlDsKPdabCZW4sKFC3zyz2dx9W2MrjAfXYEGe1sbnBzssC/Mws3VBfe0DFyTY3B3c8HdzQ13d3fc3Ny47425uLm54erqRmxKEu4e3vzjoaG4OTtgazOUaQv+xtbPnWsHV1OYsIcezymrPL/B77LPPpwTx/bjrrmMA/lkNx3PuKg22OTfhMJcSIuH3a9DYA84/pPyq+QRqvzDFf3T2LmAXwfo9gY07lt7H2I5tm3bhrOzM7m5uYwePRpn54rC5Ep6DgD+hvxnAHL0kdqGBE1emslz2X0qBf+Ifuh0b/HXX38xebLV/Eitw4VNEPsdnP0TGveBkSuMa7naAlg5EhLWKu9tHWDc35BzTREuCWvhxE/KDmnst3gDXwLaVQ0g7zr0fE+xhaH4Ii1evJhjx46xdOlSgoKCWLVqFXPmzLF4juaqBM27Rs69b8mJWIvWrVtzcOdm3N3dcXd3x9XVFVtbW4uMPaBdIw4nXMfLvgmvrPwKrSYbnD0QNrbcO2AK9w6YQramgGW7zlGok9j4tSzp3ATo8IzyWnNT+YX3aKr4+ySshbTTkHlRUY3/NxTueVbxbWjQxuBcbiXbt29nwIABTJs2jc6dDftTZuUW4GRvi79HJYImuxJBY++maHkmsjHmEkfOawhrGc6XX355ewganRYSd0DMf+HUb+DkDcEDlJ23z92hzXTov0BpW6iBXXOV/wlNuqK9FGRDj3cgIFLRUOo3KRm7cR+I+oAT55M5sHUpMuUgob4udA+SytI94klA8eb+8ssv+eyzz7CzsyMoKIhvv/2WiIgI1q1bZ/l7llLelg8gIDExUdYFVq1aJQG5c+fOStvodDrzBs++JuVvUVLOt5fyE2cpz/5l3jgW4vr16xKQ77//fpVtdTpd5fcd818pP0TKjItlj6+dKuV/g0yeT9zlG3Lgv1fLF+d9JwG5Z88ek/vecnQ6KXf9W8qvApV7/9RNyujXpCzUKOeP/STl8mHKuXXTpVw+XMovvJX3ywZJuXaalFtmSXlySZlhC7XK55yrKZA5mgKp1enk09/skA98uEE+suBveSUtW0op5dWrV+VLL70ke/ToofdeRc6YMUMWFhaadTuJiYkSCJAmfF9vP0tkHaRNG0XLOH78eLH/SHmEEOw5ncJv0fG8NfFe3JxM3Pp2aQAPboXsFFjWHzY/DcGnas0TOS4uDoC2bdtW2dZoIvFijcav7HETjcFFNPRUfGc6demGnZ0dK1asoEuXLib3v6XEr1C0k+CBEPURNBte1t+qzRRoOQF+6w4nfla2/psNg1aTIbh/heF+2nqKvw5eJDO3ABdHO7LyCmjcwI2Ppkby+rhO2NkKPFwdyc3NZfbs2fz6669cv36dVq1a8eGHH9K7d286dOhwSxK+q4LGAgQHB+Pi4sLx48eNtnNzsuf0lQye+mYHkc39cHd2IC+/kEf7t+JUUjrz/zyKr4czTw1qUzE2yNUPen0Ay4fArtchcq7i3HaLKRI0LVu2NNru079iaejpwrhuzQw3yLykLBnKC8wiY7CUJhnCnR2UZbBO2NGxY8diB8I6R+pJ2DZb2RG6/6/K7TC29jBhl3L/Vfhhebk50amZD97uTuRoCvCt74xffRfcnewRziWf3c8//8xHH33EgAED+OCDD2jfvr0l78wkzBI0QojOUkrV71uPjb5syIkTJ4y2C2/sxbwpXflmUxxrD11EU6jj8QGtAGjk7Yq3uyPHLtzgy/XHeXO8AdtHyGDFM3nfu3DwI+g8B7q/aY1bqpS4uDicnZ0JDg6utE1+oZaNMZcZ0bmSNlLCxS0Q0LXiOQc3xR9JqzFJkNrb2lDP2R4hFO/kBQsWoNFocHR0NPWWrMuVvbD2IUg7o3iWj1pZtUuDiS4PwzsFM7xT5X+HIpYvX05oaCjr16+vtXI1RvfShBA2Bh62wDu3aH63DW3atCEmJoa8vDzj7YK8+GRad/730mB+/0d/xnRVHM5cHe15Z1IXxvdoxr4zV3nplz2cuZJRtrMQMOIPeGAzNBkKe96CMysMXMV6nDx5khYtWmBjU/m/zpkrGRRodYQHeRlukHYaMs5BEwOxuQ56X9B801y1hBAsnT2QB7uH0qNHDzQaTe0HWaYcgpTDsPEJ+K2nIjijPoJpJ6FRrxoNLaVkS2wiby07REZOfpXt09LS2LJlC/fff3+t1sSqSnRmAXtQtrOLAlkE0K7SHncp48eP5+eff2bChAk88sgjDB061Ogf1tZGWT+XZ2TnEA4lXMfe1oYQX/eKHYVQtroDuivxVRumg19HqFeJ962FiYuLIzIy0mib45eU7enWQZX4tCSsUZ6NCppMJW6sGhTZx/bu3VuprczqxP+pbD+Dsixs+wh0e1OxtZmJVqfjqw0nOH81k/NXM7mZW4CfhzNuTlVrPvPnz6ewsJAHHnjA7OtbgqpmGgeMllKW+WkVQmy03pRuT+677z7efPNN5s6dy4oVKxgyZAgDBw5k1KhRNG7c2ORfEycHO96fXLKkuJqRyzPfRdO1uR8TeoSWbBfbOcKw3+CXDrB+GozdaHXnvoyMDC5cuMC0adOMtjt+8QaNvF0NClJA+cV3D4L6IRXPFQkajenO519tOIGnqwPjujXD3d29TIL0W4qUim9U/abQ5VVoMliJtTODpbvPciNLwxMDWnPiUhobYy4T4uNOt5b+tAjwoHtLf2zLaZU6nY7U1FR8fHzIzs7m3Xff5b333mPq1Kl06tTJAjdoPlUJmmGAAZdW7rPCXG57XnvtNWbPns0nn3zC/PnzWbNmDbNmzaJevXqEhYXRsmVLoqKiaNOmDZGRkSYJH52UdGjSgL9jE4mOu8KU3i3oGx6Iu7M9eIYpBuLNT8HJ36DVBKve3w8//ICUsownsCG83J0Ma2NFFGQrnqmGcCzSaDIMnzfAsYs38HR14MHuoTRu3JiLF033w6kxaWcU43z+TcW2cvUwDPoewo0LY2OcTb7J95tP0rW5H1JK2gZ78785gyr8v/zxxx+8+uqr6HQ6ZsyYwfLly9m5cyePPvoo6enpLFu2jBEjRvDxx7Wf2lvIatTGEUJMkVL+bMX5mIwQIiAxMTExIMC8X4xbwZkzZ1i3bh2nT5/mzJkzxMTEkJysbOtGRUXRrl07NmzYwNChQxkxYgTdu3fHzs6w7L+SlsO7yw9zKikdLzdHfni6N04Odorz16IuimfoI2estu2t1WoJDQ0lKCioOATBbJYNUgTJxD0VzyUfUArJjVwJoSNMGu7Fn3cjJXz4cCRDhgwhJSXF+nYaXSGcXgbbXlAEp2tDSI+He/8Pur1uWihBOQ6cvcbGmMscOncNIQTfPdVb+UEpx5kzZ9i4cSMvvPACzZs3R0pJbGwsnp6eDBs2jMWLF1NYWMjrr7/Ov/71LwvcrGGSkpIIDAwMlFImVdnYFGcbWeIkt6U67a35oA457JmKTqeTZ86ckZ9//rkMCAiQQgjZtWtXaWNjIwEZFBQko6OjjfY/lZgm1xy6IKWUMi+/UF6+niXlubWKU1fMf6029yNHjkhA/vTTT0bbaU1xTFzcQ8rf+xg+d+O0ci/HfzF5bv9cvE8+9fV2KaWUTzzxhGzQoIHJfc2iMF/KP8cq8/y2mZTXjinHteY5vkmpfG6zf9olx8xbL99dfkgev3RDSinl119/LR966CH5/PPPy8GDB8vGjRsXO9uFhobKlJQUWVBQIM+dOyfz8/OllFLGxcXJL774QhYUFNT4Vo1RHYe96n65Z1envTUft6OgKU/RP8LVq1fl0qVLZWhoqPT29pZffPGF/OGHH2RqaqrR/m8uPSinfLZZ8Tj9tauUHztKuf/DEk9TC/Ljjz9KQJ44ccJou+V7zskJH2+UWbn5lTf6+R7FA9YQWcnKF/jwApPn9s4fh+T0L/5WXr/zjgRkdna2yf2rTfQ/lTnu+0BKnbZGQ6Vl5cn4KxlSSikLCrUyO69EOMTHx0sHBwfp4OAgnZ2d5T333CMnTZokP/74Y3nmzBmzPXothdU8g6WUH1anvYpxipZJPj4+jB07loiICAYMGMDMmTMB6NChA1u3bsXd3bC9o0VAfXbEXSE9Jx+PUSth/SOKU9jpZTB+h0VTUBw5cgRnZ2eaN29utN35q5kUaiUujkauXZCjBIwawgxjsE89J67dVEyJReVeLl26RIsWLUwew2TSzykZFVtOgM4vmj1MSnoOy/acY/3hSzTydmPBYz2ws7XBzlZZcul0OmbOnIm9vT2nT5/G39/fqEtBXef2nfkdSFhYGAkJCVy+fJmlS5cSExPDCy+8UGn7FoEegOK3gosvjPoTBn4HV/bAAcvlewVF0LRr185oQOql61mcTEwnxNfduKG7ILvyVKd2ToqANNGPBuDR/q2YP1XZzi4SNFYxCOelK1vXto7Qa57Zw2yMucxj/9nGXwcvEtUmgJdHt6/web399tusW7eODz74gICAgNtayIAaglDnEEIQGBjI2LFjee655/j4448JCAigYcOGjBgxgoYNSxJFhTWsj42AU4npdA71Vba3w6fBudWw81UlsrfJ4Mp3eExESsmRI0d48MEHK21z5Px1Xv5lLxL4R9cq3KwKc8C+kvy+QihaTTUETWmKBI3Faz0VamDlKLhxSsmm6B5o1jCXrmfx0Z8xtA324sWR7fGtXzHFhpSSL7/8kmHDhjFjxoyazrxOUG1BI4QYDaQAXlJKtaCOFZk7dy7r1q3jjTfeAGDt2rXFpWcBnB3saNzAncPnU5kcpT8oBAz+AX7vBX+NVxKtNx8LQ35VysSYwebNm0lPT6djx46VtmkV6Mn9XZvQ1K8e/ds1Mj5gQXblSyeotqDZEXeFP/ef580J9xIYGIidnR1nz1q47FjMl3B5GwxZVKPcQL71nXn6vjb0bNXQoJ/Rtm3buHTpEsnJyYwaNapWvXktiTkaTQhKCZSq06BVEyHEVOD/gCvAPillFbVU7mzq16/PsWPHyM3NZe7cuXz88cecP3++TJrFR/u3xK/8r6JjfRgfDRf/VlKIHlkAgT3hnqerPYfMzEymTJlCixYtmDjRcJWdtCwNX204wch7Q2hdVd5ZnVaJY6pMowHFl0Zjuh9NeraGoxdukKspxNPNkebNm1cZ4Fptrh5RKjaY6at0/WYehxOuMyCiEcM7hVQ4X1hYSF5eHiNHjiQjQ7n3/v0rRmzfrpiz8EtBSVheSbGeGjNPStn7bhcyRQghcHFx4bnnnkMIQbdu3YqtZgAeAAAgAElEQVTrXAN0DvWlsY8BY7GDu+KH0vdzJanSthdIXPMa8+YZSphYwrVr18q837hxI1euXGHBggWVljO5npnH1uNJpGdrDJ4vQ6He/9NYORqH+tXSaJzsld/L3PxCQIk7s7igSTsFXuYZl1Mz85j5bTTfbzmJVqf4rf388888+eSTTJgwgV69euHm5kbr1q2LhUxoaKjRwNXbDXMEjR8QBVgmTV1FZgkhtgsh+llp/NuSoKAgli5dip+fH0899VQZgfDjsrXM+WY9BVpdhX46KdH0/x5dYBSBcW/TKOYlTp86ZfAaH374Ib6+vhw9erT42MaNG3FzcyvOJ2uIIgFT38UEZ8GCbOW5So3GdEFTtMOVm6/89oWHh5OQkEBOTo7JYxhFSrhxEjyNp8YwRFZeAR+tOkqOpoB3Jt6LrY1g3759PPzwwyxdupSDBw8ipeTBBx8kMTGRDh068Nlnn/Hvf1uhRFAtYs7SKQ5Ixjr5glcAPwPewAYhRCcpZRnNqdqVKu8gRo8eTcuWLWnbti0tW7YkICCAoKAgDl3IIGL8KxxLuMo9oSWpMX/77TfmzJlDSkoKgQENebQVvNIPlvz1Gc1bLCgzdmxsLC++qGzX7tq1i3btFIPuxo0b6dOnD/b2ledGSc9Woog9XExIzVCo//JXaaMxLAwN4aTPSZNXUKLRSCmJi4szalcymawkZSlXTY3mhy0n+d/eBDSFOmbeF04TP2Xr/o033sDb25vz58/j5uZW3H7GjBn4+vrStGnTms+5jmGORrMDuCaVipFmIYTwF0JsLff4TUqZLqXUSSmvoRSF8CvfV0r5tZSyE3DfbZEf1sK0atWKJUuWcP/999OsWTPi4uJo1kDJ2/L98g0cPnyY2bNnM2jQICZNmkTDhg154okniGh/D0EPfEWGxgafxKXk5OQUOT5y5swZ+vTpQ1E4R5FGs2TJEs6ePcuAAQOMzqkoXUF9VwtpNNU0Bnu4OBDqXw9bG8VwWjrjYY3Z8Qp8rTdue1VPo2nmX5++bQNZ8GiP4rwxqamprFmzhqeffrqMkAHo2rXrHSlkgOrnDAYWAHOA4dXta8LY9fTPzsAhwN5I29veM9hS6HQ62eeFb2XHh9+SgLS1tZXt2rWTDzzwgMzKyirTdtu/O8uC95HtA5A+Pj7y008/lcOHD5fu7u4yPj5e9uzZU3bo0EFOnTpVArJr164yLS3N6PWX7jorx8/faFpe5KS9ilft2dWVt9n6opSfOJly6wYpKCiQTk5OctasWQbPFxYWSq3WBI/elMNSfmSjzPdDpMw4b9L1M3Iq98xevXq1BOTWrVtNGqsuY+2cwddRDMJNqmpoBs8LIQajaFrvSSkLrHCNOw4hBKN6tOWvWD++W7iUAb26EBRkOD9Nu8d+Q7OoAxufd+CJzS147jmlVt9bb71Fs2bNiIiI4IsvvuDQoUO8/PLLvP7661Vmqxsb2ZSxkSb+EptqoynMU0rPmBEkamdnR9euXSsN/hw0aBCZmZlER0cbXRKy+w1w9IT2T8G5v5TUFka4djOX7zef5PilNNqFeDN7RESFNnv27MHW1rbW0zbcaqq1dBJCPAVsBnpUt68pSCnfkFJGSim7SCmXWHr8O5nxfdrS0NOVjt37VCpkADz8m+I6djkN7DNZNjaZ1Sv+4KmnnmLWLKUQaatWSmrRNm3a8O6771o+JaYpNhpHxeOZ7CsmDZmtKeCZ76LZfPRy8bGoqCiOHDlSvItT3DY7m82bN7Nv3z4mTZpU4XwxmZeVUjdtH1Vqtz90sExEdlxcHA888ACZmUoidZ2UzFsZw5ZjSaRk5Fa6zb97927atWtX6Q7enUp1hcVvKMuaeMBq9bdVqo9vfWd+eLo3ESHe3MzJ5/ilG5U3btwX+n+FSI9naGTTMlvXw4YNo0uXLvzxxx8mX/vzNbH8uv2MaY1N0WhCBinPJxZW3qYUjna2nE7KIDm9JHVSVFQUOp2uQrLyAwcOFJ9fvnw5AwYM4JlnnmHcuHFld6livgKpg3aPG7zmwoULWbZsGUuWKL+HO08mE3M+lRmDWvPa2A4Maq/YdfLy8vjXv/7F5s2bGTNmDNu3b68yQ+EdiSnrK1nWNuIKBFa3n6UfqDaaSvl56yk59O018thFI9HfV48qdoe4xTW+3sOfb5Hv/HHItMaxPyjXTT9nvN2SvlJ+HWxy6oXh766VX204Xvw+OztbOjo6ymeeeaZMu6Lo7tTUVPn7778Xp1wAZN++fZWo76wrUn7qKuXKMZVer1evXhKQkZGR8vLly1JTUCi3HkuUhVrFTqXVauX8+fPlmDFjisd3cnKSY8aMkTExMSbdU13H2jaaD4ALQog4KeUqC8g6FQszonMIm2MTmfPzHgbdE8STA1vjYFfO7ckzDBCKf0gNKNDquJGZh5ebiUssUzQagPBHYM0kSN5vuFpCOdyc7MjJKyx+7+LiwsiRI/n111+ZN29e8RJw165dtGzZEi8vL8aNG8f+/fvJzs4mMjKSqVOnMnToUDa82g57rQZ6Gi7UqtFo2Lt3Lx4eHhy/mMrEjzfyrxFh9O3VvbjNkSNH+Mc//gEo29b5+flMmTLFqD/SnUxdMwarWID6Lg589HAki6PjWXXgApdTs3lrQueywsbOScnZe8N0fxVDnLiUhqZQR9vGlVQ8KI8pNhrQL5+EEkJhgqBxdbQnW1N272D69OksWbKElStXMm7cOACOHTtWJnH5vHklUdi2trY89NBDJPU5RHDrPnphXJEDBw6g0Wj47rvv2JrkyMU8Z95969/03bC+uE1R9PiiRYsYP378HROzZC7mGHQ3A2PN7Ktyi/B2d2LmfeHMHhHB1Yxc0rIMhAd4tayxRnPg7DVsbQQRTbxN61Cs0VQhaJy9wb8TnN9g0rDhjb0I8CqrJfXv35+GDRuydOnS4mNXr14tEwFfmokTJ/LvV54j2PUmWR4VHf22bt3K448/ztSpU3FxcaF1514kF7rSyC6NTRs3sHbt2uK2RYKmX79+d72QAfOExUTgW6B2arKqVIsBEY34+sle+HkY+GJ7tlBieGTF0AVTcXOyo3ebAFwdTSzxW5CjaFOm5NQNHqjk1slLq7Lpc0PbMr1vWYc6W1tbZSm0YQMFBQVkZWWRk5ODr2/lZVwm9lJ27LadKzm2cuVKQkJC6Nu3L4sXL0aj0fD296t4+fejaHWSFyYOok2bNkyfPp316xWt5tKlSzg6OuLj41P1fd4FVHd7ewaQCmwAjFdKU6kzONjZcjIxnae/2UFCSimP2wZtlSDHM/8ze+wHu4cyZ1Q1SqwWZIOdiVu7YaNBauHkYvMmBwwZMoSbN2+ya9cuUlJSAPDzq+BwXkyIzVnyCuGn9SVVR3/88Ueys7P55z//SXJyMhcvXuTeiNb0axvIf5/oRetgHxYtWgTA4MGD2bx5M5cuXaJRo0aqNqOnuhrN78B2YBZghTyJKtbC1dGO+OSbnC5d/bLVRGjYBdZNhesWjnaujMKcqpdNRfh1BN974OjXSmCjERZuO83T3+yocLxfv37Y29uzZs0arl69ClC5RpNzDXH8R07kt2DZ/1bx+eefk5aWxtatWxk1ahRvvPEGLi7K3Lu19GfOqPYENVDCCNq1a8fZs2dxdXVl2bJlXLx4sTgJl0o1BY2U8gaKIXielLL6yU1Uao1Ab1ecHWyJTy4laOycYPgfSr3rlaOgwFAJr8o5cyWDMfPWczjhuumdshLByUR7DkD4dLgWA+nGE1nlFWi5cK1iiqR69erRs2dP1qxZU7VGs+99KMyl9SO/069fP5599lm8vLxIT0+nT58+AHy06igfrzpqsLuLiwsDBw5k9erVXLx40ajj5N2GOTaaxsBrQohfLD0ZFethIwTN/OsTf6VcsKJ7oJKzJj0eUg5Ua8zUzDyy8gpxdjBx81JbAIk7IbCH6Rcp2vnJuWq0mauTPQVaHfmFFdMkDRkyhGPHjrF//36gEo0m6wrELIBWk3EKiGDt2rVs3FhSkLVPnz7k5Rey/cQVbGwqXw4NHz6cy5cvk5iYqAqaUpgjaGKklK8D5i/sVWqFUP96nE3OICW9XJ4WP/0OSzW3um/od7JM9qFJOaAsnYJ6m36RonAETbrRZkV1qLPyCiok4CqqrPnLL8pvYwVBI3VK9QhtAUTOBZR4qf79+7Nv3z7mz5+PzqEeC9YdR1OgpU945UULR48eXfy6Lhc3vNWY40cTJYRoAlS9FaBSpxjaMZirGbm4OZXbIXJvrGT2TzNP0HiaKmgu/a08N4oy3q40jvqYIY3xf7eiXa8JH2+mkZcr3z3du/hcy5YtadKkCQkJCXh4eODgUGrDVKeFDY/CyUUQ+S/waFZm3M6dO5PlHMD0L7diayMY1L4R4UZ8hjw8PDhw4AAPP/wwPXv2NP0+73CqJWiEEJOBZsDHgPEkJSp1jsYN3PjXOANRwza2yhKl2hpNHvVdHLC3NVExvrRV2elyaWD6RZz0Gk2ecY0mwKvEwHz5RjZZeQXFAlUIwZAhQ1iwYEFF+8zW5+H4jxD5erE2U56OzXyYPSKCDk0b4O3uVOWUO3bsyLFjx6psdzdR3aXT/4BE4GGULHgqtyFXM3KZ88seNsaURDsX+9RUg5aBHgyMqKLiQRHafMU+U51lE5RaOhnXaFoGetK31JLm2MWyQaVFy6cyy6bcVGVHK/wR6PYvpYKEAeo5OzAgopFJQkbFMNWtVJkNmJ1ZT6VuUM/FARshmL9KqS/k7+GipKk8u1KxU9ia5nw3MKIaxs7k/Xr7TJ/qTdbWQQlXqEKjATh/LYuIEG9OXEoj5nwqXZuXaC+9e/fGycmprEZz/CelIkOH5wyOl3gjmyU7z+LubM+EHqG4ll9yqpiMWWEEQqFZ1S1V6iJO9rY8N7QtOgnbT+hzvng2B10hpJ02eRxNQTUKYVzaqjw3MiOo0MmzSo3makYu51JucuFaJuGNvThwtmw1BxcXF77//ntmz56tHCjIhUOfQkB38GlrcMzvNsWx7sgllu9NUB3vaog5BeQ6ADFALyFEhpSyGk4UKnWFhp4utAjw4IctpxjTtQm2RUua+BXQoE2l/TJy8vl09VG6tfTnoz+PMqFHKFN6G6/HDSihBN5tlBim6uLoUeWuk5ebI13DfBnXvRkFhTo0hVqklGUExIQJpWoyHZwPmRfhvp8NjpejKWRf/DX6hgfwQLdmxmuJq1SJORrNKMBFSvlDTYSMEOI+IcRJIUR0qWPuQohVQoidQogp5o6tYhp9wgPQScn5q1lQLxgZ2BPiFhr1wt135io7T6Uwb2UMNgKi2hgOUKxAViLUNzPg38mzyngnO1sb3hjfmTZBXrRv0oAuYX7GtZBTvynLuCDDO2B7z6RQoNUxtGMwTf3UHG81xRxBsx94Rgjxrxpeew9QPqnqY8BioBfwqBBCDdy0IsM7BTN/aiSBXi7kF2pZlh4JN05yPaFyx70BEY2YPzUSb3dHHu7TgmBDxesMkZ0Mrv5VtzOECRpNeZLTc/jp71PoDAlNKeHmBWgQXmn/YB93HohsSuugKipvqpiEOYLmLynlO1LKN2pyYSllmpSyfO6CSGCTVGo5xaDGU1kVO1sb2gR54eRgh6ZAh22Qkrhp1bq/KnjY5uYXcjY5A61OR5sgLxY+149x3Uw00+m0kJNivqAxQaMpT9zlNBZFxxMdl1zxpCYD8jOhXuWVIJv61ePR/q2wUW0zFsEcQfODEOJXIcS3Fp8NeABFPvIZQIWfEyHE40KIA8Da0qVhVWqGu7M99w9UXKNsMuJZviehzPmjF1J56ptoTlxSvvDV+gLmXle8b11unUbTq3UA9V0cOFjOKAwo2gwojorliL14g9cW7+PguWvFda9Uao45gmY3sAlYW1VDqLxYXCXN0ylJel5P/74M8i4vIGdV7J3BvTH3eNxgUXQ8126WBFmeTsrARkBow/rVHzdbr1W4mWjPKY+jp6KFVCNvjq2NINjHjQvXMyuezFSSUlGvoqBZtuss++Ov8fmaY6hixnKYY0r/CyWNp0mp3KWUyUBvE8feDfQTQiwB2gM1yzOpUn08w2iZe52BIY1wtC9J/Xk6KZ2gBm6mB1CWpqhsirkajZMHIJV63EWewiYQ7OPOltjECrtP2dfO4goGl05JaTl0aubDPx/oqC6bLIg5Gs3LQFfgRFUNjSGE6CSE2ASECyE2CSGcUDL3TUIpu/u9ARuOirXxbI5DZjwz7wunnrNii5dScvpKBs0bmv4lL0ORRmO2Mdi0eKfyNG7ghhBwM7eAs8kZHDx3jdgLqazbHo3OxgFcygZX6qTkSloOIb7uONnbVjKqijnUWnJyKeUBoH+5w3nAsJqMq1JDPMMUw2tuKjsS8nG0t6Wxjxvp2fk0DzBj2QQlGo25gqZIIKSfrdYW+dCOjRneKRghBPNXHeXEpRv8MLMPuQ43SNX54FMunejNnHy83BwJ9Lq7irvdCszRaJYBnczsq1LX8dQ736XGsTg6nj/2nMPLzZG3J95LZIvKU2AaJTsZHOqZnlmvPEG9wd6t2ik9bW1sEEJw8XoWe0+nMKxTMG5O9oS6pJFY6M25lLK5eTxcHfn52b4M6aBmxrM05ggLeynlM1LKTyw+G5Xax7+z8py0k3Yh3py4rCxXOjXzwaees3lj1sSHBhQB1XwMnF5W7SyA328+ySu/7sXO1oYRnUJAcxPP7GPEyzDWHLpo/pxUqoVZnsFCCBO9tFRuO1x8wasVXN5Gu2Av8gt1TP50CxeuGdi9MZXsJHA1c8epiLAxkH8TUg5Wq1uv1g3Jzdcy+J4gJW/OhY0IXSGy6VA2xlwmNVPJsR97IZWJn2zin4v31WyeKgYxx0ZT5BlsX1OnPZU6SlBvOPELHYd6Et7Yi2MXb3Au5abpXsDluXnRvGDK0tQLUZ6L7D0mEtqwPr8+1xf7ouJ551aDowcjR06kU5oGLzdHNh29zLyVMQB4u+XXbJ4qBjEqaIQQTwGHpJR7hBAzUYzAJ4G1UspCY31VbmMaRUHMf3C8cYQPp3Tlcmo2jbzNNJDqtEqckwGflWpRtPSqpqABcCq9JX95OwT3x8HBkSZ+jmTlFRQLmQ+ndKWpvxrXZA2qWjpNkFLu0b9+CPAFngHetuqsVGqX4P4gbOHsnwghCGrgZn6ahOxkpTaTew0TdTt7g41dyVa5OWjz4eZ5ZWmox83JntfHdeLFkRG0DfY2vRCeSrWoaulU2o/lZynlAgAhxPpK2qvcCTh7K8un039A97cqzTxnEpmXlOeaChphAy5+Zmk0xWScV7yLPULLHDZ7N03FZKrSaJKFEJEARUJGj5rT8E4n7H4ltWdqDQvLFbn7G4grqjauDWum0aTHK8/lBI2K9alK0MwC3hJCfCmEGCaEuFcI8RpwuYp+Krc7YWOUpcrxn2o2jqU0GtALmhpoNEVF6DxVQXOrMSpopJTXpZT9gFUo8UqPopRZmW79qanUKq5+0HS4Pq9uDXZiMi8pznaOZnoVl5mTf801Gns3cPap+VxUqoVJ29tSyrWYGK2tcgfR7jGI/x/Er4QWD5g3RuYlRZuxRICia0OlYqWuUNG2qkt6vLJsUoMlbzlqGIEZHDt2jG7dutGzZ0+mTZtWnLfk+eefp2fPnjz3nOGs+rcdwQMV20rsN+aPceMUeDS1zHxc/QEJOQZyzFSFrlCp4a0um2oFo4JGCPGxPrevmUEqdyYtWrRg165d7NixA4ADBw5w6NAhsrOz2bFjB/n5+cV1nm9rbGwhfDpc2AgZCVW3L4/mJqSeAP97LTOfIu9ic+w0x39S/HlaTrLMXFSqRVUazTwU35kFQojfhBD/J4QwUOrw7sLevsTXwtHRkaCgIHbv3k3//kowev/+/dmzZw86nY5HH32UqKgo7rvvPkCpL/Tqq6/SqVMnvvnmGyZPnkxERATr15f1GNBoNIwYMYLBgwczfvx4fvzxRwoKCujXrx+9evVizJgxaLVazp8/T58+fRg5ciRdu3YlIcEMgWCMNvoc8WdXVb9v8n5AQsMulpmLs74UbTWz7QGwf54SxxU60jJzUakWRhe6Usok4Cf9AyFEBDBQCPEC8IeUcpn1p2ges2bN4siRIzUao3379nzyieHY0T///JNXXnmF5s2b4+3tTXp6Os2aKTl069evz/Hjx1m5ciW+vr58++236HQl2eHGjh3L3LlzCQgI4MSJE2i1Wp588kkGDRpU3GbFihV069aNl19+mRkzZgBK4fnVq1fj7OzMa6+9xpYtWwgLCyM5OZmNGzdy8OBB3n//fb766qsa3XcZ6jcBt0aQtBs6PFu9vsl7lWdLaTR2esW6IKd6/bT5Sr2qyLmqfaaWqG6lyhiUpOF3PSNGjGDEiBE888wzrF69Gg8PD27eVNIO3Lx5Ew8PD06fPk23bt0AsLEpUR7Dw8Oxt7enZcuWxZUT09LKJnVKSEigXbt2gCLwALKzs3n88cdJTEwkJSWFsLAwwsLCaNu2LXZ2drRv3574+HjL32xAJFzZXf1+SXuUUrtOFqokUJRmorCagubmBUBCfQvZilSqzR1bFasyTcQSaDQaHB0dAahXrx7Ozs5ERkby3//+l3HjxrFp0yamTp1KUlISe/bsYdiwYeh0umJhU+TOX9qtv3wi7CZNmhAbG8uQIUM4evQonTt3Zv369TRv3pxFixbx6quvFvc5duwYWq2WmJiYYq3KogREwumlkJUEbgFVty8i9XhJ2glLYK5Gk3FOeVYFTa1R7V0nfaXKu5p169YRFRVFVFQUKSkpDBw4kA4dOuDk5ETPnj2xsbHh3nvvZcSIEVy5coVevXoxbFj1EgeOGjWKnTt3MmjQIJKTk7G3t6dLly78+eefDBs2jPPnzxe39fX1ZdSoUTz77LPMmTPHwncLNNSnhz70qekJwoviijzDLDcPczWaIkc9S+1+qVQfKWW1HsC/Affq9jMwzn0okeDRpY5NRUlIvhX4oIr+AYmJifJOpqCgQEop5ZNPPil37dplsE1CQoKcNGmSdSei00q5eoKUHyLlvg9M65N6Sml/7EfLzUOTqZ/DvOr1+/sFKT9xklKns9xcVGRiYqIEAqQJ3/fazEdTVKlyc7nj86SU1qgZddsxdOhQsrKyCA0NJTLSpKIT1kHYwJBfld2efe9C28eqrkZgjbgiO32Gv+pqNBnnlGWTagiuNcwqtyKlNGOvsyxSyjTAUPqBWfq6229IKcsLobuK8lvehggJCeGWFNITQonkXthRqVsd8aTx9tYQNDa2YOsAhdVL51ksaFRqjbpWqXIF0A4YA3wohKhQ80KtVFmL+LZXtJusxKrbpseDg3uFkiY1xs6lesbgC5vheqwyd5VawxyNZjdKnpqbVTUEpVIlUL4yZbKUcnz5tlLKIk+sa0KI04AfkFSuzdfA10KIgMmTJ5vwH69iMYQNODdQ4o2qwlpxRfYupi+dLmyCVQ+AV0vo/JJl56FSLepUpUohRD0p5U0hhDMQBpgR1KJiVVx8q441khKuHoagvpa/vqkajeYmrBip7DSNWgUObpafi4rJ1LVKlc8LIXaj7Dq9J6UsqMk1rMXevXuLgyqff/754uPz5s2jR48eTJo0iYKCOjn1muPiW7VGcz1WSecQXL4+oAUwVaM5s1xpN+AbqB9i+XmoVAtzBI3FKlVKKftLKT30z3lSyjeklJFSyi5SyiU1Gd+aBAcHs2XLFnbs2MHVq1eJjY3l2rVr/P3330RHR9OuXTtWrFhR29O0Ds4+kFuFoDm/QXkOHmD565uq0ZxcpBiALRVnpVIjzBE0m7jLK1X6+/vj5KRkM7Wzs8PW1pZ9+/bRu3dvoCSoEuC1116je/fu9O3bl/T0dKZOncqsWbPo3r07b7/9NjNnzqRjx458//33Fa4zbdo0+vfvz/Tp03n99dcBGDduHFFRUQwcOLA45CEiIoIJEybQsWNH60eNm7J0urABvFuDeyPLX98UjSY7GS5uhlYT1S3tOoI5NpoBwCtSyhpUFLs1vPhzxficXq0bMrxTCHkFWoPFwgZENGJgRBAZOfnUd3EwOv7Ro0e5fv06rVu35vDhw9Srp5TqqF+/PmlpaRw+fJhz586xc+fOMiEGUVFRfPLJJ7Ro0YLffvuNjz76iL59+zJ9ekniwr179+Lo6MimTZt4//33yc1VtnR//PFHXFxc+Pbbb/n999957LHHuHTpEjt37iQjI4MnnniC1atXm/V5mYSzj+JPo81XtpoNkXrCOssmUDSavCqit0/9rngwt5xonTmoVBu1gJyZ3Lhxg5kzZ7JkibLC8/DwIDFR2QQzFFRZ2l8oPDwcgIYNGxYHWJb3JyofVLl79260Wi0vvvgisbGx3Lx5k9GjRwMQGhqKm5sbbm5uZGRkWPfGi7arc68bjnuSOn0J3GrERFUHOxM0mrhfwfce8G5lvJ3KLeOOLiA3b0rlG2NO9rZGzxvTZgoLC5k8eTLz5s3D318pbNa5c2e+/PJL5syZw6ZNm+jatSvNmzdn1apVzJw5EygJnCwtVCqrl9SkSRO2bdsGKJoTwJEjR8jOzmb79u188803xYItPj6e7OxsMjIyirUqq+Giz7ebc9WwoMm5ptRxqk7wZXWwr8JGU5Cr5MGJfN0611cxC7WAnBksXbqU/fv389JLL9G7d292796Nr68vvXr1okePHhw5coRRo0bRvn17goODi2001dE2unTpQl5eHv369ePo0aPY29vTokUL4uPjGTx4MPv2lSz7goKCmD59OsOHD2fu3LnWuOUSnPUaTWV2mqLsdzWttV0Zdi7GPYOLkmK5qrWa6hTGAqGATaVeP13q9XpTAqms+eAuCqp877335KJFiypt171791s1JSlTTyqBjScWGj5/bo1y/vJO61x/62wpP3Wt/Pz1OP38Kv+8VCyDJYMqk4UQkVLK3VItIHfLeeSRR3av58cAAAv0SURBVEhISKBevXosW1ZHkhm66DWFG6cMn8/SazRuVtRoCnIUp0BDy858vdZoifIuKhajKkEzC/hdCHEKWANcBQaiFpC7Jfz0k2nF26Kjo608k1I4eUCTIXD4M+jwnFI+tzTZ+ogRqy2dnAEJWg3YGfi906iCpi6iFpBTqT69PlBc/I98qbzXFijG4ZSDiqbj5GlYCFgC+yqy7KmCpk6iFpBTqT4N2kDDeyFhDXR9FX5sXZIWAhRnPWthVzrLnlfF80XGYAdV0NQl7lrvXpUaEjIYkvfB5R2KkAl/pCRHTWX2G0tgqkZTVWIulVuKKmjMICkpqThHcGFhiTvRHVep0hghgxXnvOhXlfddX4Oo+crrlhOsd127KvIG52co6Szs1WjtuoQqaMzAy8uLzZs307Vr1+Jjd2SlSmP4d1bK5SbthHohSoS0vTM8cxMGVYzbshhFGs2WZyD3RsXzmgxwqKfGONUxVEFjBk5OTnh6lq1VdMdWqqwMG1sY8F/ldVCfkuMO7mBrb7iPJSjaXk+MVna+yqPJUA3BdZA7tq4Tf8+CqzWrVIlve+hjWn2oO7ZSpTGaDIYRy8HvFlbg8Y2Ah4/B1uch9ltlyWZT6t9YFTR1kjtX0Nxi7uhKlcYIG31rrwfKrlfEU/DnaIhfAc3HlpzTpKs7TnWQO1fQmKiJWIo7ulJlXaTZcCUn8d53IWxMiU1Gk2GdPDgqNaLWbDRCiMeEEHv0j4n6Y3ZCiF+EENFCiJdra25VUVBQQP/+/YmJiWHQoEHs3bv3zq5UWRexsYV7X4arh+DCRsjPUo7nZ4CjurVd5zAlIMoaDyBE/2wPHNS/vh8lqRbAasDfSP+7Jqiy1itV1lUKNVL+x1/KH9sqlSg3PC7lF55Sbnq6tmd2V1CdoMpa02iklOf1LwsBrf51JEqqUIC/AQtWiL/9GDp0KN27dycnJ6d2K1XWVWwdoN0TSjJ0KeHo15CXphqD6yB1wUbzJErhOAAPSupFZQCeBnvcJdSpSpV1lYgn4eRiiPoQruyBve+Ak3fV/VRuKVYXNMYKyAkhugBDgFH64+lAUYq4ekCFLRQhxOPA44D9woUL7x6bhIphXP1huj7koekwCOwBDbsa76NyyxGy3G7HLbuwEIHA78AIKeUN/bH7gRZSyneFEKuAx6WUVyrpH5CYmJgYEGCllJEqKipGSUpKIjAwMFBKmVRV29r0DJ6LUvJ2uRBiq7465SqUgnLRwO7KhIyKisrtRa3ZaKSUT1RyatItnYiKiorVUWOdVFRUrE5d2HUyF5vk5OTanoOKyl2L/vtnkrJSa8bgmiKEsEex8eiqaqtnEvCr9WZ0y65xq66j3kvdvE5duhcbIEX+f3vnGmNXVcXx358CloZp08YQKD6wglWgtEIJBRHGMjRYH2SiUuMrTRlFPjRRjIlYxFFqCh8Mtmq0DZKJJkQIYIC2fHA6HTrTBwJ1aJWKiLUhGqPGViEyBezyw943PT2ec8+5c++Zdu6sX3Jz9137sfY6+84+++7Zax2z14sam7ATTaNIetrMFk50HeOlx205MfVMVFt8j8ZxnMrxicZxnMqZTBPNhjbRMV563JYTU8+EtGXS7NE4jnP8mEwrGsdxjhNtN9FI+qCk30U3hqz8T0vaIWmjpOl5shJ6OiQ9Jmm7pM+l8s6MbhWDkvZJ+l6UD0p6Ir4vbkZHzH8+oef8KFssaaekrZJKhZorsKVD0hZJ2+L16WjEFkl3SxqStDYlvzAGONsu6aI8WVnq6Fkf2xtO6OmV9Gzs+y0t0NEn6cnYXi2I22xJA/F71dUiW34edeyUNNKkLbMl7ZY0KunkVF5LxwY4foGvqnoRQku8CRjOyDsFGCIcVFwGfDVLVlLPLcCngCnANuDUnHJrga6YHgRObsCWujpybNwKdACXAT9sVg8wFTgrpj8PrCxrC3AxsCGmfwRcmsj7BfBW4GzgkTxZyf7X0/OO+H4e8FBM99bGpEU6+oBzU+XXAVcApwODrdCTKNMNrB6rLYlxnZk1jq0cm9qr7VY0ZnbQzA7nZL8L2GtmbxACbC3KkZXhcqDfzP4LPAvMzSl3FWEwIRwu7I93poznuTasY1ZcaayXNFXSNOBVM3vZzJ4Eyj6bNlePmY3aUefWZJCyMrYkA5mlr+0sM3vJzP4MzKgjK93/LD1mVnv+zOuJvgPcJalf0oJmdQAG/DSuCt8eZRcRHINfAV6urQSb1FOjG3g48blRW2rjejAnu5VjA7ThT6cCsgJrjTXYVmE9SQuBPXESA/i4mXUCjwK3tUDHlWZ2FXCAEKNnZqI8hBVKGcrYcnrUcV8UlbGlXrsnZaSzZGUoM4ZrCKsMgHVmdglwM/D9Fuj4ipldAdwFfDfKplhcCtTpU6N6iD9z5pnZ7iZsKaKVYwNMYF8n1QmoVadaOrDWoRxZoZ5EvdGsepFj7jwW4+4QlqHLm9WRau/LwD0JWyDlnjFWPZIE3AusMrND9WxJUe/aHslIZ8nKUDSGXwKeM7PhZN/N7AWVf6Jlro5Ee8OS7ozi5Oop7/vRsC3ABzi6Qh6rLUW0cmyACTzRmNlfgc4Gq/2eEO9mCtAF7MqRFeqJG2/XSHoAWABkPdl+CbA6UWe6mf0beB/wYjM6JJ1KOJ5wuNaemf1H0mlx9XE+8FyLbPk2sN3MBopsSbETuAl4gHBt+xJ5/4yb1UcId+48WRly9UhaQtgrWZbuu6Q3U/5voJ6OWntzOTox7JF0ObAHqF2rpvREukncLMZoSxGtHJtAo5s6J/oLWEj4bXsovk8FrgM+FPM/C+wANgEz8mQl9EwnPKlhB7A8ypJ65gIPpuo8Tdh4/iVwdjM6CA6luwmbt48AHTG/i/Bl3Qq8rVlbgNnAa4S76CBwcyO2EDbDh4AfAGcSVkUQ9jCGge3AgjxZA+Oep+d54KnY9/VRtj7q2Alc3QIdj8V+DwEXRtlbgIGoY0mLbBHwa+CkRNmx2nIK4e/jILAFuLqqsTEzP7DnOE71TLbNYMdxjgM+0TiOUzk+0TiOUzk+0TiOUzk+0TiOUzk+0bQRkjolvSHpjPj5Ukkm6ZwSdXslddZpd3VWXqrcrZJWxHSXpJacVJW0QNKNMb0iIR9T+9GeA5KWFZcGBWfSTCddpxw+0bQfI8D1Md1NOO8yXtwN9ES/ntuBb7WiUTMbMbOfxI8rEvKVTTT7MzO7v6T+Dzehx8EnmnZkALgmpi8AfgsgaUa8M2+TtC7KZimEk9hMOBGMpOWSemL6/1Y5knpiCIMhSRcn88xslHDIbADYaGb/SNXtjSEV+iXdW6df3ZJ+pRBmYWltRaXw3PV5MSTCvNoqQ9L8GL5gl6TPRFmfpLUxtME3610wSSOx/N6oe5OkZ1QyzIZTjE807cdrwKikRcC+hPwLwP0WnDCnSboM6AHuMbOlwGlFDcdj7h8leKRfT1i1pBkkhDp4MKeZF8ysCzgc+5jVr48BN5jZYuDxWkUz20DwtO80s72JNu8gPB7k/cBKhUfxQAjPcCWwtMC0s4AvEo7+fwP4CME58oaCek5JfKJpTzYDP+bYUALvJLgsQPg5dS4wh3CknURe8qh42ktvDjCf4N7wMMHTOM0dwCrg1py+1fSNxD5k9Ws1cJukvvi5iJlm9icLzxfaD5wR5b+J768W1P9DXI39BdhnZkdiuqzHtVOATzTtyWbgGYKPT40/ApfE9EKCI+R+wsQB8N74/i/CHR5gXqrd/cBTcUXRCVybzJQ0n+BzdScwU9IFGX2bn3h/MadfB8yshxAgOx01Lstn5pCkc+JKZg7wtzpls7CcdMvcoSc7PtG0IWb2ipndaMc6sm0APilpCDhsZrsIYSVukvQ4UAsWtgW4TtKjGe3+HdgU91O2Al9LFVkDfD2mVwHfyejeeyRtAaaZ2c6cfvVKeoIQXyW9YfuSpIckvTshu50QJ2eYEFWw8MmJzvjiTpXOuCGplxB+tL+obMX9WET4abmmzH+eJG0kRC78ROWda1MmbDwaxxkrcdXUSNhL//d2k/iKxnGcyvE9GsdxKscnGsdxKscnGsdxKscnGsdxKscnGsdxKscnGsdxKud/N4iWjRaZSzsAAAAASUVORK5CYII=\n", + "text/plain": [ + "
              " + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "import matplotlib.pyplot as plt\n", + "plt.rcParams['font.family'] = 'sans-serif'\n", + "plt.rcParams['font.sans-serif'] = ['Helvetica']\n", + "plt.rcParams['axes.linewidth'] = 0.2 #set the value globally\n", + "\n", + "fig = plt.figure()\n", + "fig.set_size_inches(4, 2.5)\n", + "ax = fig.add_axes((0.15,0.15,0.78,0.75))\n", + "#plt.rc('font', family='sans-serif')\n", + "plt.rc('xtick',labelsize=8)\n", + "plt.rc('ytick',labelsize=8)\n", + "plt.rc('axes',labelsize=8)\n", + "plt.plot(np.linspace(-1,1,unsh_back.__len__()),(cm30_back - unsh_back)/unsh_back*100, label = '30cm gap',color = 'black') #steelblue\n", + "plt.plot(np.linspace(-1,1,unsh_back.__len__()),(cm20_back - unsh_back)/unsh_back*100, label = '20cm gap',color = 'steelblue', linestyle = '--') #steelblue\n", + "plt.plot(np.linspace(-1,1,unsh_back.__len__()),(cm10_back - unsh_back)/unsh_back*100, label = '10cm gap',color = 'darkorange') #steelblue\n", + "#plt.ylabel('$G_{rear}$ vs unshaded [Wm-2]')#(r'$BG_E$ [%]')\n", + "plt.ylabel('$G_{rear}$ / $G_{rear,tubeless}$ -1 [%]')\n", + "plt.xlabel('Module X position [m]')\n", + "plt.legend(fontsize = 8,frameon = False,loc='best')\n", + "#plt.ylim([0, 15])\n", + "plt.title('Torque tube shading loss',fontsize=9)\n", + "#plt.annotate('South',xy=(-10,9.5),fontsize = 8); plt.annotate('North',xy=(8,9.5),fontsize = 8)\n", + "plt.show()" + ] + } + ], + "metadata": { + "anaconda-cloud": {}, + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.3" + } + }, + "nbformat": 4, + "nbformat_minor": 1 +} diff --git a/docs/tutorials/9 - Advanced topics - 1 axis torque tube Shading for 1 day (Research documentation).py b/docs/tutorials/9 - Advanced topics - 1 axis torque tube Shading for 1 day (Research documentation).py new file mode 100644 index 00000000..14f03e35 --- /dev/null +++ b/docs/tutorials/9 - Advanced topics - 1 axis torque tube Shading for 1 day (Research documentation).py @@ -0,0 +1,245 @@ +#!/usr/bin/env python +# coding: utf-8 + +# # 9 - Advanced topics - 1 axis torque tube Shading for 1 day (Research Documentation) +# ## Recreating JPV 2019 / PVSC 2018 Fig. 13 +# +# +# Calculating and plotting shading from torque tube on 1-axis tracking for 1 day, which is figure 13 in: +# +# Ayala Pelaez S, Deline C, Greenberg P, Stein JS, Kostuk RK. Model and validation of single-axis tracking with bifacial PV. IEEE J Photovoltaics. 2019;9(3):715–21. https://ieeexplore.ieee.org/document/8644027 and https://www.nrel.gov/docs/fy19osti/72039.pdf (pre-print, conference version) +# +# +# This is what we will re-create: +# ![Ayala JPV-2](../images_wiki/JPV_Ayala_Fig13.png) +# +# Use bifacial_radiance minimum v. 0.3.1 or higher. Many things have been updated since this paper, simplifying the generation of this plot: +# +#
                +#
              • Sensor position is now always generated E to W on N-S tracking systems, so same sensor positions can just be added for this calculation at the end without needing to flip the sensors.
              • +#
              • Torquetubes get automatically generated in makeModule. Following PVSC 2018 paper, rotation is around the modules and not around the torque tube axis (which is a new feature)
              • +#
              • Simulating only 1 day on single-axis tracking easier with cumulativesky = False and gendaylit1axis(startdate='06/24', enddate='06/24'
              • +#
              • Sensors get generated very close to surface, so all results are from the module surface and not the torquetube for this 1-UP case.
              • +#
              +# +# ## Steps: +# +#
                +#
              1. Running the simulations for all the cases:
              2. +#
                  +#
                1. Baseline Case: No Torque Tube
                2. +#
                3. Zgap = 0.1
                4. +#
                5. Zgap = 0.2
                6. +#
                7. Zgap = 0.3
                8. +#
                +#
              3. Read-back the values and tabulate average values for unshaded, 10cm gap and 30cm gap
              4. +#
              5. Plot spatial loss values for 10cm and 30cm data
              6. +#
              +# +# + +# + +# ### 1. Running the simulations for all the cases + +# In[1]: + + +import os +testfolder = os.path.abspath(r'..\bifacial_radiance\TEMP') + +# You can alternatively point to an empty directory (it will open a load GUI Visual Interface) +# or specify any other directory in your computer. I.E.: +# testfolder = r'C:\Users\sayala\Documents\RadianceScenes\Demo' + +print ("Your simulation will be stored in %s" % testfolder) + + +# In[2]: + + +# VARIABLES of the simulation: +lat = 35.1 # ABQ +lon = -106.7 # ABQ +x=1 +y = 2 +numpanels=1 +limit_angle = 45 # tracker rotation limit angle +albedo = 'concrete' # ground albedo +hub_height = y*0.75 # H = 0.75 +gcr = 0.35 +pitch = y/gcr +#pitch = 1.0/gcr # Check from 1Axis_Shading_PVSC2018 file +cumulativesky = False # needed for set1axis and makeScene1axis so simulation is done hourly not with gencumsky. +limit_angle = 45 # tracker rotation limit angle +nMods=10 +nRows=3 +sensorsy = 200 +module_type='2m_panel' +datewanted='06/24' # sunny day 6/24/1972 (index 4180 - 4195) + +## Torque tube info +torquetube = False # redefined on each simulation below, since we need to compare with and without torque tube. +tubetype='round' +material = 'Metal_Grey' +diameter = 0.1 +axisofrotationTorqueTube = False # Original PVSC version rotated around the modules like most other software. +# Variables that will get defined on each iteration below: +zgap = 0 # 0.2, 0.3 values tested. Re-defined on each simulation. +torquetube = False # baseline is no torque tube. + + +# In[3]: + + +# Simulation Start. + +try: + import bifacial_radiance +except ImportError: + raise RuntimeError('bifacial_radiance is required. download distribution') +import numpy as np + +print(bifacial_radiance.__version__) + +demo = bifacial_radiance.RadianceObj(path = testfolder) +demo.setGround(albedo) +epwfile = demo.getEPW(lat, lon) +metdata = demo.readEPW(epwfile) +trackerdict = demo.set1axis(metdata, limit_angle = limit_angle, backtrack = True, gcr = gcr, cumulativesky = False) +trackerdict = demo.gendaylit1axis(startdate=datewanted, enddate=datewanted) +sceneDict = {'pitch':pitch,'hub_height':hub_height, 'nMods': nMods, 'nRows': nRows} + + +# + +# ### A. Baseline Case: No Torque Tube +# +# When torquetube is False, zgap is the distance from axis of torque tube to module surface, but since we are rotating from the module's axis, this Zgap doesn't matter for this baseline case. + +# In[6]: + + +#CASE 0 No torque tube +# When torquetube is False, zgap is the distance from axis of torque tube to module surface, but since we are rotating from the module's axis, this Zgap doesn't matter. +# zgap = 0.1 + diameter/2.0 +torquetube = False +customname = '_NoTT' +demo.makeModule(name=module_type,x=x,y=y, numpanels=numpanels, torquetube=torquetube, axisofrotationTorqueTube=axisofrotationTorqueTube) +trackerdict = demo.makeScene1axis(trackerdict,module_type,sceneDict, cumulativesky = cumulativesky) +trackerdict = demo.makeOct1axis(trackerdict) +trackerdict = demo.analysis1axis(trackerdict, sensorsy = sensorsy, customname = customname) + + +# + +# ### B. ZGAP = 0.1 + +# In[7]: + + +#ZGAP 0.1 +zgap = 0.1 +torquetube = True +customname = '_zgap0.1' +demo.makeModule(name=module_type,x=x,y=y, numpanels=numpanels,tubetype=tubetype, zgap=zgap, torquetube=torquetube, diameter=diameter, material=material, axisofrotationTorqueTube=axisofrotationTorqueTube) +trackerdict = demo.makeScene1axis(trackerdict,module_type,sceneDict, cumulativesky = cumulativesky) +trackerdict = demo.makeOct1axis(trackerdict) +trackerdict = demo.analysis1axis(trackerdict, sensorsy = sensorsy, customname = customname) + + +# + +# ### C. ZGAP = 0.2 + +# In[8]: + + +#ZGAP 0.2 +zgap = 0.2 +torquetube = True +customname = '_zgap0.2' +demo.makeModule(name=module_type,x=x,y=y, numpanels=numpanels,tubetype=tubetype, zgap=zgap, torquetube=torquetube, diameter=diameter, material=material, axisofrotationTorqueTube=axisofrotationTorqueTube) +trackerdict = demo.makeScene1axis(trackerdict,module_type,sceneDict, cumulativesky = cumulativesky) +trackerdict = demo.makeOct1axis(trackerdict) +trackerdict = demo.analysis1axis(trackerdict, sensorsy = sensorsy, customname = customname) + + +# + +# ### D. ZGAP = 0.3 + +# In[9]: + + +#ZGAP 0.3 +zgap = 0.3 +torquetube = True +customname = '_zgap0.3' +demo.makeModule(name=module_type,x=x,y=y, numpanels=numpanels,tubetype=tubetype, zgap=zgap, torquetube=torquetube, diameter=diameter, material=material, axisofrotationTorqueTube=axisofrotationTorqueTube) +trackerdict = demo.makeScene1axis(trackerdict,module_type,sceneDict, cumulativesky = cumulativesky) +trackerdict = demo.makeOct1axis(trackerdict) +trackerdict = demo.analysis1axis(trackerdict, sensorsy = sensorsy, customname = customname) + + +# + +# ### 2. Read-back the values and tabulate average values for unshaded, 10cm gap and 30cm gap +# + +# In[7]: + + +import glob +import pandas as pd + +resultsfolder = os.path.join(testfolder, 'results') +print (resultsfolder) +filenames = glob.glob(os.path.join(resultsfolder,'*.csv')) +noTTlist = [k for k in filenames if 'NoTT' in k] +zgap10cmlist = [k for k in filenames if 'zgap0.1' in k] +zgap20cmlist = [k for k in filenames if 'zgap0.2' in k] +zgap30cmlist = [k for k in filenames if 'zgap0.3' in k] + +# sum across all hours for each case +unsh_front = np.array([pd.read_csv(f, engine='python')['Wm2Front'] for f in noTTlist]).sum(axis = 0) +cm10_front = np.array([pd.read_csv(f, engine='python')['Wm2Front'] for f in zgap10cmlist]).sum(axis = 0) +cm20_front = np.array([pd.read_csv(f, engine='python')['Wm2Front'] for f in zgap20cmlist]).sum(axis = 0) +cm30_front = np.array([pd.read_csv(f, engine='python')['Wm2Front'] for f in zgap30cmlist]).sum(axis = 0) +unsh_back = np.array([pd.read_csv(f, engine='python')['Wm2Back'] for f in noTTlist]).sum(axis = 0) +cm10_back = np.array([pd.read_csv(f, engine='python')['Wm2Back'] for f in zgap10cmlist]).sum(axis = 0) +cm20_back = np.array([pd.read_csv(f, engine='python')['Wm2Back'] for f in zgap20cmlist]).sum(axis = 0) +cm30_back = np.array([pd.read_csv(f, engine='python')['Wm2Back'] for f in zgap30cmlist]).sum(axis = 0) + + +# + +# ### 3. plot spatial loss values for 10cm and 30cm data + +# In[5]: + + +import matplotlib.pyplot as plt +plt.rcParams['font.family'] = 'sans-serif' +plt.rcParams['font.sans-serif'] = ['Helvetica'] +plt.rcParams['axes.linewidth'] = 0.2 #set the value globally + +fig = plt.figure() +fig.set_size_inches(4, 2.5) +ax = fig.add_axes((0.15,0.15,0.78,0.75)) +#plt.rc('font', family='sans-serif') +plt.rc('xtick',labelsize=8) +plt.rc('ytick',labelsize=8) +plt.rc('axes',labelsize=8) +plt.plot(np.linspace(-1,1,unsh_back.__len__()),(cm30_back - unsh_back)/unsh_back*100, label = '30cm gap',color = 'black') #steelblue +plt.plot(np.linspace(-1,1,unsh_back.__len__()),(cm20_back - unsh_back)/unsh_back*100, label = '20cm gap',color = 'steelblue', linestyle = '--') #steelblue +plt.plot(np.linspace(-1,1,unsh_back.__len__()),(cm10_back - unsh_back)/unsh_back*100, label = '10cm gap',color = 'darkorange') #steelblue +#plt.ylabel('$G_{rear}$ vs unshaded [Wm-2]')#(r'$BG_E$ [%]') +plt.ylabel('$G_{rear}$ / $G_{rear,tubeless}$ -1 [%]') +plt.xlabel('Module X position [m]') +plt.legend(fontsize = 8,frameon = False,loc='best') +#plt.ylim([0, 15]) +plt.title('Torque tube shading loss',fontsize=9) +#plt.annotate('South',xy=(-10,9.5),fontsize = 8); plt.annotate('North',xy=(8,9.5),fontsize = 8) +plt.show() + diff --git a/docs/v3 - bifacial_radiance examples.ipynb b/docs/v3 - bifacial_radiance examples.ipynb deleted file mode 100644 index 48de9001..00000000 --- a/docs/v3 - bifacial_radiance examples.ipynb +++ /dev/null @@ -1,338 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## bifacial_radiance examples\n", - "what to do with the new bifacial_radiance python module\n", - "\n", - "\n", - "#### Prerequisites (Step 0):\n", - "This software requires the previous installation of RADIANCE from https://github.com/NREL/Radiance/releases.\n", - "\n", - "Make sure you add radiance to the system PATH so Python can interact with the radiance program\n", - "\n", - "If you are on a PC you should also copy the Jaloxa radwinexe-5.0.a.8-win64.zip executables into `program files/radiance/bin`: http://www.jaloxa.eu/resources/radiance/radwinexe.shtml\n", - "\n", - "#### STEP 1: Install and import bifacial_radiance\n", - "\n", - " - clone the bifacial_radiance repo to your local directory\n", - " - navigate to the \\bifacial_radiance directory which contains setup\n", - " - run `pip install -e . ` ( the period . is required, the -e flag is optional and installs in development mode where changes to the bifacial_radiance.py files are immediately incorporated into the module if you re-start the python kernel)\n", - "\n", - "#### STEP 2: Move gencumulativesky.exe\n", - "Copy gencumulativesky.exe from the repo's `/bifacial_radiance/data/` directory and copy into your Radiance install directory.\n", - "This is typically found in `/program files/radiance/bin/`. \n", - "\n", - "#### STEP 3: Create a local Radiance directory for storing the scene files created\n", - "Keep scene geometry files separate from the bifacial_radiance directory. Create a local directory somewhere that will be referenced in the next step.\n", - "\n", - "#### STEP 4: Reboot the computer\n", - "This makes sure the PATH is updated" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Simple Fixed-Tilt Example\n", - "\n", - "### Set a Folder \n", - "\n", - "First let's set the folder where the simulation will be saved. By default, this is the TEMP folder in the bifacial_radiance distribution.\n", - "\n", - "The lines below find the location of the folder relative to this Jupyter Journal.\n" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Your simulation will be stored in C:\\Users\\sayala\\Documents\\GitHub\\bifacial_radiance\\bifacial_radiance\\TEMP\n" - ] - } - ], - "source": [ - "import os\n", - "testfolder = os.path.abspath(r'..\\bifacial_radiance\\TEMP') \n", - "\n", - "# You can alternatively point to an empty directory (it will open a load GUI Visual Interface)\n", - "# or specify any other directory in your computer. I.E.:\n", - "# testfolder = r'C:\\Users\\sayala\\Documents\\RadianceScenes\\Demo'\n", - "\n", - "print (\"Your simulation will be stored in %s\" % testfolder)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "try:\n", - " from bifacial_radiance import *\n", - "except ImportError:\n", - " raise RuntimeError('bifacial_radiance is required. download distribution')\n", - "\n", - "import numpy as np" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Create a Radiance Object, Set the Albedo and Generate the Sky" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "path = C:\\Users\\sayala\\Documents\\GitHub\\bifacial_radiance\\bifacial_radiance\\TEMP\n", - "Getting weather file: USA_VA_Richmond.Intl.AP.724010_TMY.epw\n", - " ... OK!\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "c:\\users\\sayala\\documents\\github\\bifacial_radiance\\bifacial_radiance\\main.py:2414: pvlibDeprecationWarning: The get_sun_rise_set_transit function was deprecated in pvlib 0.6.1 and will be removed in 0.7. Use sun_rise_set_transit_spa instead.\n", - " sunup= pvlib.irradiance.solarposition.get_sun_rise_set_transit(datetimetz, lat, lon) #only for pvlib <0.6.1\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "message: There were 4255 sun up hours in this climate file\r\n", - "Total Ibh/Lbh: 0.000000\n" - ] - } - ], - "source": [ - "# Simple example system using Radiance. We'll simulate a 1-up landscape system over a white rooftop\n", - "demo = RadianceObj('bifacial_example',testfolder) # Create a RadianceObj 'object' named bifacial_example. no whitespace allowed\n", - "\n", - "demo.setGround(0.62) # input albedo number or material name like 'concrete'. \n", - "# To see options, run this without any input.\n", - "\n", - "# Pull in meteorological data using pyEPW for any global lat/lon\n", - "epwfile = demo.getEPW(lat = 37.5, lon = -77.6) \n", - "\n", - "# Read in the weather data pulled in above. \n", - "# If you want a different location, replace this filename with the new EPW file name in `epwfile`. \n", - "metdata = demo.readEPW('EPWs\\\\USA_VA_Richmond.Intl.AP.724010_TMY.epw') \n", - "\n", - "# Solar resource definition. Either choose a single time point with gendaylit, or use cumulativesky \n", - "# for the entire year. \n", - "fullYear = True\n", - "if fullYear:\n", - " demo.genCumSky(demo.epwfile) # entire year.\n", - "else:\n", - " demo.gendaylit(metdata,4020) # Noon, June 17th (timepoint # 4020)\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### DEFINE a Module type\n", - "\n", - "You can create a custom PV module type. In this case we are defining a module named \"Prism Solar Bi60\", in landscape. The x value defines the size of the module along the row, so for landscape modules x > y. y = 0.984 x = 1.695. Bifaciality = 0.90\n", - "\n", - "###### Note:\n", - "Modules are currently 100% opaque. For drawing each cell, makeModule needs more inputs with cellLevelModule = True. You can also specify a lot more variables in makeModule like multiple modules, torque tubes, spacing between modules, etc, so read the function definition." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "Module Name: Prism_Solar_Bi60_landscape\n", - "Module file did not exist before, creating new module file\n", - "Module Prism Solar Bi60 landscape successfully created\n", - "\n", - "\n", - " AVAILABLE MODULES:\n", - "Usage: SceneObj(moduletype)\n", - "No module type selected. Available module types: dict_keys(['Prism Solar Bi60', '2upTracker', 'test', 'Prism Solar Bi60 landscape', 'cellModule', 'PrismSolar'])\n", - "Available module names: ['Prism Solar Bi60', '2upTracker', 'test', 'Prism Solar Bi60 landscape', 'cellModule', 'PrismSolar']\n" - ] - } - ], - "source": [ - "\n", - "module_type = 'Prism Solar Bi60 landscape' \n", - "demo.makeModule(name=module_type,x=1.695, y=0.984, bifi = 0.90)\n", - "\n", - "# print available module types in data/module.json\n", - "print(\"\\n\\n AVAILABLE MODULES:\")\n", - "availableModules = demo.printModules()\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### MAKE the Scene:\n", - "Create a scene uses the module created above and replicates it by the number of modules and number of rows specified in the scene Dictionary. The sceneDicitonary also specifies the azimuth, tilt, clearance_height (distance between the ground and lowest point of the module) and any other parameter. Azimuth gets measured from N = 0, so for South facing modules azimuth should equal 180.\n", - "\n", - "makeScene creates a .rad file with the parameters specified in sceneDict. \n" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [], - "source": [ - "sceneDict = {'tilt':10,'pitch':3,'clearance_height':0.2,'azimuth':180, 'nMods': 20, 'nRows': 7} \n", - "\n", - "scene = demo.makeScene(module_type,sceneDict)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### COMBINE the Ground, Sky, and the Scene Objects\n", - "makeOct combines all of the ground, sky and object files into a .oct file.\n" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Created bifacial_example.oct\n" - ] - } - ], - "source": [ - "octfile = demo.makeOct(demo.getfilelist()) " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### ANALYZE and get Results\n", - "Once the octfile tying the scene, ground and sky has been created, we create an analysis object. We have to specify where the sensors will be located with moduleAnalysis. If no parameters are passed to moduleAnalysis, it will scan the center module of the center row.\n", - "\n", - "The frontscan and backscan include a linescan along a chord of the module, both on the front and back. \n", - "\n", - "![Simple example for south facing module](images_wiki/frontscan_backscan.png)\n", - "Analysis saves the measured irradiances in the front and in the back on the results folder. Prints out the ratio of the average of the rear and front irradiance values along a chord of the module." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Linescan in process: bifacial_example_Front\n", - "Linescan in process: bifacial_example_Back\n", - "Saved: results\\irr_bifacial_example.csv\n", - "Annual bifacial ratio: 0.169 \n" - ] - } - ], - "source": [ - "analysis = AnalysisObj(octfile, demo.basename)\n", - "frontscan, backscan = analysis.moduleAnalysis(scene)\n", - "analysis.analysis(octfile, demo.basename, frontscan, backscan) \n", - "print('Annual bifacial ratio: %0.3f ' %( np.mean(analysis.Wm2Back) / np.mean(analysis.Wm2Front)) )\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### View / Render the Scene\n", - "\n", - "If you used gencumsky or gendaylit, you can view the scene by navigating on a command line to the folder and typing:\n", - "\n", - "##### objview materials\\ground.rad objects\\Prism_Solar_Bi60_landscape_0.2_3_10_20x7.rad \n", - "\n", - "This objview has 3 different light sources of its own, so the shading is not representative.\n", - "\n", - "If you used gendaylit (only), you can view the scene correctly illuminated with the sky you generated after generating the oct file, with \n", - "\n", - "##### rvu -vf views\\front.vp -e .01 bifacial_example.oct\n", - "\n", - "Or you can also use the code below from bifacial_radiance to generate an HDR rendered image of the scene:" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Generating visible render of scene\n", - "Generating scene in WM-2. This may take some time.\n", - "Saving scene in false color\n" - ] - } - ], - "source": [ - "# Make a color render and falsecolor image of the scene. \n", - "# Files are saved as .hdr (high definition render) files. Try LuminanceHDR viewer (free) to view them\n", - "analysis.makeImage('side.vp')\n", - "analysis.makeFalseColor('side.vp')\n", - "# Note - if you want to have an interactive image viewer, use the `rvu` viewer - manual page here: http://radsite.lbl.gov/radiance/rvu.1.html" - ] - } - ], - "metadata": { - "anaconda-cloud": {}, - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.3" - } - }, - "nbformat": 4, - "nbformat_minor": 1 -} diff --git a/docs/v3_1Axis_tracking_example.ipynb b/docs/v3_1Axis_tracking_example.ipynb deleted file mode 100644 index ab50d30e..00000000 --- a/docs/v3_1Axis_tracking_example.ipynb +++ /dev/null @@ -1,915 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 1-Axis tracker example\n", - "\n", - "Example demonstrating Radiance gencumulativesky for 1-axis tracking.\n", - "\n", - "#### This assumes bifacial_radiance has been installed properly. \n", - "\n", - "See bifacial_radiance wiki, bifacial_radiance main page, or the jupyter journal with bifacial_radiance examples on proper installation.\n", - "\n", - "#### Types of 1-axis tracking simulations:\n", - "\n", - " CumulativeSky: True gencumsky has been modified to divide the yearly-cumulative sky into various skies, each one representing the cumulative irradiance for the hours at which the tracker is at a certain angle. For faster running, for a tracker that moves between 60 and -60 degrees limit angle, if only positions every 5 degrees are considered (60, 55, 50 ... -55, -60), then only 25 skies (and 25 simulations) will be run for the whole year.\n", - "\n", - "This procedure was presented in. Reffer to this journal for more information:\n", - "\n", - " S. Ayala Pelaez, C. Deline, P. Greenberg, J. S. Stein, and R. K. Kostuk, “Model and Validation of Single-Axis Tracking with Bifacial PV - Preprint,” Golden Co Natl. Renew. Energy Lab. NREL/CP-5K00-72039., no. October, 2018. https://www.nrel.gov/docs/fy19osti/72039.pdf\n", - "\n", - "CumulativeSky: False . This uses Gendaylit function, which performs the simulation hour by hour. A good computer and a ton of patience are needed for doing the ~4000 daylight-hours of the year, or else a high-performance-computing for handling full year simulations. The procedure can be broken into shorter steps for one day or a single timestamp simulation which is exemplified below.\n", - "\n", - "The first part is common to both procedures. We show a couple tricks of loading files / or weather files here too.\n", - "\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Your simulation will be stored in C:\\Users\\sayala\\Documents\\GitHub\\bifacial_radiance\\bifacial_radiance\\TEMP\n" - ] - } - ], - "source": [ - "import os\n", - "testfolder = os.path.abspath(r'..\\bifacial_radiance\\TEMP') \n", - "\n", - "# You can alternatively point to an empty directory (it will open a load GUI Visual Interface)\n", - "# or specify any other directory in your computer. I.E.:\n", - "# testfolder = r'C:\\Users\\sayala\\Documents\\RadianceScenes\\Demo'\n", - "\n", - "print (\"Your simulation will be stored in %s\" % testfolder)" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "#tracker geometry options:\n", - "module_height = 1.7 # module portrait dimension in meters\n", - "gcr = 0.33 # ground cover ratio, = module_height / pitch\n", - "albedo = 0.3 # ground albedo\n", - "hub_height = 2 # tracker height at 0 tilt in meters (hub height)\n", - "limit_angle = 45 # tracker rotation limit angle" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "path = C:\\Users\\sayala\\Documents\\GitHub\\bifacial_radiance\\bifacial_radiance\\TEMP\n" - ] - } - ], - "source": [ - "try:\n", - " from bifacial_radiance import RadianceObj, AnalysisObj\n", - "except ImportError:\n", - " raise RuntimeError('bifacial_radiance is required. download distribution')\n", - " # Simple example system using Radiance.\n", - "import numpy as np\n", - "\n", - "# Easy graphical director picker: \n", - "# this is only required if you want a graphical directory picker. \n", - "# Note: easygui sometimes opens in the background forcing you to hunt for the window! \n", - "#import easygui \n", - "#testfolder = easygui.diropenbox(msg = 'Select or create an empty directory for the Radiance tree',title='Browse for empty Radiance directory')\n", - "\n", - "demo = RadianceObj(path = testfolder) # Create a RadianceObj 'object'\n", - "demo.setGround(albedo) # input albedo number or material name like 'concrete'. To see options, run this without any input.\n", - "\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Look at a couple of ways to get meteorological data" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": { - "scrolled": false - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Getting weather file: USA_VA_Richmond.Intl.AP.724010_TMY.epw\n", - " ... OK!\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "c:\\users\\sayala\\documents\\github\\bifacial_radiance\\bifacial_radiance\\main.py:2449: pvlibDeprecationWarning: The get_sun_rise_set_transit function was deprecated in pvlib 0.6.1 and will be removed in 0.7. Use sun_rise_set_transit_spa instead.\n", - " sunup= pvlib.irradiance.solarposition.get_sun_rise_set_transit(datetimetz, lat, lon) #only for pvlib <0.6.1\n", - "c:\\users\\sayala\\documents\\github\\bifacial_radiance\\bifacial_radiance\\main.py:2449: pvlibDeprecationWarning: The get_sun_rise_set_transit function was deprecated in pvlib 0.6.1 and will be removed in 0.7. Use sun_rise_set_transit_spa instead.\n", - " sunup= pvlib.irradiance.solarposition.get_sun_rise_set_transit(datetimetz, lat, lon) #only for pvlib <0.6.1\n" - ] - } - ], - "source": [ - "EPWmode = True\n", - "if EPWmode is True:\n", - " epwfile = demo.getEPW(37.5,-77.6) #Pull EPW data for any global lat/lon. In this case, Richmond, VA\n", - " metdata = demo.readEPW(epwfile) # read in the weather data\n", - " #metdata = demo.readEPW('EPWs\\\\USA_VA_Richmond.Intl.AP.724010_TMY.epw') # read in the weather data directly\n", - "else:\n", - " metdata = demo.readTMY() # load TMY3 data from another source, like solar prospector. A version is saved as \\EPWs\\tmy3_temp.csv\n", - "\n", - "# Alternatively, you can use readWeatherFile, which doens't care if the file is an EPW or a CSV.\n", - "metdata = demo.readWeatherFile(epwfile)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## CumulativeSky Workflow" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": { - "scrolled": true - }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "C:\\ProgramData\\Anaconda3\\lib\\site-packages\\pvlib\\tracking.py:428: RuntimeWarning: invalid value encountered in arccos\n", - " wc = np.degrees(np.arccos(temp))\n", - "C:\\ProgramData\\Anaconda3\\lib\\site-packages\\pvlib\\tracking.py:431: RuntimeWarning: invalid value encountered in less\n", - " tracker_theta = np.where(wid < 0, wid + wc, wid - wc)\n", - "C:\\ProgramData\\Anaconda3\\lib\\site-packages\\pvlib\\tracking.py:435: RuntimeWarning: invalid value encountered in greater\n", - " tracker_theta[tracker_theta > max_angle] = max_angle\n", - "C:\\ProgramData\\Anaconda3\\lib\\site-packages\\pvlib\\tracking.py:436: RuntimeWarning: invalid value encountered in less\n", - " tracker_theta[tracker_theta < -max_angle] = -max_angle\n", - "C:\\ProgramData\\Anaconda3\\lib\\site-packages\\pvlib\\tracking.py:450: RuntimeWarning: invalid value encountered in arccos\n", - " aoi = np.degrees(np.arccos(np.abs(np.sum(sun_vec*panel_norm, axis=0))))\n", - "C:\\ProgramData\\Anaconda3\\lib\\site-packages\\pvlib\\tracking.py:543: RuntimeWarning: invalid value encountered in less\n", - " surface_azimuth[surface_azimuth < 0] += 360\n", - "C:\\ProgramData\\Anaconda3\\lib\\site-packages\\pvlib\\tracking.py:544: RuntimeWarning: invalid value encountered in greater_equal\n", - " surface_azimuth[surface_azimuth >= 360] -= 360\n", - "C:\\ProgramData\\Anaconda3\\lib\\site-packages\\pvlib\\tracking.py:548: RuntimeWarning: invalid value encountered in arccos\n", - " surface_tilt = 90 - np.degrees(np.arccos(dotproduct))\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Saving file EPWs\\1axis_-5.0.csv, # points: 141\n", - "Saving file EPWs\\1axis_-30.0.csv, # points: 157\n", - "Saving file EPWs\\1axis_-45.0.csv, # points: 815\n", - "Saving file EPWs\\1axis_-20.0.csv, # points: 110\n", - "Saving file EPWs\\1axis_10.0.csv, # points: 81\n", - "Saving file EPWs\\1axis_35.0.csv, # points: 167\n", - "Saving file EPWs\\1axis_45.0.csv, # points: 842\n", - "Saving file EPWs\\1axis_5.0.csv, # points: 244\n", - "Saving file EPWs\\1axis_15.0.csv, # points: 123\n", - "Saving file EPWs\\1axis_30.0.csv, # points: 116\n", - "Saving file EPWs\\1axis_20.0.csv, # points: 225\n", - "Saving file EPWs\\1axis_-35.0.csv, # points: 146\n", - "Saving file EPWs\\1axis_25.0.csv, # points: 154\n", - "Saving file EPWs\\1axis_-10.0.csv, # points: 362\n", - "Saving file EPWs\\1axis_-40.0.csv, # points: 173\n", - "Saving file EPWs\\1axis_0.0.csv, # points: 81\n", - "Saving file EPWs\\1axis_40.0.csv, # points: 135\n", - "Saving file EPWs\\1axis_-15.0.csv, # points: 136\n", - "Saving file EPWs\\1axis_-25.0.csv, # points: 175\n", - "message: There were 70 sun up hours in this climate file\n", - "Total Ibh/Lbh: 0.000000\n", - "Created skyfile skies\\1axis_-5.0.rad\n", - "message: There were 157 sun up hours in this climate file\n", - "Total Ibh/Lbh: 0.000000\n", - "Created skyfile skies\\1axis_-30.0.rad\n", - "message: There were 812 sun up hours in this climate file\n", - "Total Ibh/Lbh: 0.000000\n", - "Created skyfile skies\\1axis_-45.0.rad\n", - "message: There were 108 sun up hours in this climate file\n", - "Total Ibh/Lbh: 0.000000\n", - "Created skyfile skies\\1axis_-20.0.rad\n", - "message: There were 76 sun up hours in this climate file\n", - "Total Ibh/Lbh: 0.000000\n", - "Created skyfile skies\\1axis_10.0.rad\n", - "message: There were 167 sun up hours in this climate file\n", - "Total Ibh/Lbh: 0.000000\n", - "Created skyfile skies\\1axis_35.0.rad\n", - "message: There were 842 sun up hours in this climate file\n", - "Total Ibh/Lbh: 0.000000\n", - "Created skyfile skies\\1axis_45.0.rad\n", - "message: There were 243 sun up hours in this climate file\n", - "Total Ibh/Lbh: 0.000000\n", - "Created skyfile skies\\1axis_5.0.rad\n", - "message: There were 112 sun up hours in this climate file\n", - "Total Ibh/Lbh: 0.000000\n", - "Created skyfile skies\\1axis_15.0.rad\n", - "message: There were 116 sun up hours in this climate file\n", - "Total Ibh/Lbh: 0.000000\n", - "Created skyfile skies\\1axis_30.0.rad\n", - "message: There were 224 sun up hours in this climate file\n", - "Total Ibh/Lbh: 0.000000\n", - "Created skyfile skies\\1axis_20.0.rad\n", - "message: There were 145 sun up hours in this climate file\n", - "Total Ibh/Lbh: 0.000000\n", - "Created skyfile skies\\1axis_-35.0.rad\n", - "message: There were 152 sun up hours in this climate file\n", - "Total Ibh/Lbh: 0.000000\n", - "Created skyfile skies\\1axis_25.0.rad\n", - "message: There were 341 sun up hours in this climate file\n", - "Total Ibh/Lbh: 0.000000\n", - "Created skyfile skies\\1axis_-10.0.rad\n", - "message: There were 172 sun up hours in this climate file\n", - "Total Ibh/Lbh: 0.000000\n", - "Created skyfile skies\\1axis_-40.0.rad\n", - "message: There were 13 sun up hours in this climate file\n", - "Total Ibh/Lbh: 0.000000\n", - "Created skyfile skies\\1axis_0.0.rad\n", - "message: There were 135 sun up hours in this climate file\n", - "Total Ibh/Lbh: 0.000000\n", - "Created skyfile skies\\1axis_40.0.rad\n", - "message: There were 133 sun up hours in this climate file\n", - "Total Ibh/Lbh: 0.000000\n", - "Created skyfile skies\\1axis_-15.0.rad\n", - "message: There were 172 sun up hours in this climate file\n", - "Total Ibh/Lbh: 0.000000\n", - "Created skyfile skies\\1axis_-25.0.rad\n" - ] - } - ], - "source": [ - "# We have 2 workflows: cumulativesky and hourly. Start with cumulativesky\n", - "\n", - "# create metdata files for each condition. It will create a met-data file for each angle the tracker will find itself in.\n", - "# set1axis has as input variable cumulativesky, which is set to True as default.\n", - "trackerdict = demo.set1axis(metdata, limit_angle = limit_angle, backtrack = True, gcr = gcr)\n", - "\n", - "# Create the skies for each sub-metdata file created by set1axis.\n", - "trackerdict = demo.genCumSky1axis(trackerdict)" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "Module Name: Prism_Solar_Bi60\n", - "Module file did not exist before, creating new module file\n", - "Module Prism Solar Bi60 successfully created\n", - "\n", - "Module Name: 2upTracker\n", - "Module file did not exist before, creating new module file\n", - "Module 2upTracker successfully created\n", - "\n", - "Module Name: cellLevelModule\n", - "Module file did not exist before, creating new module file\n", - "Module was shifted by 0.078 in X to avoid sensors on air\n", - "This is a Cell-Level detailed module with Packaging Factor of 0.81 %\n", - "Module cellLevelModule successfully created\n", - "\n", - "Usage: SceneObj(moduletype)\n", - "No module type selected. Available module types: dict_keys(['Prism Solar Bi60', '2upTracker', 'test', 'Prism Solar Bi60 landscape', 'cellModule', 'cellLevelModule'])\n", - "Available module names: ['Prism Solar Bi60', '2upTracker', 'test', 'Prism Solar Bi60 landscape', 'cellModule', 'cellLevelModule']\n" - ] - }, - { - "data": { - "text/plain": [ - "dict_keys(['Prism Solar Bi60', '2upTracker', 'test', 'Prism Solar Bi60 landscape', 'cellModule', 'cellLevelModule'])" - ] - }, - "execution_count": 11, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Create a new moduletype: Prism Solar Bi60. width = .984m height = 1.695m. Bifaciality = 0.90\n", - "demo.makeModule(name='Prism Solar Bi60',x=0.984,y=module_height)\n", - "# note that beginning in v0.2.3 you can add torque tubes and multiple module arrays. e.g:\n", - "demo.makeModule(name='2upTracker',x=0.984,y=module_height, torquetube = True, tubetype = 'round', \n", - " diameter = 0.1, xgap=0.02, ygap = 0.05, zgap = 0.05, numpanels = 2, axisofrotationTorqueTube=True)\n", - "# and now in v0.2.4 you can even add cell-level options if you want non-opaque, cell-defined modules.\n", - "# To do this, pass a dictionary with the Cell Level Module PArameters:\n", - "cellLevelModuleParams = {'numcellsx': 6, 'numcellsy':10, 'xcell': 0.156, 'ycell':0.156, 'xcellgap':0.02, 'ycellgap':0.02}\n", - "\n", - "demo.makeModule(name='cellLevelModule', bifi=1, torquetube=True, diameter=0.1, tubetype='Oct', material='Metal_Grey', \n", - " xgap=0.02, ygap=0.05, zgap=0.05, numpanels=2, \n", - " cellLevelModuleParams=cellLevelModuleParams, \n", - " axisofrotationTorqueTube=False)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "Usage: SceneObj(moduletype)\n", - "No module type selected. Available module types: dict_keys(['Prism Solar Bi60', '2upTracker', 'test', 'Prism Solar Bi60 landscape', 'cellModule', 'cellLevelModule'])\n", - "Available module names: ['Prism Solar Bi60', '2upTracker', 'test', 'Prism Solar Bi60 landscape', 'cellModule', 'cellLevelModule']\n" - ] - }, - { - "data": { - "text/plain": [ - "dict_keys(['Prism Solar Bi60', '2upTracker', 'test', 'Prism Solar Bi60 landscape', 'cellModule', 'cellLevelModule'])" - ] - }, - "execution_count": 12, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# For more options on makemodule, see the help description of the function. \n", - "\n", - "print(\"\")\n", - "demo.printModules()# print available module types" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": { - "scrolled": true - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "Making .rad files for cumulativesky 1-axis workflow\n", - "19 Radfiles created in /objects/\n", - "\n", - "Making 19 octfiles for 1-axis tracking in root directory.\n", - "Created 1axis_-5.0.oct\n", - "Created 1axis_-30.0.oct\n", - "Created 1axis_-45.0.oct\n", - "Created 1axis_-20.0.oct\n", - "Created 1axis_10.0.oct\n", - "Created 1axis_35.0.oct\n", - "Created 1axis_45.0.oct\n", - "Created 1axis_5.0.oct\n", - "Created 1axis_15.0.oct\n", - "Created 1axis_30.0.oct\n", - "Created 1axis_20.0.oct\n", - "Created 1axis_-35.0.oct\n", - "Created 1axis_25.0.oct\n", - "Created 1axis_-10.0.oct\n", - "Created 1axis_-40.0.oct\n", - "Created 1axis_0.0.oct\n", - "Created 1axis_40.0.oct\n", - "Created 1axis_-15.0.oct\n", - "Created 1axis_-25.0.oct\n", - "Linescan in process: 1axis_-45.0_Front\n", - "Linescan in process: 1axis_-45.0_Back\n", - "Saved: results\\irr_1axis_-45.0.csv\n", - "Index: -45.0. Wm2Front: 323669.65555555554. Wm2Back: 47256.54777777778\n", - "Linescan in process: 1axis_-40.0_Front\n", - "Linescan in process: 1axis_-40.0_Back\n", - "Saved: results\\irr_1axis_-40.0.csv\n", - "Index: -40.0. Wm2Front: 78301.63666666666. Wm2Back: 12710.161111111109\n", - "Linescan in process: 1axis_-35.0_Front\n", - "Linescan in process: 1axis_-35.0_Back\n", - "Saved: results\\irr_1axis_-35.0.csv\n", - "Index: -35.0. Wm2Front: 62560.62222222222. Wm2Back: 10102.738666666668\n", - "Linescan in process: 1axis_-30.0_Front\n", - "Linescan in process: 1axis_-30.0_Back\n", - "Saved: results\\irr_1axis_-30.0.csv\n", - "Index: -30.0. Wm2Front: 55811.205555555556. Wm2Back: 9679.104555555554\n", - "Linescan in process: 1axis_-25.0_Front\n", - "Linescan in process: 1axis_-25.0_Back\n", - "Saved: results\\irr_1axis_-25.0.csv\n", - "Index: -25.0. Wm2Front: 95469.41666666667. Wm2Back: 16476.726666666666\n", - "Linescan in process: 1axis_-20.0_Front\n", - "Linescan in process: 1axis_-20.0_Back\n", - "Saved: results\\irr_1axis_-20.0.csv\n", - "Index: -20.0. Wm2Front: 31675.578888888893. Wm2Back: 5833.690444444444\n", - "Linescan in process: 1axis_-15.0_Front\n", - "Linescan in process: 1axis_-15.0_Back\n", - "Saved: results\\irr_1axis_-15.0.csv\n", - "Index: -15.0. Wm2Front: 28592.701111111113. Wm2Back: 5413.696999999999\n", - "Linescan in process: 1axis_-10.0_Front\n", - "Linescan in process: 1axis_-10.0_Back\n", - "Saved: results\\irr_1axis_-10.0.csv\n", - "Index: -10.0. Wm2Front: 143014.67777777778. Wm2Back: 26814.435555555552\n", - "Linescan in process: 1axis_-5.0_Front\n", - "Linescan in process: 1axis_-5.0_Back\n", - "Saved: results\\irr_1axis_-5.0.csv\n", - "Index: -5.0. Wm2Front: 1533.7563333333333. Wm2Back: 305.8525\n", - "Linescan in process: 1axis_0.0_Front\n", - "Linescan in process: 1axis_0.0_Back\n", - "Saved: results\\irr_1axis_0.0.csv\n", - "Index: 0.0. Wm2Front: 5100.040888888889. Wm2Back: 935.8778555555554\n", - "Linescan in process: 1axis_5.0_Front\n", - "Linescan in process: 1axis_5.0_Back\n", - "Saved: results\\irr_1axis_5.0.csv\n", - "Index: 5.0. Wm2Front: 138836.09999999998. Wm2Back: 25479.60444444444\n", - "Linescan in process: 1axis_10.0_Front\n", - "Linescan in process: 1axis_10.0_Back\n", - "Saved: results\\irr_1axis_10.0.csv\n", - "Index: 10.0. Wm2Front: 23600.682222222225. Wm2Back: 4275.620222222222\n", - "Linescan in process: 1axis_15.0_Front\n", - "Linescan in process: 1axis_15.0_Back\n", - "Saved: results\\irr_1axis_15.0.csv\n", - "Index: 15.0. Wm2Front: 28227.198888888888. Wm2Back: 4791.782999999999\n", - "Linescan in process: 1axis_20.0_Front\n", - "Linescan in process: 1axis_20.0_Back\n", - "Saved: results\\irr_1axis_20.0.csv\n", - "Index: 20.0. Wm2Front: 105853.19999999998. Wm2Back: 18204.43888888889\n", - "Linescan in process: 1axis_25.0_Front\n", - "Linescan in process: 1axis_25.0_Back\n", - "Saved: results\\irr_1axis_25.0.csv\n", - "Index: 25.0. Wm2Front: 49050.19333333333. Wm2Back: 7848.846111111112\n", - "Linescan in process: 1axis_30.0_Front\n", - "Linescan in process: 1axis_30.0_Back\n", - "Saved: results\\irr_1axis_30.0.csv\n", - "Index: 30.0. Wm2Front: 54685.22111111111. Wm2Back: 8335.959777777778\n", - "Linescan in process: 1axis_35.0_Front\n", - "Linescan in process: 1axis_35.0_Back\n", - "Saved: results\\irr_1axis_35.0.csv\n", - "Index: 35.0. Wm2Front: 81168.01. Wm2Back: 12092.342222222222\n", - "Linescan in process: 1axis_40.0_Front\n", - "Linescan in process: 1axis_40.0_Back\n", - "Saved: results\\irr_1axis_40.0.csv\n", - "Index: 40.0. Wm2Front: 58196.84444444445. Wm2Back: 8184.981555555556\n", - "Linescan in process: 1axis_45.0_Front\n", - "Linescan in process: 1axis_45.0_Back\n", - "Saved: results\\irr_1axis_45.0.csv\n", - "Index: 45.0. Wm2Front: 367530.6777777778. Wm2Back: 38412.06888888889\n", - "Annual RADIANCE bifacial ratio for 1-axis tracking: 0.152\n" - ] - } - ], - "source": [ - "# Now let's create a scene using panels in portrait, 2m hub height, 0.33 GCR. \n", - "# NOTE: clearance needs to be calculated at each step. hub height is constant.\n", - "# 'orientation':'portrait' deprecated in v0.2.4 Also, sceneDict now defines nMods and nRows for the scene.\n", - "sceneDict = {'pitch':module_height / gcr,'hub_height':hub_height, 'nMods': 20, 'nRows': 7} \n", - "module_type = 'Prism Solar Bi60' # We are using the first simple module we defined.\n", - "# makeScene creates a .rad file with 20 modules per row, 7 rows repeating the module_type Prism Solar Bi60.\n", - "trackerdict = demo.makeScene1axis(trackerdict,module_type,sceneDict) \n", - "\n", - "# makeOct1axis joins the sky.rad file, ground.rad file, and the geometry.rad files created in makeScene.\n", - "trackerdict = demo.makeOct1axis(trackerdict)\n", - "\n", - "# Note: with v0.2.4 the analysis1axis has additional parameters to allow custom scans. parameters: \n", - "# sensorsy = int() (9 = default)\n", - "# modwanted = int() (middle module default)\n", - "# rowwanted = int() (middle row default)\n", - "# Now we need to run analysis and combine the results into an annual total. \n", - "# This can be done by doing a frontscan and backscan for the modwanted and rowwanted specified.\n", - "# the frontscan and backscan include a linescan along a chord of the module, both on the front and back. \n", - "trackerdict = demo.analysis1axis(trackerdict, modWanted=9, rowWanted = 2)\n", - "\n", - "# Return the minimum of the irradiance ratio, and the average of the irradiance ratio along a chord of the module.\n", - "print('Annual RADIANCE bifacial ratio for 1-axis tracking: %0.3f' %(sum(demo.Wm2Back)/sum(demo.Wm2Front)) )\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Note: same workflow can use stored self inputs rather than repeatedly keep passing trackerdict. \n", - "In super short version:" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": { - "scrolled": true - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "path = C:\\Users\\sayala\\Documents\\RadianceScenes\\Demo2\n", - "Getting weather file: USA_VA_Richmond.Intl.AP.724010_TMY.epw ... OK!\n", - "Saving file EPWs\\1axis_0.0.csv, # points: 2546\n", - "Saving file EPWs\\1axis_-30.0.csv, # points: 200\n", - "Saving file EPWs\\1axis_35.0.csv, # points: 166\n", - "Saving file EPWs\\1axis_5.0.csv, # points: 930\n", - "Saving file EPWs\\1axis_-25.0.csv, # points: 220\n", - "Saving file EPWs\\1axis_40.0.csv, # points: 142\n", - "Saving file EPWs\\1axis_10.0.csv, # points: 303\n", - "Saving file EPWs\\1axis_-20.0.csv, # points: 177\n", - "Saving file EPWs\\1axis_45.0.csv, # points: 832\n", - "Saving file EPWs\\1axis_15.0.csv, # points: 125\n", - "Saving file EPWs\\1axis_-15.0.csv, # points: 192\n", - "Saving file EPWs\\1axis_-45.0.csv, # points: 808\n", - "Saving file EPWs\\1axis_20.0.csv, # points: 228\n", - "Saving file EPWs\\1axis_-10.0.csv, # points: 402\n", - "Saving file EPWs\\1axis_-40.0.csv, # points: 173\n", - "Saving file EPWs\\1axis_25.0.csv, # points: 150\n", - "Saving file EPWs\\1axis_-5.0.csv, # points: 869\n", - "Saving file EPWs\\1axis_-35.0.csv, # points: 179\n", - "Saving file EPWs\\1axis_30.0.csv, # points: 118\n", - "message: There were 78 sun up hours in this climate file\n", - "Total Ibh/Lbh: 0.000000\n", - "Created skyfile skies\\1axis_0.0.rad\n", - "message: There were 158 sun up hours in this climate file\n", - "Total Ibh/Lbh: 0.000000\n", - "Created skyfile skies\\1axis_-30.0.rad\n", - "message: There were 166 sun up hours in this climate file\n", - "Total Ibh/Lbh: 0.000000\n", - "Created skyfile skies\\1axis_35.0.rad\n", - "message: There were 243 sun up hours in this climate file\n", - "Total Ibh/Lbh: 0.000000\n", - "Created skyfile skies\\1axis_5.0.rad\n", - "message: There were 175 sun up hours in this climate file\n", - "Total Ibh/Lbh: 0.000000\n", - "Created skyfile skies\\1axis_-25.0.rad\n", - "message: There were 142 sun up hours in this climate file\n", - "Total Ibh/Lbh: 0.000000\n", - "Created skyfile skies\\1axis_40.0.rad\n", - "message: There were 77 sun up hours in this climate file\n", - "Total Ibh/Lbh: 0.000000\n", - "Created skyfile skies\\1axis_10.0.rad\n", - "message: There were 106 sun up hours in this climate file\n", - "Total Ibh/Lbh: 0.000000\n", - "Created skyfile skies\\1axis_-20.0.rad\n", - "message: There were 832 sun up hours in this climate file\n", - "Total Ibh/Lbh: 0.000000\n", - "Created skyfile skies\\1axis_45.0.rad\n", - "message: There were 114 sun up hours in this climate file\n", - "Total Ibh/Lbh: 0.000000\n", - "Created skyfile skies\\1axis_15.0.rad\n", - "message: There were 133 sun up hours in this climate file\n", - "Total Ibh/Lbh: 0.000000\n", - "Created skyfile skies\\1axis_-15.0.rad\n", - "message: There were 805 sun up hours in this climate file\n", - "Total Ibh/Lbh: 0.000000\n", - "Created skyfile skies\\1axis_-45.0.rad\n", - "message: There were 227 sun up hours in this climate file\n", - "Total Ibh/Lbh: 0.000000\n", - "Created skyfile skies\\1axis_20.0.rad\n", - "message: There were 342 sun up hours in this climate file\n", - "Total Ibh/Lbh: 0.000000\n", - "Created skyfile skies\\1axis_-10.0.rad\n", - "message: There were 172 sun up hours in this climate file\n", - "Total Ibh/Lbh: 0.000000\n", - "Created skyfile skies\\1axis_-40.0.rad\n", - "message: There were 148 sun up hours in this climate file\n", - "Total Ibh/Lbh: 0.000000\n", - "Created skyfile skies\\1axis_25.0.rad\n", - "message: There were 73 sun up hours in this climate file\n", - "Total Ibh/Lbh: 0.000000\n", - "Created skyfile skies\\1axis_-5.0.rad\n", - "message: There were 146 sun up hours in this climate file\n", - "Total Ibh/Lbh: 0.000000\n", - "Created skyfile skies\\1axis_-35.0.rad\n", - "message: There were 118 sun up hours in this climate file\n", - "Total Ibh/Lbh: 0.000000\n", - "Created skyfile skies\\1axis_30.0.rad\n", - "\n", - "Making .rad files for cumulativesky 1-axis workflow\n", - "19 Radfiles created in /objects/\n", - "\n", - "Making 19 octfiles for 1-axis tracking in root directory.\n", - "Created 1axis_0.0.oct\n", - "Created 1axis_-30.0.oct\n", - "Created 1axis_35.0.oct\n", - "Created 1axis_5.0.oct\n", - "Created 1axis_-25.0.oct\n", - "Created 1axis_40.0.oct\n", - "Created 1axis_10.0.oct\n", - "Created 1axis_-20.0.oct\n", - "Created 1axis_45.0.oct\n", - "Created 1axis_15.0.oct\n", - "Created 1axis_-15.0.oct\n", - "Created 1axis_-45.0.oct\n", - "Created 1axis_20.0.oct\n", - "Created 1axis_-10.0.oct\n", - "Created 1axis_-40.0.oct\n", - "Created 1axis_25.0.oct\n", - "Created 1axis_-5.0.oct\n", - "Created 1axis_-35.0.oct\n", - "Created 1axis_30.0.oct\n", - "Linescan in process: 1axis_-45.0_Front\n", - "Linescan in process: 1axis_-45.0_Back\n", - "Saved: results\\irr_1axis_-45.0.csv\n", - "Index: -45.0. Wm2Front: 249566.895926. Wm2Back: 19384.2455556\n", - "Linescan in process: 1axis_-40.0_Front\n", - "Linescan in process: 1axis_-40.0_Back\n", - "Saved: results\\irr_1axis_-40.0.csv\n", - "Index: -40.0. Wm2Front: 64635.0832963. Wm2Back: 5147.35622222\n", - "Linescan in process: 1axis_-35.0_Front\n", - "Linescan in process: 1axis_-35.0_Back\n", - "Saved: results\\irr_1axis_-35.0.csv\n", - "Index: -35.0. Wm2Front: 52374.2586667. Wm2Back: 4000.96866667\n", - "Linescan in process: 1axis_-30.0_Front\n", - "Linescan in process: 1axis_-30.0_Back\n", - "Saved: results\\irr_1axis_-30.0.csv\n", - "Index: -30.0. Wm2Front: 47763.6232963. Wm2Back: 3872.36092593\n", - "Linescan in process: 1axis_-25.0_Front\n", - "Linescan in process: 1axis_-25.0_Back\n", - "Saved: results\\irr_1axis_-25.0.csv\n", - "Index: -25.0. Wm2Front: 87761.7807407. Wm2Back: 6906.90740741\n", - "Linescan in process: 1axis_-20.0_Front\n", - "Linescan in process: 1axis_-20.0_Back\n", - "Saved: results\\irr_1axis_-20.0.csv\n", - "Index: -20.0. Wm2Front: 28741.9303704. Wm2Back: 2223.83040741\n", - "Linescan in process: 1axis_-15.0_Front\n", - "Linescan in process: 1axis_-15.0_Back\n", - "Saved: results\\irr_1axis_-15.0.csv\n", - "Index: -15.0. Wm2Front: 26914.0655556. Wm2Back: 2134.80774074\n", - "Linescan in process: 1axis_-10.0_Front\n", - "Linescan in process: 1axis_-10.0_Back\n", - "Saved: results\\irr_1axis_-10.0.csv\n", - "Index: -10.0. Wm2Front: 141562.181481. Wm2Back: 11234.2171481\n", - "Linescan in process: 1axis_-5.0_Front\n", - "Linescan in process: 1axis_-5.0_Back\n", - "Saved: results\\irr_1axis_-5.0.csv\n", - "Index: -5.0. Wm2Front: 1608.84496296. Wm2Back: 153.732814815\n", - "Linescan in process: 1axis_0.0_Front\n", - "Linescan in process: 1axis_0.0_Back\n", - "Saved: results\\irr_1axis_0.0.csv\n", - "Index: 0.0. Wm2Front: 5932.48611111. Wm2Back: 410.173859259\n", - "Linescan in process: 1axis_5.0_Front\n", - "Linescan in process: 1axis_5.0_Back\n", - "Saved: results\\irr_1axis_5.0.csv\n", - "Index: 5.0. Wm2Front: 136599.122222. Wm2Back: 10650.050037\n", - "Linescan in process: 1axis_10.0_Front\n", - "Linescan in process: 1axis_10.0_Back\n", - "Saved: results\\irr_1axis_10.0.csv\n", - "Index: 10.0. Wm2Front: 22398.5144444. Wm2Back: 1579.30477778\n", - "Linescan in process: 1axis_15.0_Front\n", - "Linescan in process: 1axis_15.0_Back\n", - "Saved: results\\irr_1axis_15.0.csv\n", - "Index: 15.0. Wm2Front: 26239.9674074. Wm2Back: 1670.07488889\n", - "Linescan in process: 1axis_20.0_Front\n", - "Linescan in process: 1axis_20.0_Back\n", - "Saved: results\\irr_1axis_20.0.csv\n", - "Index: 20.0. Wm2Front: 96825.4744444. Wm2Back: 6858.268\n", - "Linescan in process: 1axis_25.0_Front\n", - "Linescan in process: 1axis_25.0_Back\n", - "Saved: results\\irr_1axis_25.0.csv\n", - "Index: 25.0. Wm2Front: 41199.6223704. Wm2Back: 2718.62581481\n", - "Linescan in process: 1axis_30.0_Front\n", - "Linescan in process: 1axis_30.0_Back\n", - "Saved: results\\irr_1axis_30.0.csv\n", - "Index: 30.0. Wm2Front: 47028.9877778. Wm2Back: 2737.57759259\n", - "Linescan in process: 1axis_35.0_Front\n", - "Linescan in process: 1axis_35.0_Back\n", - "Saved: results\\irr_1axis_35.0.csv\n", - "Index: 35.0. Wm2Front: 69287.6914815. Wm2Back: 3978.97455556\n", - "Linescan in process: 1axis_40.0_Front\n", - "Linescan in process: 1axis_40.0_Back\n", - "Saved: results\\irr_1axis_40.0.csv\n", - "Index: 40.0. Wm2Front: 50883.864963. Wm2Back: 2663.45833333\n", - "Linescan in process: 1axis_45.0_Front\n", - "Linescan in process: 1axis_45.0_Back\n", - "Saved: results\\irr_1axis_45.0.csv\n", - "Index: 45.0. Wm2Front: 257535.217037. Wm2Back: 13469.9401111\n", - "Annual bifacial ratio for 1-axis tracking: 0.070\n" - ] - } - ], - "source": [ - "try:\n", - " from bifacial_radiance import *\n", - "except ImportError:\n", - " raise RuntimeError('bifacial_radiance is required. download distribution')\n", - "\n", - "demo = RadianceObj(path = testfolder) \n", - "demo.setGround(0.2)\n", - "epwfile = demo.getEPW(37.5,-77.6) \n", - "metdata = demo.readEPW(epwfile)\n", - "demo.set1axis()\n", - "demo.genCumSky1axis()\n", - "module_type = '2upTracker' # Since we already created this module type, we don't need to makeModule, we just ned to call it when we make the Scene.\n", - "sceneDict = {'pitch':module_height / gcr,'height':hub_height, 'nMods': 20, 'nRows': 7} # orientation deprecated on v.0.2.4.\n", - "demo.makeScene1axis(moduletype=module_type,sceneDict = sceneDict)\n", - "demo.makeOct1axis()\n", - "demo.analysis1axis()\n", - "print('Annual bifacial ratio for 1-axis tracking: %0.3f' %(np.mean(demo.Wm2Back)/np.mean(demo.Wm2Front)) )\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# GENDAYLIT Workflow. -- hourly tracker option" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "## New v0.2.3 software includes the option for hourly tracked simulation workflow using gendaylit. \n", - "\n", - "# The first part is the same:\n", - "demo2 = RadianceObj('Gendaylit_TrackingHourly',testfolder) \n", - "demo2.setGround(0.2) \n", - "epwfile = demo2.getEPW(37.5,-77.6) #pull TMY data for any global lat/lon\n", - "metdata = demo2.readEPW(epwfile) # read in the weather data \n", - "\n", - "# This is the same for gencumsky and gendaylit: create a new moduletype, and specify a sceneDict. \n", - "module_type = 'Prism Solar Bi60'\n", - "demo2.makeModule(name=module_type,x=0.984,y=1.695,bifi = 0.90) # set module type to be used and passed into makeScene1axis\n", - "# Create the scenedictionary for the 1-axis tracking\n", - "sceneDict = {'pitch':1.695 / 0.33,'height':2.35, 'nMods': 20, 'nRows': 7} \n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# NEW hourly gendaylit workflow. Note that trackerdict is returned with hourly time points as keys instead of tracker angles.\n", - "trackerdict2 = demo2.set1axis(cumulativesky = False) # this cumulativesky = False key is crucial to set up the hourly workflow\n", - "\n", - "# This is for exemplifying the changes undergone in the trackerdict by each step. Just printing information.\n", - "print (\"Full trackerdict for all the year created by set1axis: %s \" % (len(trackerdict2))) # trackerdict contains all hours in the year as keys. For example: trackerdict2['12_16_08']\n", - "print (\"Contents of trackerdict for sample hour after creating on set1axis, \\n trackerdict2['12_16_08']: \\n %s \\n\" % ( trackerdict2['12_16_08']))\n", - "\n", - "# Create the skyfiles needed for 1-axis tracking. \n", - "# If you don't specify a startdate and enddate, all the year will be created, which will take more time. \n", - "# For this example we are doing the first half of January.\n", - "# Specifying the startdate and enddate also trims down the trackerdict from the whole year to just the entries between that start adn enddate.\n", - "trackerdict2 = demo2.gendaylit1axis(startdate='01/01', enddate='01/15') # optional parameters 'startdate', 'enddate' inputs = string 'MM/DD' or 'MM_DD' \n", - "\n", - "# This is for exemplifying the changes undergone in the trackerdict by each step. Just printing information.\n", - "print (\"\\nTrimmed trackerdict by gendaylit1axis to start and enddate: %s \" % (len(trackerdict2)))\n", - "print (\"Contents of trackerdict for sample hour after runing gendaylit1axis \\n trackerdict2['01_01_11']: %s \" % ( trackerdict2['01_01_11']))\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# making the different scenes for the 1-axis tracking for the dates in trackerdict2.\n", - "trackerdict2 = demo2.makeScene1axis(trackerdict2, module_type,sceneDict, cumulativesky = False) #makeScene creates a .rad file with 20 modules per row, 7 rows.\n", - "\n", - "# This is for exemplifying the changes undergone in the trackerdict by each step. Just printing information.\n", - "print (\"\\n Contents of trackerdict for sample hour after makeScene1axis: \\n trackerdict2['01_01_11']: \\n %s \" % ( trackerdict2['01_01_11']))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Run one single index (super fast example):" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Now this is the part that takes a long time, and will probably require parallel computing for doing more time points or the full year. \n", - "# For this example we just run one hourly point:\n", - "\n", - "demo2.makeOct1axis(trackerdict2,singleindex='01_01_11')\n", - "\n", - "# This is for exemplifying the changes undergone in the trackerdict by each step. Just printing information.\n", - "print (\"\\n Contents of trackerdict for sample hour after makeOct1axis: \\n trackerdict2['01_01_11']: \\n %s \\n\" % ( trackerdict2['01_01_11']))\n", - "\n", - "demo2.analysis1axis(trackerdict2,singleindex='01_01_11')\n", - "\n", - "# This is for exemplifying the changes undergone in the trackerdict by each step. Just printing information.\n", - "print (\"\\n Contents of trackerdict for sample hour after makeOct1axis: \\n trackerdict2['01_01_11']: \\n %s \\n\" % ( trackerdict2['01_01_11']))\n", - "\n", - "# Printing the results.\n", - "print('\\n\\n1-axis tracking hourly bifi gain: {:0.3}'.format(sum(demo2.Wm2Back) / sum(demo2.Wm2Front)))\n", - "\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Run a range of indexes: (not as fast as a single index, not as slow as all!)\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "for time in ['01_01_11','01_01_12']: # just two timepoints\n", - " demo2.makeOct1axis(trackerdict2,singleindex=time)\n", - " demo2.analysis1axis(trackerdict2,singleindex=time)\n", - "\n", - "print('1-axis tracking hourly bifi gain: {:0.3}'.format(sum(demo2.Wm2Back) / sum(demo2.Wm2Front)))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Run the full trackingdictionary...\n", - "(this might considerably more time, depending on the number of entries on the trackerdictionary! You've been warned) \n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "demo2.makeOct1axis(trackerdict2,singleindex=time)\n", - "demo2.analysis1axis(trackerdict2,singleindex=time)\n", - "print('1-axis tracking hourly bifi gain: {:0.3}'.format(sum(demo2.Wm2Back) / sum(demo2.Wm2Front)))\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Gendaylit for the WHOLE Year\n", - "And because you asked: this is the summarized version to run with gendaylit the WHOLE year. \n", - "#### This will take ~4 days on a really good computer. IF you're sure this is what you want, uncomment and run below :)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "'''\n", - "demo2 = RadianceObj('Gendaylit_AllYear_Tracking',testfolder) \n", - "demo2.setGround(0.2) \n", - "epwfile = demo2.getEPW(37.5,-77.6) #pull TMY data for any global lat/lon\n", - "metdata = demo2.readEPW(epwfile) # read in the weather data \n", - "module_type = 'Prism Solar Bi60'\n", - "sceneDict = {'pitch':1.695 / 0.33,'height':2.35, 'nMods': 20, 'nRows': 7} \n", - "trackerdict2 = demo2.set1axis(cumulativesky = False) # this cumulativesky = False key is crucial to set up the hourly workflow\n", - "trackerdict2 = demo2.gendaylit1axis() # optional parameters 'startdate', 'enddate' inputs = string 'MM/DD' or 'MM_DD' \n", - "trackerdict2 = demo2.makeScene1axis(trackerdict2, module_type,sceneDict, cumulativesky = False) #makeScene creates a .rad file with 20 modules per row, 7 rows.\n", - "demo2.makeOct1axis(trackerdict2)\n", - "demo2.analysis1axis(trackerdict2)\n", - "'''" - ] - } - ], - "metadata": { - "anaconda-cloud": {}, - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.3" - } - }, - "nbformat": 4, - "nbformat_minor": 1 -} diff --git a/requirements.txt b/requirements.txt index c16f57e8..28f09624 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,6 +5,7 @@ colorama==0.4.1 coverage==4.5.2 cycler==0.10.0 funcsigs==1.0.2 +ipython==5.8.0 kiwisolver==1.0.1 matplotlib==2.2.3 more-itertools==5.0.0 @@ -12,7 +13,8 @@ numpy==1.16.0 pandas==0.24.0 pathlib2==2.3.3 pluggy==0.8.1 -pvlib==0.6.0 +pvlib==0.6.3 +pvmismatch==4.1 py==1.7.0 pyparsing==2.3.1 pytest==4.1.1 @@ -20,5 +22,8 @@ pytest-cov==2.6.1 python-dateutil==2.7.5 pytz==2018.9 scandir==1.9.0 -scipy==0.19.1 six==1.12.0 +sphinx-autoapi==1.1.0 +sphinx-rtd-theme==0.4.3 +requests +future \ No newline at end of file diff --git a/setup.py b/setup.py index 9455bf94..e8d036fa 100644 --- a/setup.py +++ b/setup.py @@ -7,13 +7,13 @@ usage: `pip install -e .` GenCumulativeSky was conceived, developed and validated by Darren Robinson and Andrew Stone for efficient solar irradiation modelling using RADIANCE -When using GenCumulativeSky they would be pleased if you would ackowledge their work by referring to the following article: "Robinson, D., Stone, A., +When using GenCumulativeSky they would be pleased if you would acknowledge their work by referring to the following article: "Robinson, D., Stone, A., Irradiation modeling made simple – the cumulative sky approach and its applications, Proc. PLEA 2004, Eindhoven 2004." """ # Always prefer setuptools over distutils -from setuptools import setup, find_packages +from setuptools import setup, find_packages, Extension # To use a consistent encoding from codecs import open from os import path @@ -26,9 +26,17 @@ with open(path.join(here, 'README.md'), encoding='utf-8') as f: long_description = f.read() +# gencumsky c++ source can (theoretically) be compiled at pip install runtime +gencumskymodule = Extension('gencumulativesky', + sources = ['GenCumSky/*.cpp']) + setup( name='bifacial_radiance', - + #ext_modules = [gencumskymodule], + # Visual C++ build tools required: https://visualstudio.microsoft.com/visual-cpp-build-tools/ + # enable the above to automatically compile GenCumSky source code as part of + # setup.py. Not really recommended for now... + # Versions should comply with PEP440. For a discussion on single-sourcing # the version across setup.py and the project code, see # https://packaging.python.org/en/latest/single_source_version.html @@ -89,10 +97,11 @@ # requirements files see: # https://packaging.python.org/en/latest/requirements.html install_requires=[ - 'pvlib', + 'pvlib>= 0.6.1', 'pytest', 'pytest-cov', - 'configparser' + 'configparser', + 'requests' ], # List additional groups of dependencies here (e.g. development diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/test_1axis.ini b/tests/ini_1axis.ini similarity index 93% rename from tests/test_1axis.ini rename to tests/ini_1axis.ini index fe8a365a..8f154872 100644 --- a/tests/test_1axis.ini +++ b/tests/ini_1axis.ini @@ -10,8 +10,8 @@ torqueTube = True hpc = False tracking = True cumulativeSky = False -timestampRangeSimulation = True -daydateSimulation = False +timestampRangeSimulation = False +daydateSimulation = True [sceneParamsDict] gcrorpitch = gcr diff --git a/tests/test_highAzimuth.ini b/tests/ini_highAzimuth.ini similarity index 89% rename from tests/test_highAzimuth.ini rename to tests/ini_highAzimuth.ini index 7b0aa270..0f5754c5 100644 --- a/tests/test_highAzimuth.ini +++ b/tests/ini_highAzimuth.ini @@ -17,12 +17,12 @@ latitude: 37.5 longitude: -77.6 [timeControlParamsDict] -HourStart: 0 -HourEnd: 24 -DayStart: 1 -DayEnd: 31 -MonthStart: 1 -MonthEnd: 12 +HourStart: 13 +HourEnd: 14 +DayStart: 17 +DayEnd: 17 +MonthStart: 6 +MonthEnd: 6 timeindexstart: 4020 timeindexend: 4021 diff --git a/tests/results_mismatch/test_2UP_torque_tube_hex_4020.csv b/tests/results_mismatch/test_2UP_torque_tube_hex_4020.csv new file mode 100644 index 00000000..0c61f98d --- /dev/null +++ b/tests/results_mismatch/test_2UP_torque_tube_hex_4020.csv @@ -0,0 +1,201 @@ +x,y,z,rearZ,mattype,rearMat,Wm2Front,Wm2Back,Back/FrontRatio +-0.01044582,0.0,3.720218,2.321842,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.7316666666667,251.56696666666664,0.42370410251976476 +-0.02089164,0.0,3.720218,2.323684,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.7344666666667,251.25206666666668,0.423171733494782 +-0.03133746,0.0,3.720218,2.325526,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.7354666666666,250.93843333333334,0.42264278416675777 +-0.04178329,0.0,3.720218,2.327368,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.7364333333334,250.62476666666666,0.42211380417707645 +-0.05222911,0.0,3.720218,2.329209,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.7374666666666,250.31113333333334,0.4215848347145439 +-0.06267493,0.0,3.720218,2.331051,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.7384333333334,249.99756666666667,0.4210560266532182 +-0.07312075,0.0,3.720218,2.332893,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.7393666666667,249.6839,0.4205270754989372 +-0.08356657,0.0,3.720218,2.334735,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.7404,249.37026666666668,0.41999811141124177 +-0.09401239,0.0,3.720218,2.336577,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.7414,249.05663333333337,0.41946917271418277 +-0.1044582,0.0,3.720218,2.338419,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.7423666666667,248.74303333333333,0.41894031545952426 +-0.114904,0.0,3.720218,2.340261,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.7433666666667,248.42936666666665,0.4184113241551597 +-0.1253499,0.0,3.720218,2.342103,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.7443666666667,248.1157333333333,0.4178823907734634 +-0.1357957,0.0,3.720218,2.343944,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.7453333333333,247.8021,0.41735348260396277 +-0.1462415,0.0,3.720218,2.345786,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.7462666666667,247.48846666666665,0.4168245995574548 +-0.1566873,0.0,3.720218,2.347628,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.7473,247.17486666666665,0.4162957042010338 +-0.1671331,0.0,3.720218,2.34947,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.7482666666666,246.86120000000003,0.4157667450873484 +-0.177579,0.0,3.720218,2.351312,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.7492666666666,246.54756666666665,0.4152378205247684 +-0.1880248,0.0,3.720218,2.353154,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.7502666666668,246.23396666666667,0.4147089538840562 +-0.1984706,0.0,3.720218,2.354996,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.7512666666668,245.9203,0.4141799767445098 +-0.2089164,0.0,3.720218,2.356838,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.7522333333334,245.60666666666665,0.4136510807492023 +-0.2193623,0.0,3.720218,2.358679,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.7532,245.29303333333334,0.4131221864760424 +-0.2298081,0.0,3.720218,2.360521,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.7542,244.97940000000003,0.41259327076209196 +-0.2402539,0.0,3.720218,2.362363,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.7551666666667,244.6658,0.4120644361026987 +-0.2506997,0.0,3.720218,2.364205,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.7561666666667,244.35216666666668,0.41153552392209725 +-0.2611455,0.0,3.720218,2.366047,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.7571666666666,244.03853333333336,0.4110066135230702 +-0.2715914,0.0,3.720218,2.367889,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.7581666666666,243.72486666666666,0.41047764876612436 +-0.2820372,0.0,3.720218,2.369731,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.7591333333334,243.41126666666665,0.4099488210839798 +-0.292483,0.0,3.720218,2.371573,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.7601333333333,243.09759999999997,0.4094198598605926 +-0.3029288,0.0,3.720218,2.373415,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.7611666666668,242.784,0.4088909897425259 +-0.3133746,0.0,3.720218,2.375256,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.7620666666668,242.47033333333331,0.4083621008873797 +-0.3238205,0.0,3.720218,2.377098,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.7630666666668,242.1567,0.4078332010885131 +-0.3342663,0.0,3.720218,2.37894,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.7640666666667,241.84310000000002,0.4073043592100849 +-0.3447121,0.0,3.720218,2.380782,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.7650666666667,241.5294666666667,0.4067754629741388 +-0.3551579,0.0,3.720218,2.382624,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.7660666666667,241.21580000000003,0.40624651238094944 +-0.3656038,0.0,3.720218,2.384466,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.7670666666667,236.6145333333333,0.3984965622379378 +-0.3760496,0.0,3.720218,2.386308,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.768,236.09716666666665,0.3976246093458342 +-0.3864954,0.0,3.720218,2.38815,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.7689666666666,235.58063333333334,0.396754040383428 +-0.3969412,0.0,3.720218,2.389991,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.7699666666666,235.06410000000002,0.39588345203136416 +-0.407387,0.0,3.720218,2.391833,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.7710000000001,234.54753333333335,0.3950127882980897 +-0.4178329,0.0,3.720218,2.393675,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.7719666666667,234.03103333333334,0.39414228412442043 +-0.4282787,0.0,3.720218,2.395517,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.7729333333333,233.5145,0.3932717266470326 +-0.4387245,0.0,3.720218,2.397359,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.7739666666668,232.99799999999996,0.392401184084947 +-0.4491703,0.0,3.720218,2.399201,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.7749333333334,232.4814,0.3915305200985467 +-0.4596161,0.0,3.720218,2.401043,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.7758666666667,231.96490000000003,0.3906600492912433 +-0.470062,0.0,3.720218,2.402885,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.7769,231.44836666666666,0.3897894594370499 +-0.4805078,0.0,3.720218,2.404726,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.7779,230.93183333333332,0.3889188944459517 +-0.4909536,0.0,3.720218,2.406568,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.7788666666667,230.4153,0.38804835417120914 +-0.5013994,0.0,3.720218,2.40841,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.7798666666666,229.89873333333333,0.3871777388583196 +-0.5118453,0.0,3.720218,2.410252,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.7808666666666,229.38223333333335,0.3863072387525476 +-0.5222911,0.0,3.720218,2.412094,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.7818333333333,228.86566666666667,0.3854366509416883 +-0.5327369,0.0,3.720218,2.413936,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.7828333333333,228.34913333333336,0.38456610051413215 +-0.5431827,0.0,3.720218,2.415778,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.7838333333333,227.83263333333332,0.38369560915584183 +-0.5536285,0.0,3.720218,2.41762,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.7848333333334,227.31606666666667,0.38282500845563006 +-0.5640744,0.0,3.720218,2.419462,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.7857666666667,226.79956666666666,0.3819545658449893 +-0.5745202,0.0,3.720218,2.421303,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.7867666666667,226.283,0.3810839709114923 +-0.584966,0.0,3.720218,2.423145,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.7877666666667,225.76646666666667,0.3802134350470198 +-0.5954118,0.0,3.720218,2.424987,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.7887666666667,225.24993333333336,0.3793429021146823 +-0.6058576,0.0,3.720218,2.426829,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.7897333333334,224.7334,0.378472393360579 +-0.6163035,0.0,3.720218,2.428671,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.7907333333334,224.21686666666665,0.3776018662435628 +-0.6267493,0.0,3.720218,2.430513,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.7916666666666,223.7003,0.3767313282189407 +-0.6371951,0.0,3.720218,2.432355,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.7926666666666,223.1838,0.37586086300460153 +-0.6476409,0.0,3.720218,2.434197,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.7937000000001,222.66729999999998,0.3749903796716272 +-0.6580868,0.0,3.720218,2.436038,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.7946666666668,222.1507,0.37411977296342674 +-0.6685326,0.0,3.720218,2.43788,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.7956666666668,221.6342,0.3732493165449452 +-0.6789784,0.0,3.720218,2.439722,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.7966666666667,221.11763333333332,0.3723787507866035 +-0.6894242,0.0,3.720218,2.441564,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.7976333333332,213.03403333333335,0.35876477542134244 +-0.69987,0.0,3.720218,2.443406,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.7985666666667,211.75373333333334,0.35660809677249683 +-0.7103159,0.0,3.720218,2.445248,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.7995666666667,210.4746,0.3544533498536574 +-0.7207617,0.0,3.720218,2.44709,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.8006,209.19546666666668,0.3522985904158336 +-0.7312075,0.0,3.720218,2.448932,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.8015666666666,207.91633333333334,0.3501438777883356 +-0.7416533,0.0,3.720218,2.450773,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.8025666666666,206.63719999999998,0.34798915264177993 +-0.7520992,0.0,3.720218,2.452615,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.8035666666666,205.35806666666667,0.34583443475257886 +-0.762545,0.0,3.720218,2.454457,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.8045000000001,204.0789,0.3436797065705858 +-0.7729908,0.0,3.720218,2.456299,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.8054666666667,202.7998,0.34152507826062745 +-0.7834366,0.0,3.720218,2.458141,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.8065666666668,201.52066666666667,0.33937032462873634 +-0.7938824,0.0,3.720218,2.459983,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.8075,200.24153333333334,0.337215673627665 +-0.8043283,0.0,3.720218,2.461825,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.8084666666667,198.9624,0.3350610105912761 +-0.8147741,0.0,3.720218,2.463667,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.8094666666667,197.68326666666667,0.3329063358824819 +-0.8252199,0.0,3.720218,2.465509,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.8104666666667,196.4041333333333,0.33075166843078807 +-0.8356657,0.0,3.720218,2.46735,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.8114333333333,195.125,0.3285970266817699 +-0.8461115,0.0,3.720218,2.469192,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.8124333333334,204.2764,0.3440077110639071 +-0.8565574,0.0,3.720218,2.471034,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.8134333333334,206.20606666666666,0.3472567440120042 +-0.8670032,0.0,3.720218,2.472876,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.8144000000001,208.13660000000002,0.35050724518090975 +-0.877449,0.0,3.720218,2.474718,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.8153666666666,210.06706666666665,0.3537576234987573 +-0.8878948,0.0,3.720218,2.47656,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.8163666666668,211.9976333333333,0.3570081395957825 +-0.8983407,0.0,3.720218,2.478402,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.8173666666668,213.92816666666667,0.3602585886110741 +-0.9087865,0.0,3.720218,2.480244,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.8183666666667,215.85866666666666,0.36350897054497094 +-0.9192323,0.0,3.720218,2.482085,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.8193333333332,217.78919999999997,0.3667594182527712 +-0.9296781,0.0,3.720218,2.483927,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.8203666666667,233.22866666666664,0.39275896718884196 +-0.9401239,0.0,3.720218,2.485769,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.8213,236.26953333333336,0.3978791859674744 +-0.9505698,0.0,3.720218,2.487611,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.8222666666667,239.31066666666666,0.4029998150965007 +-0.9610156,0.0,3.720218,2.489453,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.8233,242.35176666666666,0.4081203256024832 +-0.9714614,0.0,3.720218,2.491295,a10.3.a0.PVmodule.6457,a10.3.a0.PVmodule.2310,593.8242666666666,245.3929333333333,0.41324097694731526 +-0.9819072,0.0,3.720218,2.493137,a10.3.hextube1b.6457,sky,195.6762,262.1641,1.3397784718914623 +-0.992353,0.0,3.720218,2.494979,a10.3.hextube1b.6457,sky,198.4741,262.1641,1.3208916382961895 +-1.002799,0.0,3.720218,2.49682,a10.3.hextube1b.6457,sky,201.27116666666666,262.1641,1.302535290108833 +-1.013245,0.0,3.720218,2.498662,a10.3.hextube1a.6457,sky,406.48606666666666,262.1641,0.6449506552566003 +-1.023691,0.0,3.720218,2.500504,a10.3.hextube1a.6457,sky,408.9166,262.1641,0.64111718351081 +-1.034136,0.0,3.720218,2.502346,a10.3.hextube1a.6457,sky,411.3470666666667,262.1641,0.6373291167366615 +-1.044582,0.0,3.720218,2.504188,a10.3.hextube1a.6457,sky,413.77756666666664,262.1641,0.6335854998772694 +-1.055028,0.0,3.720218,2.50603,a10.3.hextube1a.6457,sky,416.20806666666664,262.1641,0.6298856055674585 +-1.065474,0.0,3.720218,2.507872,a10.3.hextube1a.6457,sky,418.6385666666667,262.1641,0.6262286722858733 +-1.07592,0.0,3.720218,2.509714,a10.3.hextube1a.6457,sky,421.0690666666667,262.1641,0.6226139560937681 +-1.086365,0.0,3.720218,2.511556,a10.3.hextube1a.6457,sky,423.4995333333333,262.1641,0.619040778854588 +-1.096811,0.0,3.720218,2.513397,a10.3.hextube1a.6457,sky,392.97316666666666,262.1641,0.6671280766971537 +-1.107257,0.0,3.720218,2.515239,a10.3.hextube1a.6457,sky,163.6367,262.1641,1.6021008606207496 +-1.117703,0.0,3.720218,2.517081,a10.3.hextube1a.6457,sky,161.43063333333336,262.1641,1.6239945950287726 +-1.128149,0.0,3.720218,2.518923,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.8390666666668,263.1624,0.4431536616873611 +-1.138595,0.0,3.720218,2.520765,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.8400666666668,262.9383666666667,0.4427756540021483 +-1.14904,0.0,3.720218,2.522607,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.8410333333334,262.7133,0.44239593234137853 +-1.159486,0.0,3.720218,2.524449,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.8420333333332,262.4881666666667,0.44201607484267325 +-1.169932,0.0,3.720218,2.526291,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.8430666666667,262.26309999999995,0.44163630609651616 +-1.180378,0.0,3.720218,2.528132,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.8440333333333,262.03796666666665,0.44125647594594125 +-1.190824,0.0,3.720218,2.529974,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.8449666666667,261.81286666666665,0.4408767279101949 +-1.201269,0.0,3.720218,2.531816,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.846,261.5877666666667,0.4404969068912813 +-1.211715,0.0,3.720218,2.533658,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.8469666666666,246.67406666666668,0.41538252299031203 +-1.222161,0.0,3.720218,2.5355,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.8479666666666,245.00896666666665,0.4125779119258663 +-1.232607,0.0,3.720218,2.537342,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.8489333333333,243.3427333333333,0.4097714248571665 +-1.243053,0.0,3.720218,2.539184,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.8499666666668,241.67656666666667,0.40696501350030073 +-1.253499,0.0,3.720218,2.541026,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.8509333333333,240.0103333333333,0.4041585450200325 +-1.263944,0.0,3.720218,2.542868,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.8519,238.3441333333333,0.40135214180705914 +-1.27439,0.0,3.720218,2.544709,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.8528666666667,236.6779,0.39854569159999853 +-1.284836,0.0,3.720218,2.546551,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.8538666666667,235.01173333333335,0.3957393405773779 +-1.295282,0.0,3.720218,2.548393,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.8548666666667,233.3455333333333,0.3929329428757011 +-1.305728,0.0,3.720218,2.550235,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.8558666666667,234.29283333333333,0.39452744673716555 +-1.316174,0.0,3.720218,2.552077,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.8568666666666,233.44386666666665,0.3930972035059343 +-1.326619,0.0,3.720218,2.553919,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.8578000000001,232.59350000000003,0.3916646515973157 +-1.337065,0.0,3.720218,2.555761,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.8588,231.74316666666664,0.3902321165141447 +-1.347511,0.0,3.720218,2.557603,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.8598000000001,230.8928,0.3887995301255782 +-1.357957,0.0,3.720218,2.559444,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.8607666666668,230.0425,0.38736708256405117 +-1.368403,0.0,3.720218,2.561286,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.8617666666668,229.19213333333335,0.38593450574411603 +-1.378848,0.0,3.720218,2.563128,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.8628,228.34176666666667,0.38450191216684143 +-1.389294,0.0,3.720218,2.56497,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.8637666666667,227.4914666666667,0.383069478837017 +-1.39974,0.0,3.720218,2.566812,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.8647,226.64110000000002,0.3816369593327246 +-1.410186,0.0,3.720218,2.568654,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.8657,229.4752,0.3864085997750001 +-1.420632,0.0,3.720218,2.570496,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.8667,229.5600333333333,0.38655079798637526 +-1.431078,0.0,3.720218,2.572338,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.8676666666667,229.6436666666667,0.3866909967748201 +-1.441523,0.0,3.720218,2.574179,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.8687,229.72736666666665,0.38683126393999673 +-1.451969,0.0,3.720218,2.576021,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.8696666666666,229.8110333333333,0.38697151792870726 +-1.462415,0.0,3.720218,2.577863,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.8706333333333,229.89466666666667,0.38711171533197214 +-1.472861,0.0,3.720218,2.579705,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.8716,229.9783666666667,0.38725202453635127 +-1.483307,0.0,3.720218,2.581547,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.8726333333333,230.0620333333333,0.38739223366766745 +-1.493752,0.0,3.720218,2.583389,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.8736333333334,230.14573333333337,0.3875325201912705 +-1.504198,0.0,3.720218,2.585231,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.8746,230.2293666666667,0.38767271574495854 +-1.514644,0.0,3.720218,2.587073,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.8756,230.31306666666669,0.3878130013317021 +-1.52509,0.0,3.720218,2.588915,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.8765666666667,230.39673333333334,0.3879532520928697 +-1.535536,0.0,3.720218,2.590756,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.8775333333333,230.4804333333333,0.38809355852566035 +-1.545982,0.0,3.720218,2.592598,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.8785666666666,233.87630000000001,0.3938109898488398 +-1.556427,0.0,3.720218,2.59444,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.8795666666666,234.35203333333334,0.39461138566749987 +-1.566873,0.0,3.720218,2.596282,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.8805333333333,234.82600000000002,0.39540882620473244 +-1.577319,0.0,3.720218,2.598124,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.8815,235.29999999999998,0.39620632027379155 +-1.587765,0.0,3.720218,2.599966,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.8825,235.77396666666667,0.39700373333602745 +-1.598211,0.0,3.720218,2.601808,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.8834666666668,236.24800000000002,0.3978012782957673 +-1.608657,0.0,3.720218,2.60365,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.8844666666668,236.72196666666665,0.3985986860317174 +-1.619102,0.0,3.720218,2.605491,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.8854333333333,237.19596666666666,0.3993961696268193 +-1.629548,0.0,3.720218,2.607333,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.8864666666667,237.66996666666668,0.40019360570218016 +-1.639994,0.0,3.720218,2.609175,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.8874333333333,238.14396666666667,0.40099108401561173 +-1.65044,0.0,3.720218,2.611017,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.8883666666667,238.61796666666666,0.40178858228420883 +-1.660886,0.0,3.720218,2.612859,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.8894333333333,239.09196666666665,0.4025859876622585 +-1.671331,0.0,3.720218,2.614701,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.8903666666666,239.5659333333333,0.4033834246117177 +-1.681777,0.0,3.720218,2.616543,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.8913666666666,240.03993333333332,0.4041808698108092 +-1.692223,0.0,3.720218,2.618385,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.8923666666666,240.5139333333333,0.4049783123244179 +-1.702669,0.0,3.720218,2.620226,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.8933666666667,240.98793333333333,0.40577575215255735 +-1.713115,0.0,3.720218,2.622068,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.8942999999999,241.46193333333335,0.4065732349343957 +-1.723561,0.0,3.720218,2.62391,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.8953,241.93593333333334,0.407370669481075 +-1.734006,0.0,3.720218,2.625752,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.8963333333334,242.4099,0.4081680223068858 +-1.744452,0.0,3.720218,2.627594,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.8972666666667,242.8839,0.4089654973455611 +-1.754898,0.0,3.720218,2.629436,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.8983,243.3579,0.409762900882355 +-1.765344,0.0,3.720218,2.631278,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.8992666666667,243.83186666666666,0.4105602916045163 +-1.77579,0.0,3.720218,2.63312,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.9002666666667,244.30589999999998,0.4113577688951441 +-1.786235,0.0,3.720218,2.634962,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.9012000000001,244.77986666666666,0.4121551775135142 +-1.796681,0.0,3.720218,2.636803,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.9022666666666,245.25386666666668,0.4129525470421727 +-1.807127,0.0,3.720218,2.638645,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.9032333333333,245.7278666666667,0.41374998337274027 +-1.817573,0.0,3.720218,2.640487,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.9041666666666,246.20186666666666,0.4145474403741788 +-1.828019,0.0,3.720218,2.642329,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.9317,246.67583333333334,0.4153262370186611 +-1.838465,0.0,3.720218,2.644171,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.9342666666666,247.1498333333333,0.4161225089737613 +-1.84891,0.0,3.720218,2.646013,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.9349,247.62383333333332,0.4169201311679145 +-1.859356,0.0,3.720218,2.647855,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.9355333333334,248.09783333333334,0.417717751661008 +-1.869802,0.0,3.720218,2.649697,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.9361666666667,248.57180000000002,0.4185153143303879 +-1.880248,0.0,3.720218,2.651538,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.9368,249.04583333333335,0.41931298754403806 +-1.890694,0.0,3.720218,2.65338,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.9374333333334,249.5198,0.4201105468114456 +-1.90114,0.0,3.720218,2.655222,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.9380333333334,249.9938,0.42090818412282605 +-1.911585,0.0,3.720218,2.657064,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.9386666666668,250.4678,0.4217057961555017 +-1.922031,0.0,3.720218,2.658906,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.9393,250.94179999999997,0.4225034064871503 +-1.932477,0.0,3.720218,2.660748,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.9399333333333,251.4158,0.4233010151177771 +-1.942923,0.0,3.720218,2.66259,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.9406,251.8898,0.42409859824602286 +-1.953369,0.0,3.720218,2.664432,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.9412,252.36373333333336,0.4248961150316199 +-1.963814,0.0,3.720218,2.666273,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.9419,252.8378,0.42569378302190325 +-1.97426,0.0,3.720218,2.668115,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.9425,253.31176666666667,0.42649135257253706 +-1.984706,0.0,3.720218,2.669957,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.9431666666668,253.78573333333335,0.42728887255115167 +-1.995152,0.0,3.720218,2.671799,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.9438,254.25973333333332,0.42808647088640783 +-2.005598,0.0,3.720218,2.673641,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.9444333333332,267.72963333333337,0.4507646970712182 +-2.016044,0.0,3.720218,2.675483,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.9450666666667,268.13273333333336,0.45144289756499784 +-2.026489,0.0,3.720218,2.677325,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.9457,268.5355666666667,0.45212064763835996 +-2.036935,0.0,3.720218,2.679167,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.9463333333333,268.93833333333333,0.4527982840229381 +-2.047381,0.0,3.720218,2.681009,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.9469666666668,269.3412,0.45347608732729056 +-2.057827,0.0,3.720218,2.68285,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.9476,269.74396666666667,0.4541537208214089 +-2.068273,0.0,3.720218,2.684692,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.9482333333334,270.1468333333333,0.4548315212349517 +-2.078718,0.0,3.720218,2.686534,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.9488666666666,270.54963333333336,0.4555092079600883 +-2.089164,0.0,3.720218,2.688376,a10.3.a1.PVmodule.6457,a10.3.a1.PVmodule.2310,593.9494666666666,270.9524666666667,0.4561869749632322 \ No newline at end of file diff --git a/tests/test_bifacial_radiance.py b/tests/test_bifacial_radiance.py index 326bab47..9ad7689e 100644 --- a/tests/test_bifacial_radiance.py +++ b/tests/test_bifacial_radiance.py @@ -23,6 +23,8 @@ except: pass +TESTDIR = os.path.dirname(__file__) # this folder + # test the readepw on a dummy Boulder EPW file in the /tests/ directory MET_FILENAME = 'USA_CO_Boulder.724699_TMY2.epw' # also test a dummy TMY3 Denver file in /tests/ @@ -35,8 +37,8 @@ def test_RadianceObj_set1axis(): demo = bifacial_radiance.RadianceObj(name) metdata = demo.readEPW(epwfile = MET_FILENAME) trackerdict = demo.set1axis() - assert trackerdict[0]['count'] == 75 #this was 108 in v0.2.4 and earlier - assert trackerdict[45]['count'] == 823 + assert trackerdict[0]['count'] == 80 #this was 108 < v0.2.4 and 75 < 0.3.2 + assert trackerdict[45]['count'] == 822 #this was 823 < 0.3.2 def test_RadianceObj_fixed_tilt_end_to_end(): # just run the demo example. Rear irradiance fraction roughly 11.8% for 0.95m landscape panel @@ -67,19 +69,20 @@ def test_RadianceObj_fixed_tilt_end_to_end(): def test_Radiance_high_azimuth_modelchains(): # duplicate next example using modelchain # high azimuth .ini file - HIGH_AZIMUTH_INI = "test_highAzimuth.ini" + + HIGH_AZIMUTH_INI = os.path.join(TESTDIR, "ini_highAzimuth.ini") (Params)= bifacial_radiance.load.readconfigurationinputfile(inifile=HIGH_AZIMUTH_INI) - Params[0]['testfolder'] = os.getcwd() + Params[0]['testfolder'] = TESTDIR # unpack the Params tuple with *Params demo2, analysis = bifacial_radiance.modelchain.runModelChain(*Params ) #assert np.round(np.mean(analysis.backRatio),2) == 0.20 # bifi ratio was == 0.22 in v0.2.2 assert np.mean(analysis.Wm2Front) == pytest.approx(899, rel = 0.005) # was 912 in v0.2.3 - assert np.mean(analysis.Wm2Back) == pytest.approx(189, rel = 0.02) # was 182 in v0.2.2 + assert np.mean(analysis.Wm2Back) == pytest.approx(189, rel = 0.03) # was 182 in v0.2.2 """ def test_RadianceObj_high_azimuth_angle_end_to_end(): - # modify example for high azimuth angle to test different parts of makesceneNxR. Rear irradiance fraction roughly 17.3% for 0.95m landscape panel + # modify example for high azimuth angle to test different parts of _makeSceneNxR. Rear irradiance fraction roughly 17.3% for 0.95m landscape panel # takes 14 seconds for sensorsy = 9, 11 seconds for sensorsy = 2 name = "_test_high_azimuth_angle_end_to_end" demo = bifacial_radiance.RadianceObj(name) # Create a RadianceObj 'object' @@ -107,12 +110,12 @@ def test_RadianceObj_high_azimuth_angle_end_to_end(): """ def test_Radiance_1axis_gendaylit_modelchains(): - # duplicate next scample using modelchain + # duplicate next sample using modelchain # 1-axis .ini file - filename = "test_1axis.ini" + filename = "ini_1axis.ini" (Params)= bifacial_radiance.load.readconfigurationinputfile(inifile=filename) - Params[0]['testfolder'] = os.getcwd() + Params[0]['testfolder'] = TESTDIR # unpack the Params tuple with *Params demo2, analysis = bifacial_radiance.modelchain.runModelChain(*Params ) #V 0.2.5 fixed the gcr passed to set1axis. (since gcr was not being passd to set1axis, gcr was default 0.33 default). @@ -130,7 +133,7 @@ def test_RadianceObj_1axis_gendaylit_end_to_end(): demo = bifacial_radiance.RadianceObj(name) # Create a RadianceObj 'object' demo.setGround(albedo) # input albedo number or material name like 'concrete'. To see options, run this without any input. - metdata = demo.readEPW(MET_FILENAME) # read in the EPW weather data from above + metdata = demo.readEPW(MET_FILENAME, starttime='01_01_01', endtime = '01_01_23') # read in the EPW weather data from above #metdata = demo.readTMY(MET_FILENAME2) # select a TMY file using graphical picker # set module type to be used and passed into makeScene1axis # test modules with gap and rear tube @@ -154,13 +157,13 @@ def test_RadianceObj_1axis_gendaylit_end_to_end(): """ def test_SceneObj_makeSceneNxR_lowtilt(): - # test makeSceneNxR(tilt, height, pitch, azimuth = 180, nMods = 20, nRows = 7, radname = None) + # test _makeSceneNxR(tilt, height, pitch, azimuth = 180, nMods = 20, nRows = 7, radname = None) # default scene with simple_panel, 10 degree tilt, 0.2 height, 1.5 row spacing, landscape name = "_test_makeSceneNxR_lowtilt" demo = bifacial_radiance.RadianceObj(name) demo.makeModule(name='test',y=0.95,x=1.59) #scene = bifacial_radiance.SceneObj(moduletype = name) - #scene.makeSceneNxR(tilt=10,height=0.2,pitch=1.5) + #scene._makeSceneNxR(tilt=10,height=0.2,pitch=1.5) sceneDict={'tilt':10, 'height':0.2, 'pitch':1.5} scene = demo.makeScene(moduletype='test', sceneDict=sceneDict) analysis = bifacial_radiance.AnalysisObj() @@ -178,13 +181,13 @@ def test_SceneObj_makeSceneNxR_lowtilt(): assert scene.text[0:116] == '!xform -rx 10 -t 0 0 0.2824828843917919 -a 20 -t 1.6 0 0 -a 7 -t 0 1.5 0 -i 1 -t -14.4 -4.5 0 -rz 0 -t 0 0 0 objects' #linux has different directory structure and will error here. def test_SceneObj_makeSceneNxR_hightilt(): - # test makeSceneNxR(tilt, height, pitch, orientation = None, azimuth = 180, nMods = 20, nRows = 7, radname = None) + # test _makeSceneNxR(tilt, height, pitch, orientation = None, azimuth = 180, nMods = 20, nRows = 7, radname = None) # default scene with simple_panel, 50 degree tilt, 0.2 height, 1.5 row spacing, landscape name = "_test__makeSceneNxR_hightilt" demo = bifacial_radiance.RadianceObj(name) demo.makeModule(name='test',y=0.95,x=1.59) #scene = bifacial_radiance.SceneObj(moduletype = name) - #scene.makeSceneNxR(tilt=65,height=0.2,pitch=1.5,azimuth=89) + #scene._makeSceneNxR(tilt=65,height=0.2,pitch=1.5,azimuth=89) sceneDict={'tilt':65, 'height':0.2, 'pitch':1.5, 'azimuth':89} scene = demo.makeScene(moduletype='test', sceneDict=sceneDict) analysis = bifacial_radiance.AnalysisObj() @@ -222,7 +225,7 @@ def test_SceneObj_makeSceneNxR_hightilt(): def test_AnalysisObj_linePtsMake3D(): # test linepts = linePtsMake3D(xstart,ystart,zstart,xinc,yinc,zinc,Nx,Ny,Nz,orient): analysis = bifacial_radiance.AnalysisObj() - linepts = analysis.linePtsMake3D(0,0,0,1,1,1,1,2,3,'0 1 0') + linepts = analysis._linePtsMake3D(0,0,0,1,1,1,1,2,3,'0 1 0') assert linepts == '0 0 0 0 1 0 \r1 1 1 0 1 0 \r0 0 0 0 1 0 \r1 1 1 0 1 0 \r0 0 0 0 1 0 \r1 1 1 0 1 0 \r' # v2.5.0 new linepts because now x and z also increase not only y. #assert linepts == '0 0 0 0 1 0 \r0 1 0 0 1 0 \r0 0 1 0 1 0 \r0 1 1 0 1 0 \r0 0 2 0 1 0 \r0 1 2 0 1 0 \r' @@ -247,7 +250,7 @@ def test_SingleModule_end_to_end(): demo = bifacial_radiance.RadianceObj(name) # Create a RadianceObj 'object' demo.setGround('litesoil') metdata = demo.readEPW(epwfile= MET_FILENAME) - demo.gendaylit(metdata,4020) # Noon, June 17th + demo.gendaylit(metdata,4020,debug=True) # 1pm, June 17th # create a scene using panels in landscape at 10 deg tilt, 1.5m pitch. 0.2 m ground clearance sceneDict = {'tilt':0,'pitch':1.5,'clearance_height':1, 'nMods':1, 'nRows':1} demo.makeModule(name='test',y=0.95,x=1.59, xgap=0) @@ -265,4 +268,5 @@ def test_SingleModule_end_to_end(): assert analysis.rearMat[0][:12] == 'a0.0.a0.test' assert analysis.x == [0] assert analysis.y == [0] - #assert np.mean(analysis.backRatio) == pytest.approx(0.12, abs = 0.01) \ No newline at end of file + assert np.mean(analysis.Wm2Front) == pytest.approx(1025, abs = 2) + assert np.mean(analysis.Wm2Back) == pytest.approx(166, abs = 6) \ No newline at end of file diff --git a/tests/test_gencumsky.py b/tests/test_gencumsky.py new file mode 100644 index 00000000..67ae0f05 --- /dev/null +++ b/tests/test_gencumsky.py @@ -0,0 +1,69 @@ +# -*- coding: utf-8 -*- +""" +Created on Fri Jul 27 10:08:25 2018 + +@author: cdeline + +Using pytest to create unit tests for gencumulativesky. +Note that this can't be included in the repo until TravisCI has a Linux version of gencumsky +set up in .travis.yml + +to run unit tests, run pytest from the command line in the bifacial_radiance directory +to run coverage tests, run py.test --cov-report term-missing --cov=bifacial_radiance + +""" + +#from bifacial_radiance import RadianceObj, SceneObj, AnalysisObj +import bifacial_radiance +import numpy as np +import pytest +import os + +# try navigating to tests directory so tests run from here. +try: + os.chdir('tests') +except: + pass + +TESTDIR = os.path.dirname(__file__) # this folder + +# test the readepw on a dummy Boulder EPW file in the /tests/ directory +MET_FILENAME = 'USA_CO_Boulder.724699_TMY2.epw' +# also test a dummy TMY3 Denver file in /tests/ +MET_FILENAME2 = "724666TYA.CSV" +DEBUG = True + +def test_SingleModule_gencumsky(): + import datetime + + # 1 module for STC conditions. DNI:900, DHI:100, sun angle: 33 elevation 0 azimuth + name = "_test_fixedtilt_end_to_end" + demo = bifacial_radiance.RadianceObj(name) # Create a RadianceObj 'object' + demo.setGround(0.62) + metdata = demo.readWeatherFile(MET_FILENAME, starttime='06_17_13', endtime='06_17_13') + demo.genCumSky() # 1p, June 17th + # create a scene using panels in landscape at 10 deg tilt, 1.5m pitch. 0.2 m ground clearance + sceneDict = {'tilt':10,'pitch':1.5,'clearance_height':0.2, 'nMods':10, 'nRows':3} + demo.makeModule(name='test',y=0.95,x=1.59, xgap=0) + scene = demo.makeScene('test',sceneDict) + octfile = demo.makeOct(demo.getfilelist()) # makeOct combines all of the ground, sky and object files into a .oct file. + analysis = bifacial_radiance.AnalysisObj(octfile, demo.name) # return an analysis object including the scan dimensions for back irradiance + (frontscan,backscan) = analysis.moduleAnalysis(scene) + analysis.analysis(octfile, demo.name, frontscan, backscan) # compare the back vs front irradiance + assert analysis.mattype[0][:12] == 'a4.1.a0.test' + assert analysis.rearMat[0][:12] == 'a4.1.a0.test' + assert np.mean(analysis.x) == pytest.approx(0) + assert np.mean(analysis.y) == pytest.approx(0) + + if DEBUG: + print(np.mean(analysis.Wm2Front)) + print(np.mean(analysis.Wm2Back)) + print(np.mean(analysis.backRatio)) + # Note: gencumsky has 30-50 Wm-2 variability from run to run... unsure why. + assert np.mean(analysis.Wm2Front) == pytest.approx(1030, abs = 60) #1023,1037,1050, 1035, 1027, 1044, 1015, 1003, 1056 + assert np.mean(analysis.Wm2Back) == pytest.approx(133, abs = 15) # 127, 131, 131, 135, 130, 139, 120, 145 + + # run 1-axis gencumsky option + trackerdict = demo.set1axis(metdata, limit_angle = 45, backtrack = True, gcr = 0.33) + demo.genCumSky1axis(trackerdict) + diff --git a/tests/test_gui.py b/tests/test_gui.py new file mode 100644 index 00000000..8688dc3b --- /dev/null +++ b/tests/test_gui.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +""" +Created on Fri Jul 27 10:08:25 2018 + +@author: cdeline + +Attempt to do some sort of test of the gui. Unsure how to do this exactly.. + +to run unit tests, run pytest from the command line in the bifacial_radiance directory +to run coverage tests, run py.test --cov-report term-missing --cov=bifacial_radiance + +""" + +from bifacial_radiance.gui import Window +import matplotlib as mpl + +def test_GuiWindow(): + mpl.use('Agg') # set up display backend + # this doesn't do much at the moment, but gives us 50% coverage of gui.py! + root = Window() + root.__init__() + + diff --git a/tests/test_load.py b/tests/test_load.py index 262e5ac8..33fab48d 100644 --- a/tests/test_load.py +++ b/tests/test_load.py @@ -63,4 +63,30 @@ def test_deepcleanResult(): resultsDict=bifacial_radiance.load.read1Result(resultfile) Frontresults, Backresults=bifacial_radiance.load.deepcleanResult(resultsDict, 110, 2, automatic=True) assert len(Frontresults) == 110 - assert Backresults[54] == pytest.approx(245.3929333333333, rel = 0.01) + assert Backresults[54] == pytest.approx(245.3929333333333, rel = 0.01) + + +def test_gh126_raise_OSError(): + """Catch OSError for any platform instead of WindowsError""" + with pytest.raises(OSError): + nopath = '/there/is/no/path' + demo = bifacial_radiance.RadianceObj(name='test', path=nopath) + + +def test_gh127_abspath(): + """RadianceObj path must be absolute""" + testpath = os.path.abspath(os.path.dirname(__file__)) + projpath = os.path.dirname(testpath) + temp_path = os.path.join(projpath, 'bifacial_radiance', 'TEMP') + demo = bifacial_radiance.RadianceObj(name='test', path=temp_path) + os.path.isabs(demo.path) + + +def test_gh130_import_tkinter(): + import tkinter + from tkinter import filedialog + + +def test_gh128_import_requests(): + import requests + diff --git a/tests/test_mismatch.py b/tests/test_mismatch.py new file mode 100644 index 00000000..b8a67bc1 --- /dev/null +++ b/tests/test_mismatch.py @@ -0,0 +1,77 @@ +# -*- coding: utf-8 -*- +""" +Created on Fri Jul 27 10:08:25 2018 + +@author: cdeline + +Using pytest to create unit tests for mismatch.py. + +to run unit tests, run pytest from the command line in the bifacial_radiance directory +to run coverage tests, run py.test --cov-report term-missing --cov=bifacial_radiance + +""" + +import bifacial_radiance +import numpy as np +import pytest +import os +import pandas as pd +# try navigating to tests directory so tests run from here. +try: + os.chdir('tests') +except: + pass + +TESTDIR = os.path.dirname(__file__) # this folder +TEST_ARRAY = np.array([[ 0, 23, 24, 47, 48, 71], + [ 1, 22, 25, 46, 49, 70], + [ 2, 21, 26, 45, 50, 69], + [ 3, 20, 27, 44, 51, 68], + [ 4, 19, 28, 43, 52, 67], + [ 5, 18, 29, 42, 53, 66], + [ 6, 17, 30, 41, 54, 65], + [ 7, 16, 31, 40, 55, 64], + [ 8, 15, 32, 39, 56, 63], + [ 9, 14, 33, 38, 57, 62], + [10, 13, 34, 37, 58, 61], + [11, 12, 35, 36, 59, 60]]) + +def test_setupforPVMismatch(): + + out = bifacial_radiance.mismatch._setupforPVMismatch( + portraitorlandscape='portrait', + sensorsy=12, + numcells=72) + np.testing.assert_array_equal(out[0], TEST_ARRAY) + + assert out[1] == 6 + assert out[2] == 12 + + +def test_MAD(): + + assert bifacial_radiance.mismatch.mad_fn(TEST_ARRAY) == \ + pytest.approx(2433.333,abs = 0.001) + + temp = bifacial_radiance.mismatch.mad_fn(pd.DataFrame(TEST_ARRAY)) + ans = pd.Series([15706.061,4936.190,2928.249,2081.526,1614.642,1318.8295]) + pd.testing.assert_series_equal(temp,ans,check_less_precise=True) +# assert temp == \ +# pytest.approx(2433.333,abs = 0.001) + +def test_analysisIrradianceandPowerMismatch(): + #analysisIrradianceandPowerMismatch(testfolder, writefiletitle, + # portraitorlandscape, bififactor, + # numcells=72, downsamplingmethod='byCenter'): + + #testfolder = r'C:\Users\cdeline\Documents\Python Scripts\Bifacial_Radiance\tests\results_mismatch' + #writefiletitle = r'C:\Users\cdeline\Documents\Python Scripts\Bifacial_Radiance\tests\mismatch.txt' + testfolder = os.path.join(TESTDIR,'results_mismatch') + writefiletitle = os.path.join(TESTDIR,'mismatch.txt') + bifacial_radiance.mismatch.analysisIrradianceandPowerMismatch(testfolder, writefiletitle, + 'portrait', bififactor=1, + numcells=72, downsamplingmethod='byCenter') + df_all = pd.read_csv(writefiletitle) + assert df_all.Mismatch_rel[0] == pytest.approx(0.376, abs = 0.001) + assert df_all["MAD/G_Total"][0] == pytest.approx(1.987, abs = 0.001) + \ No newline at end of file diff --git a/tests/test_modelchain.py b/tests/test_modelchain.py new file mode 100644 index 00000000..3a017412 --- /dev/null +++ b/tests/test_modelchain.py @@ -0,0 +1,61 @@ +# -*- coding: utf-8 -*- +""" +Created on Fri Jul 27 10:08:25 2018 + +@author: cdeline + +Using pytest to create unit tests for modelchain. + +to run unit tests, run pytest from the command line in the bifacial_radiance directory +to run coverage tests, run py.test --cov-report term-missing --cov=bifacial_radiance + +""" + +#from bifacial_radiance import RadianceObj, SceneObj, AnalysisObj +import bifacial_radiance +import bifacial_radiance.modelchain as mc +import numpy as np +import pytest +import os + +# try navigating to tests directory so tests run from here. +try: + os.chdir('tests') +except: + pass + +TESTDIR = os.path.dirname(__file__) # this folder + +# test the readepw on a dummy Boulder EPW file in the /tests/ directory +MET_FILENAME = 'USA_CO_Boulder.724699_TMY2.epw' +# also test a dummy TMY3 Denver file in /tests/ +MET_FILENAME2 = "724666TYA.CSV" + +def test_append_dicts(): + z = mc._append_dicts({'a':True},{'b':'hello'}) + assert z['a']==True + assert z['b']=='hello' + +def test_returnTimeVals(): + t = {'MonthStart':1, 'DayStart':1, 'HourStart':11, 'MonthEnd':1, + 'DayEnd':1,'HourEnd':11} + trackerdict = dict.fromkeys(['01_01_09','01_01_10','01_01_11','01_01_12']) + timelist = mc._returnTimeVals(t,trackerdict) + assert timelist == {'01_01_11'} + +def test_Radiance_high_azimuth_modelchains2(): + # duplicate next example using modelchain + # high azimuth .ini file + HIGH_AZIMUTH_INI = os.path.join(TESTDIR, "ini_highAzimuth.ini") + + (Params)= bifacial_radiance.load.readconfigurationinputfile(inifile=HIGH_AZIMUTH_INI) + Params[0]['testfolder'] = TESTDIR + Params[0]['daydateSimulation'] = True + Params[2].update({'MonthStart': 6, 'MonthEnd':6, 'DayStart':17, + 'DayEnd':17, 'HourStart':13, 'HourEnd':13}); + # change params to + # unpack the Params tuple with *Params + demo2, analysis = bifacial_radiance.modelchain.runModelChain(*Params ) + #assert np.round(np.mean(analysis.backRatio),2) == 0.20 # bifi ratio was == 0.22 in v0.2.2 + assert np.mean(analysis.Wm2Front) == pytest.approx(899, rel = 0.005) # was 912 in v0.2.3 + assert np.mean(analysis.Wm2Back) == pytest.approx(189, rel = 0.02) # was 182 in v0.2.2 diff --git a/tests/test_readepw.py b/tests/test_readepw.py deleted file mode 100644 index 46b26f8e..00000000 --- a/tests/test_readepw.py +++ /dev/null @@ -1,48 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Fri Jul 27 10:08:25 2018 - -@author: cdeline - -Using pytest to create unit tests for readepw. - -to run unit tests, run pytest from the command line in the bifacial_radiance directory - -""" - -import bifacial_radiance -import os - -# try navigating to tests directory so tests run from here. -try: - os.chdir('tests') -except: - pass - -# test the readepw on a dummy Boulder EPW file in the /tests/ directory -TESTDATA_FILENAME = 'USA_CO_Boulder.724699_TMY2.epw' - -def test_readepw_metadata(): - # Is this returning correct metadata? - (EPW_DATA, EPW_METADATA) = bifacial_radiance.readepw(filename = TESTDATA_FILENAME) # this is done outside of an assert, but maybe that's ok? - - assert EPW_METADATA == {'Name': 'BOULDER', - 'State': 'USA', - 'TZ': -7.0, - 'USAF': 724699, - 'altitude': 1634.0, - 'latitude': 40.02, - 'longitude': -105.25} - - -def test_readepw_data_length(): - # Is this returning the correct amount of data? 34 x 8760 - (EPW_DATA, EPW_METADATA) = bifacial_radiance.readepw(filename = TESTDATA_FILENAME) # this is done outside of an assert, but maybe that's ok? - assert EPW_DATA.__len__() == 8760 - assert EPW_DATA.columns.__len__() == 34 - -def test_readepw_data_values(): - # Is this returning the correct data maxima? - (EPW_DATA, EPW_METADATA) = bifacial_radiance.readepw(filename = TESTDATA_FILENAME) # this is done outside of an assert, but maybe that's ok? - assert EPW_DATA['Dry bulb temperature in C'].max() == 36.7 - assert EPW_DATA['Global horizontal radiation in Wh/m2'].max() == 1029 \ No newline at end of file