Rustic Retreat
Hot Projects
Live broadcasts and documentation from a remote tech outpost in rustic Portugal. Sharing off-grid life, the necessary research & development and the pursuit of life, without centralized infrastructure.
This is an old revision of the document!
Using the sun as a sustainable energy source isn't really a new invention. Plants have been relying on it for millions of years and have developed and optimized the process of photosynthesis over a very long bio-evolutionary period. Almost everybody appreciates the comfort, when it's warm and the sun is shining but we very often forget how hard our ability to actually survive as a species, is linked to solar output:
Natural interdependently balanced processes
Global freshwater distribution | Oceans → Evaporate (Desalinize) → Clouds → Rain |
Global atmospheric conditions (Weather) → | Flora/Fauna (direct) |
Global Flora → | Food for Fauna (direct) |
Global atmospheric conditions (Weather) (indirect) |
Technical processes
Usage | Technology | Result |
---|---|---|
Industrial Agriculture | Photosynthesis | Energy (Food) |
Solar Heating | Mirror/Focus/Transfer | Energy (Thermal) |
Photovoltaic | Direct, solid-state Photon to Electron conversion | Energy (Electricity) |
Therefore knowledge about global solar radiation (Rs) is of fundamental importance for human life on earth. We're depending very much on knowing how much solar energy can be harvested on a given point on our planet's surface. Yet we still commonly refer and are taught to use 1000 W/m2 on any point on Earth, as a clear-sky reference value. Even the Watt-Peak value, PV-Panel manufacturers put into their datasheets, is virtually always based only on 1000 W/m2. But how do we calculate the output we may generate with a given panel surface, if we don't know what our clear-sky (Rs)max for a specific location/time will be?
The UCSSPM is an open-source clear-sky prediction model, incorporating math algorithms based on latest research by the Environmental and Water Resources Institute of the American Society of Civil Engineers and a few veteran but still valid and publicly available NOAA/NASA computations and some revised research & assumptions regarding commonly used constants.
With help of this open-source model, anyone can now easily estimate the maximum global solar radiation value on a clear-sky day for any given time and place on Earth - to predict the maximum usable output a given conversion process (currently only PV) may yield. This enables us to plan, calculate, dimension, optimize and control/verify any solar energy conversion system with base data that is as accurate and reliable as possible.
Long term PV (live & UCSSPM) metrics are collected and accessible on these VFCC dashboards:
The system can be easily extended to estimate the optimum parabolic oven-reflector size, to satisfy the energy needs for a given project and the specific position on the planet.
Possibility to calibrate a Pyranometer in the field, without another calibrated reference, on a clear-sky day.
Usable as basis for open agricultural applications (growth/photosynthetic calculations)
What started out of the necessity to calculate and verify the solar power requirements and project feasibility of Apollo-NG itself, has become an advanced clear-sky prediction model, implemented in python without any further dependency, incorporating the following factors:
#!/usr/bin/env python2 # -*- coding: UTF-8 -*- ################################################################################ # # @file ucsspm.py # @authors chrono # @version V1.0.3 (Argument Tamer) # @date 2014-11-15 # @brief Unified Clear-Sky Solar output Prediction Model (UCSSPM) # @status Beta - Request for Comment, Re-Verification & Enhancement # ################################################################################ # Copyright (c) 2014 Apollo-NG - https://apollo.open-resource.org/ ################################################################################ # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. # ################################################################################ import sys, argparse, math, time, calendar ################################################################################ ## Inputs & Defaults ######################################################### ################################################################################ def options(arg): arg.add_argument( "-v", "--verbose" ,\ action = "store_true" ,\ help = "Verbose output" ) # Decreased Solar Constant - See docs/solar-constant.pdf for update info. ## # Default value of 1361.0 should IMHO serve as a good average point # between the min/max values over the 11-year sun cycle. arg.add_argument( "-sc" ,\ type = float ,\ help = "Solar Constant (@1AU) in kW/m² [Default: 1361.0]" ,\ default = 1361.0 ) # Space/Time Pinpointing ################################################### arg.add_argument( "-lat" ,\ type = float ,\ help = "Latitude in decimal degrees [Default: 48.0]" ,\ default = 48.00000 ) arg.add_argument( "-lon" ,\ type = float ,\ help = "Longitude in decimal degrees [Default: 11.0]" ,\ default = 11.00000 ) # Optional, only needed if barometric pressure not available to compute it. # If no value is supplied to either, an altitude of 0m (NN) will be default # Obviously, this is only a fallback and using the actual barometric pressure # should always be preferred to yield a less averagish result. arg.add_argument( "-alt" ,\ type = float ,\ help = "Altitude in meters above NN [Default: 0]" ,\ default = 0 ) arg.add_argument( "-date" ,\ type = str ,\ help = "ISO Date YYYY-MM-DD [Default: " \ + time.strftime("%Y-%m-%d") + "]" ,\ default = time.strftime("%Y-%m-%d") ) arg.add_argument( "-time" ,\ type = str ,\ help = "ISO Time HH:MM:SS [Default: " \ + time.strftime("%H:%M:%S") + "]" ,\ default = time.strftime("%H:%M:%S") ) # Environmental Conditions ################################################# arg.add_argument( "-at_t" ,\ type = float ,\ help = "Atmospheric Temperature in °C [Default: 25.0]" ,\ default = 25.0 ) arg.add_argument( "-at_h" ,\ type = float ,\ help = "Atmospheric Relative Humidity in Percent [Default: 50]" ,\ default = 50.0 ) # Can be optional by submitting altitude - but will be less precise then ### arg.add_argument( "-at_p" ,\ type = float ,\ help = "Atmospheric Air Pressure in hPa [Default: Computed]" ) arg.add_argument( "-at_tc" ,\ type = float ,\ help = "Atmospheric Turbidity Coefficient [Default: 0.95]" ,\ default = 0.95 ) # Photovoltaic Parameters ################################################## arg.add_argument( "-pv_a" ,\ type = float ,\ help = "Effective PV Panel Surface in m² [Default: 1.67]" ,\ default = 1.67 ) arg.add_argument( "-pv_e" ,\ type = float ,\ help = "PV Panel Efficiency in Percent [Default: 16]" ,\ default = 20 ) arg.add_argument( "-pv_t" ,\ type = float ,\ help = "PV Panel Temperature in °C [Default: 25.0]" ,\ default = 25.0 ) arg.add_argument( "-pv_tc" ,\ type = float ,\ help = "PV Panel negative Temp. Coefficient [Default: 0.35]" ,\ default = 0.35 ) arg.add_argument( "-pv_ac" ,\ type = float ,\ help = "PV Panel age related Coefficient [Default: 0.98]" ,\ default = 0.98 ) ################################################################################ ## Outputs ################################################################### ################################################################################ def output(opt,res): if res['sol_z'] > 90: if opt.verbose: print "The sun has set - no data" return 0 else: print "0.0|0.0|90.0|0.0|0.0" return 0 elif not opt.verbose: print "%.1f|%.1f|%.1f|%.1f|%.1f" % \ ( \ res['ETR'], res['RSO'], res['sol_z'] ,\ res['pv_max'], res['pv_out'] \ ) return 0 else: print "--------+--------------------------------------------------------" print " UCSSPM | Clear-Sky Prediction for %s @ %s" % (opt.date, opt.time ) print "--------+--------------------------------------------------------" print " Solar Constant : %s kW/m² @ 1AU" % opt.sc print " Atmospheric turbidity coefficient : %s" % opt.at_tc print "-----------------------------------------------------------------" print " Equation of time : %s min" % res['eqt'] print " Inverse relative distance factor : %s" % res['sol_r'] print " Sun declination : %s°" % res['sol_d'] print " Solar Noon : %s " % res['sol_n'] print " Barometric Pressure at site : %s kPa" % opt.at_p print " Estimated Vapor Pressure at site : %s kPa" % res['at_vp'] print " Estimated Extraterrestrial Radiation : %s W/m²" % res['ETR'] print " Estimated precipitable water in Atmosphere : %s mm" % res['at_pw'] print " Clearness index for direct beam radiation : %s" % res['CIDBR'] print " Transmissivity index for diffuse radiation : %s" % res['TIDR'] print "-----------------------------------------------------------------" print " Estimated Max. global solar radiation (Rs) : \033[1;33m%3.1f W/m²\033[0m" % res['RSO'] print "-----------------------------------------------------------------" print " Optimum Elevation of PV-Panel : \033[1;37m%02.1f°\033[0m" % res['sol_z'] print " Estimated Max. Clear-Sky PV-Power Output : \033[1;32m%3.1f W\033[0m \033[1;37m@ %d%% Peff\033[0m" % (res['pv_max'], opt.pv_e) if res['pv_lp'] >= 0: print " PV-Panel temperature (%2.1f °C) compensation - \033[1;31m%2.1f W / %2.1f%%\033[0m" % (opt.pv_t, res['pv_lp'] , res['pv_l'] ) else: print " PV-Panel temperature (%2.1f °C) compensation + \033[1;32m%2.1f W / %2.1f%%\033[0m" % (opt.pv_t, res['pv_lp']*-1 , res['pv_l']*-1 ) print " PV-Panel aging loss - \033[1;31m%03.1f W\033[0m" % res['pv_la'] print "-----------------------------------------------------------------" print " Compensated Max. Clear-Sky PV-Power Output : \033[1;32m%3.1f W\033[0m" % res['pv_out'] return 0 ################################################################################ ## MAIN ###################################################################### ################################################################################ def main(): arg = argparse.ArgumentParser() options (arg) opt = arg.parse_args() parse_d = opt.date.split("-") opt.year = int(parse_d[0]) opt.month = int(parse_d[1]) opt.day = int(parse_d[2]) parse_t = opt.time.split(":") opt.hour = int(parse_t[0]) opt.min = int(parse_t[1]) opt.sec = int(parse_t[2]) dst_off = 0 tz_off_deg = 0 + opt.lon res = {} # Compute Julian Day (Day of Year) ######################################### if calendar.isleap(opt.year): # Leap year (366 days) lMonth = [0,31,60,91,121,152,182,213,244,274,305,335,366] else: # Normal year (365 days) lMonth = [0,31,59,90,120,151,181,212,243,273,304,334,365] res['DoY'] = lMonth[opt.month - 1] + opt.day res['ToD'] = float(opt.hour + (opt.min/60.0) + (opt.sec/3600.0)) # Solve equation of time ################################################### # (More info on http://www.srrb.noaa.gov/highlights/sunrise/azel.html) res['eqt'] = (((5.0323-(430.847*math.cos((((2*math.pi)*res['DoY'])/366)+4.8718)))\ + (12.5024*(math.cos(2*((((2*math.pi)*res['DoY'])/366)+4.8718))))\ + (18.25*(math.cos(3*((((2*math.pi)*res['DoY'])/366)+4.8718))))\ - (100.976*(math.sin((((2*math.pi)*res['DoY'])/366)+4.8718))))\ + (595.275*(math.sin(2*((((2*math.pi)*res['DoY'])/366)+4.8718))))\ + (3.6858*(math.sin(3*((((2*math.pi)*res['DoY'])/366)+4.871))))\ - (12.47*(math.sin(4*((((2*math.pi)*res['DoY'])/366)+4.8718)))))\ / 60 # Compute inverse relative distance factor (Distance between Earth and Sun) res['sol_r'] = 1.0 / (1.0 - 9.464e-4 * math.sin(res['DoY']) \ - 0.01671 * math.cos(res['DoY']) \ - 1.489e-4 * math.cos(2.0 * res['DoY']) \ - 2.917e-5 * math.sin(3.0 * res['DoY']) \ - 3.438e-4 * math.cos(4.0 * res['DoY'])) ** 2 # Compute solar declination ################################################ res['sol_d'] = (math.asin(0.39785 * (math.sin(((278.97 \ + (0.9856 * res['DoY'])) + (1.9165 \ * (math.sin((356.6 + (0.9856 * res['DoY'])) \ * (math.pi / 180))))) * (math.pi / 180)))) * 180) / math.pi # Compute time of solar noon ########################################### res['sol_n'] = ((12 + dst_off) - (res['eqt'] / 60)) \ - ((tz_off_deg - opt.lon) / 15) # Compute solar zenith angle in DEG #################################### res['sol_z'] = math.acos(((math.sin(opt.lat * (math.pi / 180))) \ * (math.sin(res['sol_d'] * (math.pi / 180)))) \ + (((math.cos(opt.lat * ((math.pi / 180)))) \ * (math.cos(res['sol_d'] * (math.pi / 180)))) \ * (math.cos((res['ToD'] - res['sol_n']) \ * (math.pi /12))))) * (180/math.pi) # A solar zenith angle value of > 90 usually indicates that the sun has set # (from observer's perspective at the given location for this computation). # However, in extreme latitudes, valid values over 90 may occur. If you live # in such a place and happen to stumble upon this code, please report back # when you use it so we can find a better fix for this than the follwing hack. # Unfortunately, if we don't fail safely here, we are confronted with some # nasty division by zero business further on, so... if res['sol_z'] > 90: output (opt, res) sys.exit (0) # Barometric pressure at site ############################################## # (this should be replaced by the real measured value) in kPa if opt.at_p: # Real value given, convert hPa to kPa opt.at_p = opt.at_p / 10 else: # Estimate Pressure from given altitude opt.at_p = math.pow(((288 - (0.0065 * (opt.alt - 0))) / 288), \ (9.80665 / (0.0065 * 287))) * 101.325 # Estimate air vapor pressure in kPa ####################################### res['at_vp'] = (0.61121 * math.exp((17.502 * opt.at_t) \ / (240.97 + opt.at_t))) \ * (opt.at_h / 100) # Extraterrestrial radiation in W/m2 ####################################### res['ETR'] = (opt.sc * res['sol_r']) \ * (math.cos(res['sol_z'] * (math.pi / 180))) # Precipitable water in the atmosphere in mm ############################### res['at_pw'] = ((0.14 * res['at_vp']) * opt.at_p) + 2.1 # Clearness index for direct beam radiation [unitless] ##################### res['CIDBR'] = 0.98 * (math.exp(((-0.00146 * opt.at_p) \ / (opt.at_tc * (math.sin((90 - res['sol_z']) \ * (math.pi / 180))))) - (0.075 * (math.pow((res['at_pw'] \ / (math.sin((90 - res['sol_z']) * (math.pi / 180)))),0.4))))) # Transmissivity index for diffuse radiation [unitless] #################### if (res['CIDBR'] > 0.15): res['TIDR'] = 0.35 - (0.36 * res['CIDBR']) else: res['TIDR'] = 0.18 + (0.82 * res['CIDBR']) # Model Estimated Shortwave Radiation (W/m2) ############################### res['RSO'] = (res['CIDBR'] + res['TIDR']) * res['ETR'] # Estimate Theoretical Max. Power Output (Panel at nominal Efficiency) ##### res['pv_max'] = (res['RSO'] * opt.pv_a) / 100 * opt.pv_e # Estimate conversion loss due to module temperature ####################### res['pv_l'] = (opt.pv_t-25 ) * opt.pv_tc res['pv_lp'] = (res['pv_max'] / 100) * res['pv_l'] # Estimate conversion loss due to module age res['pv_la'] = res['pv_max'] - (res['pv_max'] * opt.pv_ac) # Estimate final System Power output res['pv_out'] = res['pv_max'] - res['pv_la'] - res['pv_lp'] output (opt, res) ################################################################################ if __name__ == '__main__': rc = main() sys.exit (rc)
This should work on any operating system with Python 2.7 installed. Other python versions haven't been tested yet.
You can either clone the whole repo with documentation with
$ git clone https://github.com/apollo-ng/UCSSPM.git $ cd UCSSPM
or just download the script itself
$ wget https://raw.githubusercontent.com/apollo-ng/UCSSPM/master/ucsspm.py
$ ./ucsspm.py -v -pv_t 16 -at_t 9.3 -at_p 945.5 -at_h 81 --------+-------------------------------------------------------- UCSSPM | Clear-Sky Prediction for 2014-11-15 @ 13:31:36 --------+-------------------------------------------------------- Solar Constant : 1361.0 kW/m² @ 1AU Atmospheric turbidity coefficient : 0.95 ----------------------------------------------------------------- Equation of time : 15.6165056158 min Inverse relative distance factor : 1.00277104587 Sun declination : -18.2528587° Solar Noon : 11.7397249064 Barometric Pressure at site : 94.55 kPa Estimated Vapor Pressure at site : 0.948698993906 kPa Estimated Extraterrestrial Radiation : 456.410564923 W/m² Estimated precipitable water in Atmosphere : 14.6579285823 mm Clearness index for direct beam radiation : 0.451609480011 Transmissivity index for diffuse radiation : 0.187420587196 ----------------------------------------------------------------- Estimated Max. global solar radiation (Rs) : 291.7 W/m² ----------------------------------------------------------------- Optimum Elevation of PV-Panel : 70.5° Estimated Max. Clear-Sky PV-Power Output : 97.4 W @ 20% Peff PV-Panel temperature (16.0 °C) compensation + 3.1 W / 3.1% PV-Panel aging loss - 1.9 W ----------------------------------------------------------------- Compensated Max. Clear-Sky PV-Power Output : 98.5 W
Anyone interested is of course also invited to download the software and play/use/verify/optimize as well. Feedback, PR's and everything else that might increase precision/usability are always welcome: