From 6ea5aca2e354dfec980201e84c7812a305c5eb25 Mon Sep 17 00:00:00 2001 From: Mark Mikofski Date: Wed, 22 May 2019 20:35:34 -0700 Subject: [PATCH] Get solpos8760 - and version 0.3 (Carpenters) (#14) * ENH: loop inside solar_utils * ENH:TST: test 8760 method * ENH: return angles and airmass separately * ENH: collapse reassignments, use inplace * TST:ENH: test errors, output all error codes * ENH: any size datetime array * combine get_solposAM with get_solpos8760, add get specified year function --- CHANGES.rst | 3 ++ setup.cfg | 2 +- solar_utils/__init__.py | 12 ++--- solar_utils/core.py | 92 ++++++++++++++++++++++++++++++++- solar_utils/docs/conf.py | 4 ++ solar_utils/docs/core.rst | 8 +++ solar_utils/src/solposAM.c | 49 ++++++++++++------ solar_utils/tests/test_cdlls.py | 92 +++++++++++++++++++++++++++++++-- 8 files changed, 233 insertions(+), 29 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 3980345..70edf63 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,6 +1,9 @@ Changes ======= +For more recent changes, please see +`GitHub Releases `_. + v0.2.1 - Bartenders (2016-07-11) -------------------------------- * add documentation diff --git a/setup.cfg b/setup.cfg index a67b5c7..12d528c 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,3 +1,3 @@ # content of setup.cfg -[pytest] +[tool:pytest] norecursedirs = .git build dist venv diff --git a/solar_utils/__init__.py b/solar_utils/__init__.py index 5eb802c..777fbc4 100644 --- a/solar_utils/__init__.py +++ b/solar_utils/__init__.py @@ -2,16 +2,14 @@ """ This is the solar utilities package. -2013 SunPower Corp. -Confidential & Proprietary -Do Not Distribute +2013, 2019 SunPower Corp. """ -from solar_utils.core import solposAM, spectrl2 +from solar_utils.core import solposAM, spectrl2, get_solpos8760, get_solposAM -__version__ = '0.2.4' -__release__ = 'Beetroot' +__version__ = '0.3' +__release__ = 'Carpenters' __author__ = 'Mark Mikofski' __email__ = 'mark.mikofski@sunpowercorp.com' __url__ = 'https://github.com/SunPower/SolarUtils' -__all__ = ['solposAM', 'spectrl2'] +__all__ = ['solposAM', 'spectrl2', 'get_solpos8760', 'get_solposAM'] diff --git a/solar_utils/core.py b/solar_utils/core.py index 6469f57..f90c336 100644 --- a/solar_utils/core.py +++ b/solar_utils/core.py @@ -6,10 +6,11 @@ `NREL RREDC Solar Resource Models and Tools `_ -2013 SunPower Corp. +2013, 2019 SunPower Corp. """ import ctypes +import datetime as pydatetime import math import os import sys @@ -43,6 +44,95 @@ def _int2bits(err_code): return int(math.log(err_code, 2)) +def get_solpos8760(location, year, weather): + """ + Get SOLPOS hourly calculation for specified non-leap year. + + :param location: [latitude, longitude, UTC-timezone] + :type location: float + :param year: a non-leap year + :type year: int + :param weather: [ambient-pressure (mB), ambient-temperature (C)] + :type weather: float + :returns: angles [degrees], airmass [atm] + :rtype: float + :raises: :exc:`~solar_utils.exceptions.SOLPOS_Error` + + **Example:** + + >>> location = [35.56836, -119.2022, -8.0] + >>> weather = [1015.62055, 40.0] + >>> angles, airmass = get_solpos8760(location, 2013, weather) + """ + datetimes = [ + (pydatetime.datetime(year, 1, 1, 0, 0, 0) + + pydatetime.timedelta(hours=h)).timetuple()[:6] + for h in range(8760)] + return get_solposAM(location, datetimes, weather) + + +def get_solposAM(location, datetimes, weather): + """ + Get SOLPOS hourly calculation for sequence of datetimes. + + :param location: [latitude, longitude, UTC-timezone] + :type location: float + :param datetimes: [year, month, day, hour, minute, second] + :type datetimes: int + :param weather: [ambient-pressure (mB), ambient-temperature (C)] + :type weather: float + :returns: angles [degrees], airmass [atm] + :rtype: float + :raises: :exc:`~solar_utils.exceptions.SOLPOS_Error` + + **Example:** + + >>> location = [35.56836, -119.2022, -8.0] + >>> datetimes = [ + ... (datetime.datetime(2013, 1, 1, 0, 0, 0) + ... + datetime.timedelta(hours=h)).timetuple()[:6] + ... for h in range(1000)] + >>> weather = [1015.62055, 40.0] + >>> angles, airmass = get_solposAM(location, datetimes, weather) + """ + count = len(datetimes) + # load the DLL + solposAM_dll = ctypes.cdll.LoadLibrary(SOLPOSAMDLL) + _get_solposAM = solposAM_dll.get_solposAM + # cast Python types as ctypes + _location = (ctypes.c_float * 3)(*location) + _datetime = ((ctypes.c_int * 6) * count)(*datetimes) + _weather = (ctypes.c_float * 2)(*weather) + # allocate space for results + angles = ((ctypes.c_float * 2) * count)() + airmass = ((ctypes.c_float * 2) * count)() + settings = ((ctypes.c_int * 2) * count)() + orientation = ((ctypes.c_float * 2) * count)() + shadowband = ((ctypes.c_float * 3) * count)() + err_code = (ctypes.c_long * count)() + # call + retval = _get_solposAM( + _location, _datetime, _weather, count, angles, airmass, settings, + orientation, shadowband, err_code) + if (retval != 0): raise RuntimeError('solposAM did not execute') + if all(ec == 0 for ec in err_code): + return angles, airmass + else: + for n, ec in enumerate(err_code): + if ec == 0: continue + # convert err_code to bits + _code = _int2bits(ec) + data = {'location': location, + 'datetime': datetimes[n], + 'weather': weather, + 'angles': angles[n], + 'airmass': airmass[n], + 'settings': settings[n], + 'orientation': orientation[n], + 'shadowband': shadowband[n]} + raise SOLPOS_Error(_code, data) + + def solposAM(location, datetime, weather): """ Calculate solar position and air mass by calling functions exported by diff --git a/solar_utils/docs/conf.py b/solar_utils/docs/conf.py index 0fc3ad3..b4c1225 100644 --- a/solar_utils/docs/conf.py +++ b/solar_utils/docs/conf.py @@ -12,9 +12,13 @@ # All configuration values have a default; values that are commented out # serve to show the default. +from __future__ import unicode_literals, absolute_import, division import sys import os +if sys.version_info[0] >= 3: + unicode = str + # 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. diff --git a/solar_utils/docs/core.rst b/solar_utils/docs/core.rst index 06fe92f..99a4db3 100644 --- a/solar_utils/docs/core.rst +++ b/solar_utils/docs/core.rst @@ -30,6 +30,14 @@ that imports :data:`SOLPOSAMDLL` and exports functions called by .. data:: SPECTRL2DLL +get_solpos8760 +-------------- +.. autofunction:: get_solpos8760 + +get_solposAM +------------ +.. autofunction:: get_solposAM + solposAM -------- .. autofunction:: solposAM diff --git a/solar_utils/src/solposAM.c b/solar_utils/src/solposAM.c index 20666ba..3710703 100644 --- a/solar_utils/src/solposAM.c +++ b/solar_utils/src/solposAM.c @@ -1,6 +1,4 @@ -// 2013 Sunpower Corp. -// Confidential & Proprietary -// Do Not Distribute +// 2013, 2019 Sunpower Corp. // include C Standard Library headers #include @@ -19,20 +17,24 @@ #define DllExport #endif -// solposAM -// Inputs: -// location: (float*) [longitude, latitude, UTC-timezone] -// datetime: (int*) [year, month, day, hour, minute, second] -// weather: (float*) [ambient-pressure (mBar), ambient-temperature (C)] -// Outputs: -// angles: (float*) [refracted-zenith, azimuth] -// airmass: (float*) [airmass (atmos), pressure-adjusted-airmass (atmos)] -// settings: (int*): [daynum, interval] -// orientation: (float*) [tilt, aspect] (degrees) -// shadowband: (float*): [width, radiation, sky] -DllExport long solposAM( float *location, int *datetime, float *weather, - float *angles, float *airmass, int *settings, float *orientation, - float *shadowband ) +/* NREL SOLPOS solar position algorithm + * + * Parameters + * ---------- + * location: (float*) [longitude, latitude, UTC-timezone] + * datetime: (int*) [year, month, day, hour, minute, second] + * weather: (float*) [ambient-pressure (mBar), ambient-temperature (C)] + * Returns + * ------- + * angles: (float*) [refracted-zenith, azimuth] + * airmass: (float*) [airmass (atmos), pressure-adjusted-airmass (atmos)] + * settings: (int*): [daynum, interval] + * orientation: (float*) [tilt, aspect] (degrees) + * shadowband: (float*): [width, radiation, sky] + */ +DllExport long solposAM( float location[3], int datetime[6], float weather[2], + float angles[2], float airmass[2], int settings[2], float orientation[2], + float shadowband[3] ) { /* variable declarations */ @@ -102,3 +104,16 @@ DllExport long solposAM( float *location, int *datetime, float *weather, return retval; } + +// get_solposAM +DllExport long get_solposAM( float location[3], int datetimes[][6], + float weather[2], int cnt, float angles[][2], float airmass[][2], + int settings[][2], float orientation[][2], float shadowband[][3], + long err_code[]) +{ + for (size_t i=0; i