SolarUtils 0.4.8__cp313-cp313-win_amd64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,60 @@
1
+ Disclaimer
2
+ The National Renewable Energy Laboratory (NREL) is operated for the U.S.
3
+ Department of Energy by the Alliance for Sustainable Energy, LLC
4
+ ("Alliance"). As such the following rules apply:
5
+
6
+ Copyright Status
7
+ NREL-authored documents are sponsored by the U.S. Department of Energy under
8
+ Contract DE-AC36-08GO28308. Accordingly, with respect to such documents, the
9
+ U.S. Government and others acting on its behalf retain a paid-up
10
+ nonexclusive, irrevocable world-wide license to reproduce, prepare derivative
11
+ works, distribute copies to the public, and perform publicly and display
12
+ publicly, by or on behalf of the Government. Use of documents available from
13
+ or referenced by this server may be subject to U.S. and foreign Copyright Laws.
14
+
15
+ Disclaimer of Endorsement
16
+ Reference herein to any specific commercial products, process, or service by
17
+ trade name, trademark, manufacturer, or otherwise, does not necessarily
18
+ constitute or imply its endorsement, recommendation, or favoring by the
19
+ United States Government or Alliance . The views and opinions of authors
20
+ expressed in the available or referenced documents do not necessarily state
21
+ or reflect those of the United States Government or Alliance.
22
+
23
+ Disclaimer of Liability
24
+ WITH RESPECT TO INFORMATION FROM THIS SERVER, NEITHER THE UNITED STATES
25
+ GOVERNMENT NOR ALLIANCE, NOR ANY OF THEIR EMPLOYEES, MAKES ANY WARRANTY,
26
+ EXPRESS OR IMPLIED, INCLUDING THE WARRANTIES OF MERCHANTABILITY AND FITNESS
27
+ FOR A PARTICULAR PURPOSE, OR ASSUMES ANY LEGAL LIABILITY OR RESPONSIBILITY
28
+ FOR THE ACCURACY, COMPLETENESS, OR USEFULNESS OF ANY SUCH INFORMATION, OR OF
29
+ ANY APPARATUS, PRODUCT, OR PROCESS DISCLOSED, OR REPRESENTS THAT ITS USE
30
+ WOULD NOT INFRINGE PRIVATELY OWNED RIGHTS.
31
+
32
+ Data and Software
33
+ Access to or use of any data or software made available on this server
34
+ ("Data") shall impose the following obligations on the user, and use of the
35
+ Data constitutes user's agreement to these terms. The user is granted the
36
+ right, without any fee or cost, to use or copy the Data, provided that this
37
+ entire notice appears in all copies of the Data. Further, the user agrees to
38
+ credit the U.S. Department of Energy (DOE)/NREL/ALLIANCE in any publication
39
+ that results from the use of the Data. The names DOE/NREL/ALLIANCE, however,
40
+ may not be used in any advertising or publicity to endorse or promote any
41
+ products or commercial entities unless specific written permission is
42
+ obtained from DOE/NREL/ ALLIANCE. The user also understands that
43
+ DOE/NREL/ALLIANCE are not obligated to provide the user with any support,
44
+ consulting, training or assistance of any kind with regard to the use of the
45
+ Data or to provide the user with any updates, revisions or new versions
46
+ thereof. DOE, NREL, and ALLIANCE do not guarantee or endorse any results
47
+ generated by use of the Data, and user is entirely responsible for the
48
+ results and any reliance on the results or the Data in general.
49
+
50
+ USER AGREES TO INDEMNIFY DOE/NREL/ALLIANCE AND ITS SUBSIDIARIES, AFFILIATES,
51
+ OFFICERS, AGENTS, AND EMPLOYEES AGAINST ANY CLAIM OR DEMAND, INCLUDING
52
+ REASONABLE ATTORNEYS' FEES, RELATED TO USER’S USE OF THE DATA. THE DATA ARE
53
+ PROVIDED BY DOE/NREL/ALLIANCE "AS IS," AND ANY EXPRESS OR IMPLIED WARRANTIES,
54
+ INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
55
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
56
+ DOE/NREL/ALLIANCE BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
57
+ DAMAGES OR ANY DAMAGES WHATSOEVER, INCLUDING BUT NOT LIMITED TO CLAIMS
58
+ ASSOCIATED WITH THE LOSS OF DATA OR PROFITS, THAT MAY RESULT FROM AN ACTION
59
+ IN CONTRACT, NEGLIGENCE OR OTHER TORTIOUS CLAIM THAT ARISES OUT OF OR IN
60
+ CONNECTION WITH THE ACCESS, USE OR PERFORMANCE OF THE DATA.
@@ -0,0 +1,15 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ This is the solar utilities package.
4
+
5
+ 2026 Mark Mikofski
6
+ """
7
+
8
+ from solar_utils.core import solposAM, spectrl2, get_solpos8760, get_solposAM
9
+
10
+ __version__ = '0.4.8'
11
+ __release__ = 'Dentists'
12
+ __author__ = 'Mark Mikofski'
13
+ __email__ = 'bwana.marko@yahoo.com'
14
+ __url__ = 'https://github.com/mikofski/SolarUtils'
15
+ __all__ = ['solposAM', 'spectrl2', 'get_solpos8760', 'get_solposAM']
solar_utils/core.py ADDED
@@ -0,0 +1,301 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ Solar utilities based on `SOLPOS
4
+ <http://rredc.nrel.gov/solar/codesandalgorithms/solpos/>`_ and `SPECTRL2
5
+ <http://rredc.nrel.gov/solar/models/spectral/>`_ from
6
+ `NREL RREDC Solar Resource Models and Tools
7
+ <http://www.nrel.gov/rredc/models_tools.html>`_
8
+
9
+ 2026 Mark Mikofski
10
+ """
11
+
12
+ import ctypes
13
+ import datetime as pydatetime
14
+ import math
15
+ import os
16
+ import sys
17
+ from solar_utils.exceptions import SOLPOS_Error, SPECTRL2_Error
18
+
19
+ _DIRNAME = os.path.dirname(__file__)
20
+ PLATFORM = sys.platform
21
+ if PLATFORM == 'win32':
22
+ SOLPOSAM = 'solposAM.dll'
23
+ SPECTRL2 = 'spectrl2.dll'
24
+ elif PLATFORM in ['linux2', 'linux']:
25
+ PLATFORM = 'linux'
26
+ SOLPOSAM = 'libsolposAM.so'
27
+ SPECTRL2 = 'libspectrl2.so'
28
+ elif PLATFORM == 'darwin':
29
+ SOLPOSAM = 'libsolposAM.dylib'
30
+ SPECTRL2 = 'libspectrl2.dylib'
31
+ else:
32
+ raise OSError('Platform "%s" is unknown or unsupported.' % PLATFORM)
33
+ SOLPOSAMDLL = os.path.join(_DIRNAME, SOLPOSAM)
34
+ SPECTRL2DLL = os.path.join(_DIRNAME, SPECTRL2)
35
+
36
+
37
+ def _int2bits(err_code):
38
+ """
39
+ Convert integer to bits.
40
+
41
+ :param err_code: integer to convert
42
+ :returns: log(err_code, 2)
43
+ """
44
+ return int(math.log(err_code, 2))
45
+
46
+
47
+ def get_solpos8760(location, year, weather):
48
+ """
49
+ Get SOLPOS hourly calculation for specified non-leap year.
50
+
51
+ :param location: [latitude, longitude, UTC-timezone]
52
+ :type location: float
53
+ :param year: a non-leap year
54
+ :type year: int
55
+ :param weather: [ambient-pressure (mB), ambient-temperature (C)]
56
+ :type weather: float
57
+ :returns: angles [degrees], airmass [atm]
58
+ :rtype: float
59
+ :raises: :exc:`~solar_utils.exceptions.SOLPOS_Error`
60
+
61
+ **Example:**
62
+
63
+ >>> location = [35.56836, -119.2022, -8.0]
64
+ >>> weather = [1015.62055, 40.0]
65
+ >>> angles, airmass = get_solpos8760(location, 2013, weather)
66
+ """
67
+ datetimes = [
68
+ (pydatetime.datetime(year, 1, 1, 0, 0, 0)
69
+ + pydatetime.timedelta(hours=h)).timetuple()[:6]
70
+ for h in range(8760)]
71
+ return get_solposAM(location, datetimes, weather)
72
+
73
+
74
+ def get_solposAM(location, datetimes, weather):
75
+ """
76
+ Get SOLPOS hourly calculation for sequence of datetimes.
77
+
78
+ :param location: [latitude, longitude, UTC-timezone]
79
+ :type location: float
80
+ :param datetimes: [year, month, day, hour, minute, second]
81
+ :type datetimes: int
82
+ :param weather: [ambient-pressure (mB), ambient-temperature (C)]
83
+ :type weather: float
84
+ :returns: angles [degrees], airmass [atm]
85
+ :rtype: float
86
+ :raises: :exc:`~solar_utils.exceptions.SOLPOS_Error`
87
+
88
+ **Example:**
89
+
90
+ >>> location = [35.56836, -119.2022, -8.0]
91
+ >>> datetimes = [
92
+ ... (datetime.datetime(2013, 1, 1, 0, 0, 0)
93
+ ... + datetime.timedelta(hours=h)).timetuple()[:6]
94
+ ... for h in range(1000)]
95
+ >>> weather = [1015.62055, 40.0]
96
+ >>> angles, airmass = get_solposAM(location, datetimes, weather)
97
+ """
98
+ count = len(datetimes)
99
+ # load the DLL
100
+ solposAM_dll = ctypes.cdll.LoadLibrary(SOLPOSAMDLL)
101
+ _get_solposAM = solposAM_dll.get_solposAM
102
+ # cast Python types as ctypes
103
+ _location = (ctypes.c_float * 3)(*location)
104
+ _datetime = ((ctypes.c_int * 6) * count)(*datetimes)
105
+ _weather = (ctypes.c_float * 2)(*weather)
106
+ # allocate space for results
107
+ angles = ((ctypes.c_float * 2) * count)()
108
+ airmass = ((ctypes.c_float * 2) * count)()
109
+ settings = ((ctypes.c_int * 2) * count)()
110
+ orientation = ((ctypes.c_float * 2) * count)()
111
+ shadowband = ((ctypes.c_float * 3) * count)()
112
+ err_code = (ctypes.c_long * count)()
113
+ # call
114
+ retval = _get_solposAM(
115
+ _location, _datetime, _weather, count, angles, airmass, settings,
116
+ orientation, shadowband, err_code)
117
+ if (retval != 0): raise RuntimeError('solposAM did not execute')
118
+ if all(ec == 0 for ec in err_code):
119
+ return angles, airmass
120
+ else:
121
+ for n, ec in enumerate(err_code):
122
+ if ec == 0: continue
123
+ # convert err_code to bits
124
+ _code = _int2bits(ec)
125
+ data = {'location': location,
126
+ 'datetime': datetimes[n],
127
+ 'weather': weather,
128
+ 'angles': angles[n],
129
+ 'airmass': airmass[n],
130
+ 'settings': settings[n],
131
+ 'orientation': orientation[n],
132
+ 'shadowband': shadowband[n]}
133
+ raise SOLPOS_Error(_code, data)
134
+
135
+
136
+ def solposAM(location, datetime, weather):
137
+ """
138
+ Calculate solar position and air mass by calling functions exported by
139
+ :data:`SOLPOSAMDLL`.
140
+
141
+ :param location: [latitude, longitude, UTC-timezone]
142
+ :type location: float
143
+ :param datetime: [year, month, day, hour, minute, second]
144
+ :type datetime: int
145
+ :param weather: [ambient-pressure (mB), ambient-temperature (C)]
146
+ :type weather: float
147
+ :returns: angles [degrees], airmass [atm]
148
+ :rtype: float
149
+ :raises: :exc:`~solar_utils.exceptions.SOLPOS_Error`
150
+
151
+ Returns the solar zenith and azimuth angles in degrees, as well as the
152
+ relative and absolute (or pressure corrected) air mass.
153
+
154
+ **Examples:**
155
+
156
+ >>> location = [35.56836, -119.2022, -8.0]
157
+ >>> datetime = [2013, 6, 5, 12, 31, 0]
158
+ >>> weather = [1015.62055, 40.0]
159
+ >>> (angles, airmass) = solposAM(location, datetime, weather)
160
+ >>> list(angles)
161
+ [15.074043273925781, 213.29042053222656]
162
+ >>> list(airmass)
163
+ [1.0352272987365723, 1.0379053354263306]
164
+ """
165
+ # load the DLL
166
+ solposAM_dll = ctypes.cdll.LoadLibrary(SOLPOSAMDLL)
167
+ _solposAM = solposAM_dll.solposAM
168
+ # cast Python types as ctypes
169
+ _location = (ctypes.c_float * 3)(*location)
170
+ _datetime = (ctypes.c_int * 6)(*datetime)
171
+ _weather = (ctypes.c_float * 2)(*weather)
172
+ # allocate space for results
173
+ angles = (ctypes.c_float * 2)()
174
+ airmass = (ctypes.c_float * 2)()
175
+ settings = (ctypes.c_int * 2)()
176
+ orientation = (ctypes.c_float * 2)()
177
+ shadowband = (ctypes.c_float * 3)()
178
+ # call DLL
179
+ err_code = _solposAM(_location, _datetime, _weather, angles, airmass,
180
+ settings, orientation, shadowband)
181
+ # return results if successful, otherwise raise SOLPOS_Error
182
+ if err_code == 0:
183
+ return angles, airmass
184
+ else:
185
+ # convert err_code to bits
186
+ _code = _int2bits(err_code)
187
+ data = {'location': location,
188
+ 'datetime': datetime,
189
+ 'weather': weather,
190
+ 'angles': angles,
191
+ 'airmass': airmass,
192
+ 'settings': settings,
193
+ 'orientation': orientation,
194
+ 'shadowband': shadowband}
195
+ raise SOLPOS_Error(_code, data)
196
+
197
+
198
+ def spectrl2(units, location, datetime, weather, orientation,
199
+ atmospheric_conditions, albedo):
200
+ """
201
+ Calculate solar spectrum by calling functions exported by
202
+ :data:`SPECTRL2DLL`.
203
+
204
+ :param units: set ``units`` = 1 for W/m:sup:`2`/micron
205
+ :type units: int
206
+ :param location: latitude, longitude and UTC-timezone
207
+ :type location: float
208
+ :param datetime: year, month, day, hour, minute and second
209
+ :type datetime: int
210
+ :param weather: ambient-pressure [mB] and ambient-temperature [C]
211
+ :type weather: float
212
+ :param orientation: tilt and aspect [degrees]
213
+ :type orientation: float
214
+ :param atmospheric_conditions: alpha, assym, ozone, tau500 and watvap
215
+ :type atmospheric_conditions: float
216
+ :param albedo: 6 wavelengths and 6 reflectivities
217
+ :type albedo: float
218
+ :returns: spectral decomposition, x-coordinate
219
+ :rtype: float
220
+ :raises: :exc:`~solar_utils.exceptions.SPECTRL2_Error`,
221
+ :exc:`~solar_utils.exceptions.SOLPOS_Error`
222
+
223
+ Returns the diffuse, direct, extraterrestrial and global spectral components
224
+ on the tilted surface in as a function of x-coordinate specified by units.
225
+
226
+ ===== ===============================================================
227
+ units output units
228
+ ===== ===============================================================
229
+ 1 irradiance (W/sq m/micron) per wavelength (microns)
230
+ 2 photon flux (10.0E+16 /sq cm/s/micron) per wavelength (microns)
231
+ 3 photon flux density (10.0E+16 /sq cm/s/eV) per energy (eV)
232
+ ===== ===============================================================
233
+
234
+ See
235
+ `NREL SPECTRL2 Documentation <http://rredc.nrel.gov/solar/models/spectral/spectrl2/documentation.html>`_
236
+ for more detail.
237
+
238
+ .. seealso::
239
+ :func:`solposAM`
240
+
241
+ **Examples:**
242
+
243
+ >>> units = 1
244
+ >>> location = [33.65, -84.43, -5.0]
245
+ >>> datetime = [1999, 7, 22, 9, 45, 37]
246
+ >>> weather = [1006.0, 27.0]
247
+ >>> orientation = [33.65, 135.0]
248
+ >>> atmospheric_conditions = [1.14, 0.65, -1.0, 0.2, 1.36]
249
+ >>> albedo = [0.3, 0.7, 0.8, 1.3, 2.5, 4.0] + ([0.2] * 6)
250
+ >>> (specdif, specdir, specetr, specglo,
251
+ specx) = spectrl2(units, location, datetime, weather, orientation,
252
+ atmospheric_conditions, albedo)
253
+ """
254
+ # load the DLL
255
+ ctypes.cdll.LoadLibrary(SOLPOSAMDLL) # requires 'solpos.dll'
256
+ spectrl2_dll = ctypes.cdll.LoadLibrary(SPECTRL2DLL)
257
+ _spectrl2 = spectrl2_dll.spectrl2
258
+ # cast Python types as ctypes
259
+ _location = (ctypes.c_float * 3)(*location)
260
+ _datetime = (ctypes.c_int * 6)(*datetime)
261
+ _weather = (ctypes.c_float * 2)(*weather)
262
+ _orientation = (ctypes.c_float * 2)(*orientation)
263
+ _atmospheric_conditions = (ctypes.c_float * 5)(*atmospheric_conditions)
264
+ _albedo = (ctypes.c_float * 12)(*albedo)
265
+ # allocate space for results
266
+ specdif = (ctypes.c_float * 122)()
267
+ specdir = (ctypes.c_float * 122)()
268
+ specetr = (ctypes.c_float * 122)()
269
+ specglo = (ctypes.c_float * 122)()
270
+ specx = (ctypes.c_float * 122)()
271
+ angles = (ctypes.c_float * 2)()
272
+ airmass = (ctypes.c_float * 2)()
273
+ settings = (ctypes.c_int * 2)()
274
+ shadowband = (ctypes.c_float * 3)()
275
+ # call DLL
276
+ err_code = _spectrl2(
277
+ units, _location, _datetime, _weather, _orientation,
278
+ _atmospheric_conditions, _albedo, specdif, specdir, specetr, specglo,
279
+ specx, angles, airmass, settings, shadowband
280
+ )
281
+ # return results if successful, otherwise raise exception
282
+ if err_code == 0:
283
+ return specdif, specdir, specetr, specglo, specx
284
+ elif err_code < 0:
285
+ data = {'units': units,
286
+ 'tau500': atmospheric_conditions[3],
287
+ 'watvap': atmospheric_conditions[4],
288
+ 'assym': atmospheric_conditions[1]}
289
+ raise SPECTRL2_Error(err_code, data)
290
+ else:
291
+ # convert err_code to bits
292
+ _code = _int2bits(err_code)
293
+ data = {'location': location,
294
+ 'datetime': datetime,
295
+ 'weather': weather,
296
+ 'angles': angles,
297
+ 'airmass': airmass,
298
+ 'settings': settings,
299
+ 'orientation': orientation,
300
+ 'shadowband': shadowband}
301
+ raise SOLPOS_Error(_code, data)
Binary file
@@ -0,0 +1,122 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ Exceptions for solar utilities.
4
+
5
+ 2026 Mark Mikofski
6
+ """
7
+
8
+
9
+ class SolarUtilsException(Exception):
10
+ """
11
+ Base exception class for solar_utils.
12
+ """
13
+ pass
14
+
15
+
16
+ class SOLPOS_Error(SolarUtilsException):
17
+ """
18
+ SOLPOS Errors
19
+ """
20
+ S_CODE = ['S_YEAR_ERROR',
21
+ 'S_MONTH_ERROR',
22
+ 'S_DAY_ERROR',
23
+ 'S_DOY_ERROR',
24
+ 'S_HOUR_ERROR',
25
+ 'S_MINUTE_ERROR',
26
+ 'S_SECOND_ERROR',
27
+ 'S_TZONE_ERROR',
28
+ 'S_INTRVL_ERROR',
29
+ 'S_LAT_ERROR',
30
+ 'S_LON_ERROR',
31
+ 'S_TEMP_ERROR',
32
+ 'S_PRESS_ERROR',
33
+ 'S_TILT_ERROR',
34
+ 'S_ASPECT_ERROR',
35
+ 'S_SBWID_ERROR',
36
+ 'S_SBRAD_ERROR',
37
+ 'S_SBSKY_ERROR']
38
+
39
+ def __init__(self, code, data):
40
+ self.args = SOLPOS_Error.S_CODE[code], data
41
+ self.message = str(self)
42
+
43
+ def __str__(self):
44
+ if self.args[0] == 'S_YEAR_ERROR':
45
+ errmsg = "S_decode ==> Please fix the year: %d [1950-2050]\n"
46
+ message = errmsg % self.args[1]['datetime'][0]
47
+ if self.args[0] == 'S_MONTH_ERROR':
48
+ errmsg = "S_decode ==> Please fix the month: %d\n"
49
+ message = errmsg % self.args[1]['datetime'][1]
50
+ if self.args[0] == 'S_DAY_ERROR':
51
+ errmsg = "S_decode ==> Please fix the day-of-month: %d\n"
52
+ message = errmsg % self.args[1]['datetime'][2]
53
+ if self.args[0] == 'S_HOUR_ERROR':
54
+ errmsg = "S_decode ==> Please fix the hour: %d\n"
55
+ message = errmsg % self.args[1]['datetime'][3]
56
+ if self.args[0] == 'S_MINUTE_ERROR':
57
+ errmsg = "S_decode ==> Please fix the minute: %d\n"
58
+ message = errmsg % self.args[1]['datetime'][4]
59
+ if self.args[0] == 'S_SECOND_ERROR':
60
+ errmsg = "S_decode ==> Please fix the second: %d\n"
61
+ message = errmsg % self.args[1]['datetime'][5]
62
+ if self.args[0] == 'S_DOY_ERROR':
63
+ errmsg = "S_decode ==> Please fix the day-of-year: %d\n"
64
+ message = errmsg % self.args[1]['settings'][0]
65
+ if self.args[0] == 'S_INTRVL_ERROR':
66
+ errmsg = "S_decode ==> Please fix the interval: %d\n"
67
+ message = errmsg % self.args[1]['settings'][1]
68
+ if self.args[0] == 'S_LAT_ERROR':
69
+ errmsg = "S_decode ==> Please fix the latitude: %f\n"
70
+ message = errmsg % self.args[1]['location'][0]
71
+ if self.args[0] == 'S_LON_ERROR':
72
+ errmsg = "S_decode ==> Please fix the longitude: %f\n"
73
+ message = errmsg % self.args[1]['location'][1]
74
+ if self.args[0] == 'S_TZONE_ERROR':
75
+ errmsg = "S_decode ==> Please fix the time zone: %f\n"
76
+ message = errmsg % self.args[1]['location'][2]
77
+ if self.args[0] == 'S_PRESS_ERROR':
78
+ errmsg = "S_decode ==> Please fix the pressure: %f\n"
79
+ message = errmsg % self.args[1]['weather'][0]
80
+ if self.args[0] == 'S_TEMP_ERROR':
81
+ errmsg = "S_decode ==> Please fix the temperature: %f\n"
82
+ message = errmsg % self.args[1]['weather'][1]
83
+ if self.args[0] == 'S_TILT_ERROR':
84
+ errmsg = "S_decode ==> Please fix the tilt: %f\n"
85
+ message = errmsg % self.args[1]['orientation'][0]
86
+ if self.args[0] == 'S_ASPECT_ERROR':
87
+ errmsg = "S_decode ==> Please fix the aspect: %f\n"
88
+ message = errmsg % self.args[1]['orientation'][1]
89
+ if self.args[0] == 'S_SBWID_ERROR':
90
+ errmsg = "S_decode ==> Please fix the shadowband width: %f\n"
91
+ message = errmsg % self.args[1]['shadowband'][0]
92
+ if self.args[0] == 'S_SBRAD_ERROR':
93
+ errmsg = "S_decode ==> Please fix the shadowband radius: %f\n"
94
+ message = errmsg % self.args[1]['shadowband'][1]
95
+ if self.args[0] == 'S_SBSKY_ERROR':
96
+ errmsg = "S_decode ==> Please fix the shadowband sky factor: %f\n"
97
+ message = errmsg % self.args[1]['shadowband'][2]
98
+ return message
99
+
100
+
101
+ class SPECTRL2_Error(SolarUtilsException):
102
+ """
103
+ SPECTRL2 Errors
104
+ """
105
+ def __init__(self, code, data):
106
+ self.args = code, data
107
+ self.message = str(self)
108
+
109
+ def __str__(self):
110
+ if self.args[0] == -1:
111
+ errmsg = "*** S_spectral2 -> units should be 1 to 3, not %d\n"
112
+ message = errmsg % self.args[1]['units']
113
+ if self.args[0] == -2:
114
+ errmsg = "*** S_spectral2 -> tau500 should not be %f\n"
115
+ message = errmsg % self.args[1]['tau500']
116
+ if self.args[0] == -3:
117
+ errmsg = "*** S_spectral2 -> watvap should not be %f\n"
118
+ message = errmsg % self.args[1]['watvap']
119
+ if self.args[0] == -4:
120
+ errmsg = "*** S_spectral2 -> assym should not be %f\n"
121
+ message = errmsg % self.args[1]['assym']
122
+ return message
Binary file
Binary file
@@ -0,0 +1,5 @@
1
+ """
2
+ Tests for solar utilities.
3
+
4
+ 2026 Mark Mikofski
5
+ """
@@ -0,0 +1,225 @@
1
+ #! /usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ Tests for solar utilities.
5
+
6
+ Usage
7
+ =====
8
+ This code uses pytest. Enter the following in a CMD shell::
9
+
10
+ $ pytest --verbose
11
+
12
+ 2026 Mark Mikofski
13
+ """
14
+
15
+ import datetime as pydatetime
16
+ import json
17
+ import numpy as np
18
+ import os
19
+
20
+ from solar_utils import *
21
+ from solar_utils.exceptions import SOLPOS_Error, SPECTRL2_Error
22
+
23
+ _DIRNAME = os.path.dirname(__file__)
24
+ TOL = 1E-3
25
+
26
+ RELDIFF = lambda x, x0: np.abs(x - x0) / x0
27
+
28
+
29
+ def test_get_solpos8760():
30
+ location = [35.56836, -119.2022, -8.0]
31
+ weather = [1015.62055, 40.0]
32
+ x, y = get_solpos8760(location, 2017, weather)
33
+ z = np.array([
34
+ [ 98.96697 , 248.12735 , -1. , -1. ],
35
+ [ 98.96697 , 111.86818 , -1. , -1. ],
36
+ [ 98.96697 , 111.86365 , -1. , -1. ],
37
+ [ 98.96697 , 111.85951 , -1. , -1. ],
38
+ [ 98.96697 , 111.85494 , -1. , -1. ],
39
+ [ 98.96697 , 111.85041 , -1. , -1. ],
40
+ [ 98.96697 , 111.84623 , -1. , -1. ],
41
+ [ 91.8338 , 117.05424 , 64.716125 , 64.88354 ],
42
+ [ 81.50037 , 126.166016 , 6.489029 , 6.5058155],
43
+ [ 72.399506 , 136.78699 , 3.2762375, 3.2847128],
44
+ [ 65.06621 , 149.33855 , 2.3618126, 2.3679223],
45
+ [ 60.21105 , 163.89734 , 2.0070193, 2.0122113],
46
+ [ 58.47457 , 179.82529 , 1.9076174, 1.9125524],
47
+ [ 60.13257 , 195.77512 , 2.002265 , 2.0074446],
48
+ [ 64.921104 , 210.37862 , 2.3491564, 2.3552334],
49
+ [ 72.20489 , 222.98093 , 3.2422662, 3.2506535],
50
+ [ 81.272095 , 233.6459 , 6.333636 , 6.3500204],
51
+ [ 91.55735 , 242.79282 , 64.014565 , 64.18017 ],
52
+ [ 98.96697 , 248.20842 , -1. , -1. ],
53
+ [ 98.96697 , 248.21324 , -1. , -1. ],
54
+ [ 98.96697 , 248.21808 , -1. , -1. ],
55
+ [ 98.96697 , 248.22249 , -1. , -1. ],
56
+ [ 98.96697 , 248.22736 , -1. , -1. ],
57
+ [ 98.96697 , 248.23221 , -1. , -1. ]],
58
+ dtype=np.float32)
59
+ assert np.allclose(x[:24], z[:24,:2], TOL)
60
+ assert np.allclose(y[:24], z[:24,2:])
61
+
62
+
63
+ def test_get_solposAM():
64
+ location = [35.56836, -119.2022, -8.0]
65
+ weather = [1015.62055, 40.0]
66
+ times = [
67
+ (pydatetime.datetime(2017, 1, 1, 0, 0, 0)
68
+ + pydatetime.timedelta(hours=h)).timetuple()[:6]
69
+ for h in range(1000)]
70
+ x, y = get_solposAM(location, times, weather)
71
+ z = np.array([
72
+ [ 98.96697 , 248.12735 , -1. , -1. ],
73
+ [ 98.96697 , 111.86818 , -1. , -1. ],
74
+ [ 98.96697 , 111.86365 , -1. , -1. ],
75
+ [ 98.96697 , 111.85951 , -1. , -1. ],
76
+ [ 98.96697 , 111.85494 , -1. , -1. ],
77
+ [ 98.96697 , 111.85041 , -1. , -1. ],
78
+ [ 98.96697 , 111.84623 , -1. , -1. ],
79
+ [ 91.8338 , 117.05424 , 64.716125 , 64.88354 ],
80
+ [ 81.50037 , 126.166016 , 6.489029 , 6.5058155],
81
+ [ 72.399506 , 136.78699 , 3.2762375, 3.2847128],
82
+ [ 65.06621 , 149.33855 , 2.3618126, 2.3679223],
83
+ [ 60.21105 , 163.89734 , 2.0070193, 2.0122113],
84
+ [ 58.47457 , 179.82529 , 1.9076174, 1.9125524],
85
+ [ 60.13257 , 195.77512 , 2.002265 , 2.0074446],
86
+ [ 64.921104 , 210.37862 , 2.3491564, 2.3552334],
87
+ [ 72.20489 , 222.98093 , 3.2422662, 3.2506535],
88
+ [ 81.272095 , 233.6459 , 6.333636 , 6.3500204],
89
+ [ 91.55735 , 242.79282 , 64.014565 , 64.18017 ],
90
+ [ 98.96697 , 248.20842 , -1. , -1. ],
91
+ [ 98.96697 , 248.21324 , -1. , -1. ],
92
+ [ 98.96697 , 248.21808 , -1. , -1. ],
93
+ [ 98.96697 , 248.22249 , -1. , -1. ],
94
+ [ 98.96697 , 248.22736 , -1. , -1. ],
95
+ [ 98.96697 , 248.23221 , -1. , -1. ]],
96
+ dtype=np.float32)
97
+ assert np.allclose(x[:24], z[:24,:2], TOL)
98
+ assert np.allclose(y[:24], z[:24,2:])
99
+ # test LOCATION
100
+ location = [1135.56836, -119.2022, -8.0]
101
+ try:
102
+ x, y = get_solposAM(location, times, weather)
103
+ except SOLPOS_Error as err:
104
+ assert err.args[0] == 'S_LAT_ERROR'
105
+ location = [35.56836, -119.2022, -8.0]
106
+ # test DATETIMES
107
+ times = [list(ts) for ts in times]
108
+ times[10][5] = 3248273
109
+ times = [tuple(ts) for ts in times]
110
+ try:
111
+ x, y = get_solposAM(location, times, weather)
112
+ except SOLPOS_Error as err:
113
+ assert err.args[0] == 'S_SECOND_ERROR'
114
+
115
+
116
+ def test_solposAM():
117
+ """
118
+ test solposAM.dll
119
+ """
120
+ # test data
121
+ test_data = {'angles': [15.074043273925781, 213.29042053222656],
122
+ 'airmass': [1.0352272987365723, 1.0379053354263306]}
123
+ location = [35.56836, -119.2022, -8.0]
124
+ datetime = [2013, 6, 5, 12, 31, 0]
125
+ weather = [1015.62055, 40.0]
126
+ # call test function
127
+ angles, airmass = solposAM(location, datetime, weather)
128
+ # test OUTPUTS are within TOL
129
+ # FIXME: use np.testing.assert_allclose instead of manual RELDIFF checks
130
+ assert np.all(RELDIFF(angles[0], test_data['angles'][0]) < TOL)
131
+ assert np.all(RELDIFF(angles[1], test_data['angles'][1]) < TOL)
132
+ assert np.all(RELDIFF(airmass[0], test_data['airmass'][0]) < TOL)
133
+ assert np.all(RELDIFF(airmass[1], test_data['airmass'][1]) < TOL)
134
+ # test errors - YEAR
135
+ datetime = [2051, 6, 5, 12, 31, 0]
136
+ try:
137
+ solposAM(location, datetime, weather)
138
+ except SOLPOS_Error as err:
139
+ assert err.args[0] == 'S_YEAR_ERROR'
140
+ # test MONTH
141
+ datetime = [2013, 13, 5, 12, 31, 0]
142
+ try:
143
+ solposAM(location, datetime, weather)
144
+ except SOLPOS_Error as err:
145
+ assert err.args[0] == 'S_MONTH_ERROR'
146
+ # test DAY
147
+ datetime = [2013, 6, 32, 12, 31, 0]
148
+ try:
149
+ solposAM(location, datetime, weather)
150
+ except SOLPOS_Error as err:
151
+ assert err.args[0] == 'S_DAY_ERROR'
152
+ # test HOUR
153
+ datetime = [2013, 6, 5, 25, 31, 0]
154
+ try:
155
+ solposAM(location, datetime, weather)
156
+ except SOLPOS_Error as err:
157
+ assert err.args[0] == 'S_HOUR_ERROR'
158
+ # test MINUTE
159
+ datetime = [2013, 6, 5, 12, 61, 0]
160
+ try:
161
+ solposAM(location, datetime, weather)
162
+ except SOLPOS_Error as err:
163
+ assert err.args[0] == 'S_MINUTE_ERROR'
164
+ # test SECOND
165
+ datetime = [2013, 6, 5, 12, 31, 61]
166
+ try:
167
+ solposAM(location, datetime, weather)
168
+ except SOLPOS_Error as err:
169
+ assert err.args[0] == 'S_SECOND_ERROR'
170
+
171
+
172
+ def test_spectrl2():
173
+ """
174
+ test spectrl2.dll
175
+ """
176
+ # test data
177
+ test_data_jsonfile = 'test_spectrl2_data.json'
178
+ with open(os.path.join(_DIRNAME, test_data_jsonfile), 'r') as filepath:
179
+ test_data = json.load(filepath)
180
+ units = 1
181
+ location = [33.65, -84.43, -5.0]
182
+ datetime = [1999, 7, 22, 9, 45, 37]
183
+ weather = [1006.0, 27.0]
184
+ orientation = [33.65, 135.0]
185
+ atmospheric_conditions = [1.14, 0.65, -1.0, 0.2, 1.36]
186
+ albedo = [0.3, 0.7, 0.8, 1.3, 2.5, 4.0] + ([0.2] * 6)
187
+ # call test function
188
+ (specdif, specdir, specetr, specglo,
189
+ specx) = spectrl2(units, location, datetime, weather, orientation,
190
+ atmospheric_conditions, albedo)
191
+ # convert OUTPUT to numpy arrays
192
+ _specdif = np.ctypeslib.as_array(specdif) # use ctypeslib
193
+ _specdir = np.ctypeslib.as_array(specdir)
194
+ _specetr = np.ctypeslib.as_array(specetr)
195
+ _specglo = np.ctypeslib.as_array(specglo)
196
+ _specx = np.ctypeslib.as_array(specx)
197
+ # test OUTPUT are within TOL
198
+ # FIXME: use np.testing.assert_allclose instead of manual RELDIFF checks
199
+ assert np.all(RELDIFF(_specdif, test_data['specdif']) < TOL)
200
+ assert np.all(RELDIFF(_specdir, test_data['specdir']) < TOL)
201
+ assert np.all(RELDIFF(_specetr, test_data['specetr']) < TOL)
202
+ assert np.all(RELDIFF(_specglo, test_data['specglo']) < TOL)
203
+ assert np.all(RELDIFF(_specx, test_data['specx']) < TOL)
204
+ # raise a SPECTRL2_Error - UNITS
205
+ try:
206
+ spectrl2(3, location, datetime, weather, orientation,
207
+ atmospheric_conditions, albedo)
208
+ except SPECTRL2_Error as err:
209
+ assert err.args[0] == -1
210
+ # raise a SPECTRL2_Error - TAU500
211
+ try:
212
+ spectrl2(units, location, datetime, weather, orientation,
213
+ [-1.0] * 5, albedo)
214
+ except SPECTRL2_Error as err:
215
+ assert err.args[0] == -2
216
+ # now raise a SOLPOS_Error - YEAR
217
+ datetime = [2051, 6, 5, 12, 31, 0]
218
+ try:
219
+ spectrl2(units, location, datetime, weather, orientation,
220
+ atmospheric_conditions, albedo)
221
+ except SOLPOS_Error as err:
222
+ assert err.args[0] == 'S_YEAR_ERROR'
223
+
224
+ if __name__ == '__main__':
225
+ test_spectrl2()
@@ -0,0 +1,154 @@
1
+ {
2
+ "specdif": [
3
+ 1.8256434202194214, 17.148418426513672, 46.67808532714844, 92.94384765625,
4
+ 121.91969299316406, 170.91250610351562, 217.76622009277344,
5
+ 219.23980712890625, 215.29632568359375, 219.9678955078125,
6
+ 236.76014709472656, 237.5330810546875, 272.32635498046875, 267.9977722167969,
7
+ 250.46548461914062, 357.3795166015625, 409.7325134277344, 417.58392333984375,
8
+ 379.23907470703125, 436.9317626953125, 473.8831481933594, 471.4217529296875,
9
+ 447.6335144042969, 445.4048156738281, 405.9974670410156, 398.010498046875,
10
+ 391.03826904296875, 362.05389404296875, 363.166015625, 354.62506103515625,
11
+ 344.33380126953125, 315.5977783203125, 282.89276123046875, 273.4025573730469,
12
+ 256.17169189453125, 227.1124267578125, 224.96456909179688, 185.011962890625,
13
+ 192.93472290039062, 162.24752807617188, 155.99087524414062,
14
+ 168.61582946777344, 164.92115783691406, 160.66506958007812,
15
+ 94.19819641113281, 133.77293395996094, 147.88609313964844,
16
+ 137.38803100585938, 112.47001647949219, 104.15765380859375,
17
+ 111.84978485107422, 112.81221008300781, 111.17762756347656,
18
+ 102.47245788574219, 68.63471221923828, 69.3549575805664, 65.4437255859375,
19
+ 44.521202087402344, 32.40762710571289, 33.900672912597656, 59.41389846801758,
20
+ 64.13468170166016, 69.25424194335938, 61.30603790283203, 55.20729064941406,
21
+ 41.383174896240234, 12.391764640808105, 16.226001739501953,
22
+ 15.225900650024414, 29.422449111938477, 32.120059967041016,
23
+ 32.47023010253906, 33.25129699707031, 27.744041442871094, 29.106645584106445,
24
+ 22.566112518310547, 4.330706596374512, 0.3838190734386444, 3.492144823074341,
25
+ 6.2399139404296875, 6.015700340270996, 11.793516159057617,
26
+ 16.401538848876953, 15.6276273727417, 14.255119323730469, 13.917386054992676,
27
+ 12.989603996276855, 12.33969783782959, 13.073138236999512,
28
+ 12.412393569946289, 11.351621627807617, 8.5829439163208, 2.005711555480957,
29
+ 0.1156865805387497, 0.35932114720344543, 1.0042234659194946,
30
+ 3.953986167907715, 1.2784755229949951, 3.875173807144165, 3.0610997676849365,
31
+ 3.453407049179077, 3.166436195373535, 2.7821335792541504, 2.4278974533081055,
32
+ 1.881614089012146, 0.5927937030792236, 0.1878427267074585,
33
+ 4.3069093180747586e-08, 6.464413726536833e-11, 2.3338509436143795e-07,
34
+ 0.03232985734939575, 0.1072206050157547, 0.0981285348534584,
35
+ 0.1371127963066101, 0.09519591182470322, 0.19665323197841644,
36
+ 0.28267475962638855, 0.26244720816612244, 0.23989030718803406,
37
+ 0.22150859236717224, 0.19426670670509338, 0.1939333975315094
38
+ ],
39
+ "specdir": [
40
+ 0.9149357080459595, 9.68359375, 29.28199005126953, 63.89971160888672,
41
+ 91.44681549072266, 138.75453186035156, 190.586181640625, 206.19862365722656,
42
+ 216.7138214111328, 235.91452026367188, 269.44183349609375, 301.0520324707031,
43
+ 379.4623718261719, 406.0775146484375, 408.90765380859375, 623.77392578125,
44
+ 759.52734375, 817.4668579101562, 780.1897583007812, 940.6309814453125,
45
+ 1063.818603515625, 1119.2725830078125, 1120.5660400390625,
46
+ 1172.5084228515625, 1121.2725830078125, 1150.788818359375,
47
+ 1181.3558349609375, 1140.5875244140625, 1191.60107421875, 1209.749755859375,
48
+ 1219.31884765625, 1200.78857421875, 1164.79541015625, 1178.989990234375,
49
+ 1166.7801513671875, 1106.51708984375, 1127.48681640625, 996.6547241210938,
50
+ 1067.58740234375, 941.46630859375, 923.0817260742188, 998.2432250976562,
51
+ 997.566650390625, 981.5950927734375, 639.4552001953125, 857.7056884765625,
52
+ 944.158203125, 913.6908569335938, 790.43212890625, 747.81689453125,
53
+ 797.17138671875, 809.8842163085938, 819.34130859375, 780.630615234375,
54
+ 577.6444091796875, 587.2220458984375, 562.51220703125, 409.9044189453125,
55
+ 312.8055114746094, 328.8128662109375, 538.27587890625, 582.6295166015625,
56
+ 627.5308837890625, 587.275390625, 549.2069091796875, 443.9672546386719,
57
+ 157.4540557861328, 202.82371520996094, 194.2506866455078, 348.53863525390625,
58
+ 376.73052978515625, 386.14031982421875, 403.5086975097656, 352.179443359375,
59
+ 371.6019592285156, 306.12567138671875, 71.12310791015625, 6.875940322875977,
60
+ 61.84910583496094, 107.49081420898438, 104.7576675415039, 191.7346649169922,
61
+ 254.77392578125, 245.11929321289062, 229.71128845214844, 226.039794921875,
62
+ 213.5852813720703, 206.62855529785156, 219.04933166503906, 210.5574951171875,
63
+ 197.1224365234375, 158.13107299804688, 44.59239959716797, 2.8388142585754395,
64
+ 9.00092887878418, 24.810455322265625, 87.16841125488281, 31.691259384155273,
65
+ 85.90641784667969, 70.16461944580078, 78.18312072753906, 72.8062973022461,
66
+ 65.62701416015625, 59.399967193603516, 49.05033493041992, 17.618515014648438,
67
+ 5.997376441955566, 1.4635077150160214e-06, 2.269817223066184e-09,
68
+ 8.451898793282453e-06, 1.195347785949707, 3.9817092418670654,
69
+ 3.737084150314331, 5.256494045257568, 3.7773163318634033, 7.605112552642822,
70
+ 10.660894393920898, 10.066052436828613, 9.372171401977539, 8.79566764831543,
71
+ 7.892968654632568, 7.920684814453125
72
+ ],
73
+ "specetr": [
74
+ 518.744873046875, 540.4277954101562, 602.0886840820312, 670.5254516601562,
75
+ 692.2083129882812, 806.2373657226562, 931.1078491210938, 902.0682373046875,
76
+ 871.7701416015625, 882.1276245117188, 944.2724609375, 944.6597290039062,
77
+ 1084.050048828125, 1068.4654541015625, 1000.7062377929688,
78
+ 1431.7513427734375, 1646.83837890625, 1684.6866455078125, 1536.390869140625,
79
+ 1778.1943359375, 1940.8162841796875, 1977.599853515625, 1923.392578125,
80
+ 1962.112060546875, 1835.3056640625, 1847.889404296875, 1865.313232421875,
81
+ 1772.3863525390625, 1830.4656982421875, 1837.2415771484375,
82
+ 1831.4337158203125, 1781.0982666015625, 1711.403076171875, 1672.68359375,
83
+ 1604.9244384765625, 1475.2139892578125, 1481.9898681640625,
84
+ 1374.543212890625, 1354.2154541015625, 1330.0157470703125,
85
+ 1329.0477294921875, 1256.44873046875, 1228.3770751953125, 1205.145263671875,
86
+ 1183.849609375, 1166.42578125, 1145.1300048828125, 1111.25048828125,
87
+ 1056.0750732421875, 1028.00341796875, 1004.771728515625, 989.283935546875,
88
+ 966.7297973632812, 916.87841796875, 864.6070556640625, 840.4073486328125,
89
+ 803.1398315429688, 803.7205810546875, 787.9423828125, 761.7099609375,
90
+ 743.705322265625, 742.4469604492188, 733.3478393554688, 666.0726318359375,
91
+ 620.1900634765625, 586.79443359375, 567.144287109375, 551.9468994140625,
92
+ 546.0421142578125, 526.7791748046875, 516.324951171875, 485.5428771972656,
93
+ 462.21435546875, 428.52838134765625, 425.914794921875, 403.45745849609375,
94
+ 378.8705749511719, 347.41094970703125, 317.0161437988281, 307.33624267578125,
95
+ 297.4627685546875, 290.7836608886719, 283.42694091796875, 266.6807556152344,
96
+ 263.38958740234375, 250.99932861328125, 238.99627685546875,
97
+ 236.18911743164062, 235.7051239013672, 227.28363037109375,
98
+ 213.44139099121094, 184.69215393066406, 165.62278747558594,
99
+ 139.87429809570312, 131.35598754882812, 119.06254577636719,
100
+ 119.83694458007812, 109.3826675415039, 105.02671813964844, 94.37884521484375,
101
+ 89.44210815429688, 79.7622299194336, 72.2119140625, 66.11359405517578,
102
+ 61.75764465332031, 47.9154167175293, 46.947425842285156, 37.364341735839844,
103
+ 35.428367614746094, 30.975622177124023, 27.200468063354492,
104
+ 24.006107330322266, 21.392539978027344, 18.97256851196289, 16.93979263305664,
105
+ 15.19741439819336, 13.64863395690918, 12.293449401855469, 11.131864547729492,
106
+ 10.067076683044434, 9.195887565612793, 8.324698448181152
107
+ ],
108
+ "specglo": [
109
+ 2.6605875492095947, 25.985387802124023, 73.39999389648438,
110
+ 151.25689697265625, 205.37142944335938, 297.5359191894531, 391.689697265625,
111
+ 407.4107666015625, 413.0631408691406, 435.2567138671875, 482.64501953125,
112
+ 512.2645263671875, 618.61279296875, 638.5724487304688, 623.6228637695312,
113
+ 926.61767578125, 1102.8553466796875, 1163.580810546875, 1091.2178955078125,
114
+ 1295.3245849609375, 1444.6934814453125, 1492.837890625, 1470.22998046875,
115
+ 1515.40234375, 1429.2386474609375, 1448.1873779296875, 1469.109619140625,
116
+ 1402.92138671875, 1450.5869140625, 1458.60791015625, 1457.04931640625,
117
+ 1411.4029541015625, 1345.8515625, 1349.3150634765625, 1320.94189453125,
118
+ 1236.88818359375, 1253.876708984375, 1094.530517578125, 1167.184326171875,
119
+ 1021.4027099609375, 998.3687744140625, 1079.583984375, 1075.2718505859375,
120
+ 1056.4405517578125, 677.7466430664062, 916.4905395507812, 1009.497802734375,
121
+ 971.196044921875, 833.7957153320312, 786.5938720703125, 839.3255004882812,
122
+ 851.8892822265625, 858.885009765625, 814.8535766601562, 595.7764282226562,
123
+ 605.2369995117188, 578.7762451171875, 418.5882263183594, 317.8649597167969,
124
+ 333.9658508300781, 550.6290283203125, 595.82568359375, 641.9208984375,
125
+ 597.2366943359375, 556.3977661132812, 446.53497314453125, 156.07981872558594,
126
+ 201.31710815429688, 192.49351501464844, 347.4888000488281, 375.9135437011719,
127
+ 384.850830078125, 401.48175048828125, 349.1329040527344, 368.2199401855469,
128
+ 301.9276123046875, 69.23561096191406, 6.658605098724365, 59.93386459350586,
129
+ 104.33294677734375, 101.61454010009766, 186.76507568359375,
130
+ 248.90090942382812, 239.3164520263672, 223.883056640625, 220.19482421875,
131
+ 207.9014129638672, 200.90298461914062, 212.9712677001953, 204.56112670898438,
132
+ 191.23989868164062, 152.88882446289062, 42.69945526123047,
133
+ 2.7063071727752686, 8.573310852050781, 23.645532608032227, 83.50138092041016,
134
+ 30.19900894165039, 82.27090454101562, 67.09131622314453, 74.80107879638672,
135
+ 69.60737609863281, 62.67146682739258, 56.634605407714844, 46.6435432434082,
136
+ 16.67094612121582, 5.660876750946045, 1.3786242334390408e-06,
137
+ 2.1360142543613847e-09, 7.946346158860251e-06, 1.1231698989868164,
138
+ 3.740814208984375, 3.508484363555908, 4.934038162231445, 3.542266607284546,
139
+ 7.1368608474731445, 10.01150131225586, 9.448437690734863, 8.792664527893066,
140
+ 8.248183250427246, 7.397163391113281, 7.422123432159424
141
+ ],
142
+ "specx": [
143
+ 0.3, 0.305, 0.31, 0.315, 0.32, 0.325, 0.33, 0.335, 0.34, 0.345, 0.35, 0.36,
144
+ 0.37, 0.38, 0.39, 0.4, 0.41, 0.42, 0.43, 0.44, 0.45, 0.46, 0.47, 0.48, 0.49,
145
+ 0.5, 0.51, 0.52, 0.53, 0.54, 0.55, 0.57, 0.593, 0.61, 0.63, 0.656, 0.6676,
146
+ 0.69, 0.71, 0.718, 0.7244, 0.74, 0.7525, 0.7575, 0.7625, 0.7675, 0.78, 0.8,
147
+ 0.816, 0.8237, 0.8315, 0.84, 0.86, 0.88, 0.905, 0.915, 0.925, 0.93, 0.937,
148
+ 0.948, 0.965, 0.98, 0.9935, 1.04, 1.07, 1.1, 1.12, 1.13, 1.145, 1.161, 1.17,
149
+ 1.2, 1.24, 1.27, 1.29, 1.32, 1.35, 1.395, 1.4425, 1.4625, 1.477, 1.497, 1.52,
150
+ 1.539, 1.558, 1.578, 1.592, 1.61, 1.63, 1.646, 1.678, 1.74, 1.8, 1.86, 1.92,
151
+ 1.96, 1.985, 2.005, 2.035, 2.065, 2.1, 2.148, 2.198, 2.27, 2.36, 2.45, 2.5,
152
+ 2.6, 2.7, 2.8, 2.9, 3.0, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6, 3.7, 3.8, 3.9, 4.0
153
+ ]
154
+ }
@@ -0,0 +1,106 @@
1
+ Metadata-Version: 2.4
2
+ Name: SolarUtils
3
+ Version: 0.4.8
4
+ Summary: Python wrappers around NREL SOLPOS and SPECTRL2
5
+ Home-page: https://github.com/mikofski/SolarUtils
6
+ Author: Mark Mikofski
7
+ Author-email: bwana.marko@yahoo.com
8
+ License: BSD 3-Clause
9
+ Platform: win32
10
+ Platform: linux
11
+ Platform: linux2
12
+ Platform: darwin
13
+ License-File: LICENSE
14
+ Provides-Extra: test
15
+ Requires-Dist: numpy; extra == "test"
16
+ Requires-Dist: pytest; extra == "test"
17
+ Dynamic: author
18
+ Dynamic: author-email
19
+ Dynamic: description
20
+ Dynamic: home-page
21
+ Dynamic: license
22
+ Dynamic: license-file
23
+ Dynamic: platform
24
+ Dynamic: provides-extra
25
+ Dynamic: summary
26
+
27
+ .. image:: https://github.com/mikofski/SolarUtils/actions/workflows/python-test.yml/badge.svg
28
+ :target: https://github.com/mikofski/SolarUtils/actions/workflows/python-test.yml
29
+
30
+ SolarUtils is set of Python wrappers around the publicly available
31
+ `NREL <http://www.nrel.gov/>`_
32
+ `SOLPOS <http://rredc.nrel.gov/solar/codesandalgorithms/solpos/>`_ and
33
+ `SPECTRL2 <http://rredc.nrel.gov/solar/models/spectral/>`_ C-language computer
34
+ programs that calculate solar position and spectral decomposition. Please read
35
+ the `NREL disclaimer and license <http://www.nrel.gov/disclaimer.html>`_. Usage
36
+ of this software implies acceptance of the terms.
37
+
38
+ Installation
39
+ ============
40
+ Use ``pip`` to install
41
+ `SolarUtils from the PyPI <https://pypi.python.org/pypi/SolarUtils>`_::
42
+
43
+ pip install SolarUtils
44
+
45
+ You can also download a source distribution from PyPI or clone the repository
46
+ and use Python ``distutils``::
47
+
48
+ python setup.py install
49
+
50
+ Requirements
51
+ ============
52
+ SolarUtils has no requirements for usage however for installation, testing and
53
+ to build the documentaiton you will need the following pacakges:
54
+
55
+ * NumPy
56
+ * PyTest
57
+ * Sphinx
58
+
59
+ Usage
60
+ =====
61
+ See `SOLPOS Documentation <http://rredc.nrel.gov/solar/codesandalgorithms/solpos/aboutsolpos.html>`_
62
+ and
63
+ `SPECTRL2 Documentation <http://rredc.nrel.gov/solar/models/spectral/spectrl2/documentation.html>`_
64
+ for more detail.
65
+
66
+ Examples
67
+ --------
68
+
69
+ >>> from solar_utils import *
70
+ >>> import pandas as pd
71
+ >>> location = [35.56836, -119.2022, -8.0]
72
+ >>> datetime = [2013, 6, 5, 12, 31, 0]
73
+ >>> weather = [1015.62055, 40.0]
74
+ >>> (angles, airmass) = solposAM(location, datetime, weather)
75
+ >>> zenith, azimuth = angles
76
+ >>> zenith
77
+ 15.074043273925781
78
+ >>> azimuth
79
+ 213.29042053222656]
80
+ >>> am, amp = airmass
81
+ >>> am
82
+ 1.0352272987365723
83
+ >>> amp
84
+ 1.0379053354263306]
85
+
86
+ >>> units = 1
87
+ >>> location = [33.65, -84.43, -5.0]
88
+ >>> datetime = [1999, 7, 22, 9, 45, 37]
89
+ >>> weather = [1006.0, 27.0]
90
+ >>> orientation = [33.65, 135.0]
91
+ >>> atmospheric_conditions = [1.14, 0.65, -1.0, 0.2, 1.36]
92
+ >>> albedo = [0.3, 0.7, 0.8, 1.3, 2.5, 4.0] + ([0.2] * 6)
93
+ >>> specdif, specdir, specetr, specglo, specx = spectrl2(
94
+ ... units, location, datetime, weather, orientation,
95
+ ... atmospheric_conditions, albedo
96
+ ... )
97
+ >>> spec = pd.DataFrame(
98
+ ... {'DIF': specdif, 'DIR': specdir, 'ETR': specetr, 'GLO': specglo},
99
+ ... index=specx
100
+ ... )
101
+ >>> f = spec.plot()
102
+ >>> f.set_title('Solar Spectrum Example')
103
+ >>> f.set_xlabel('Wavelength, $\lambda [\mu m]$')
104
+ >>> f.set_ylabel('Spectral Irradiance, $I_{\lambda} [W/m^2/\mu m]$')
105
+ >>> f.grid(True)
106
+ >>> f.figure.show()
@@ -0,0 +1,15 @@
1
+ solar_utils/NREL_DISCLAIMERS-COPYRIGHTS-LICENSES.txt,sha256=rcnn4G5mGQiwmH7STxDT2VI1-fh2pWRIIevQ5uVyyWk,3809
2
+ solar_utils/__init__.py,sha256=iHQv1h-D2EcVcqRy7kaI1LzIzfQli7eIpl5EYuAPebY,416
3
+ solar_utils/core.py,sha256=-uLbqDV2kO1MmFBG2sid6yYlXle2ePxvQA8SeZCDWHM,11194
4
+ solar_utils/dummy.cp313-win_amd64.pyd,sha256=FENvOPplo7gHqu6dyaZyVIX8k-gH5Ev0Y0jTO6-Dy_Y,9728
5
+ solar_utils/exceptions.py,sha256=CFeUqtGwwVBmo4nukXPP7q24K1wB3wvKktmYpnTd_lc,5030
6
+ solar_utils/solposAM.dll,sha256=2hteBib54dYHt9LuYLefbkl8vqAjXjM6_KvxcG27vHQ,14336
7
+ solar_utils/spectrl2.dll,sha256=fypZPEUM0ke_u67ckAxC8Vi38VW4PR-dewF_W0BDpx8,23040
8
+ solar_utils/tests/__init__.py,sha256=Asix3OCHzqoO8GSk3RKAUsbpJ2Xif-hDbjAhkQaIjGQ,60
9
+ solar_utils/tests/test_cdlls.py,sha256=zEtxphIh5gceq7BjN703IIkRv1YEfoh7HDzYoHROGfU,9034
10
+ solar_utils/tests/test_spectrl2_data.json,sha256=BL74L0iSmTKP-JDhhY1-g0fHfWv2J6Izrv5LUexsIQE,10637
11
+ solarutils-0.4.8.dist-info/licenses/LICENSE,sha256=WH-S__LyaIxxN7nDu2h9dacDe2DBhDAq_avzcfFtn2k,1518
12
+ solarutils-0.4.8.dist-info/METADATA,sha256=CLWrOlamql_mBkjNci-CgrOn7jOaJwB0aT6LFKOX9xY,3410
13
+ solarutils-0.4.8.dist-info/WHEEL,sha256=fOyJWI5N6P4iPL9_doBC3lf0L4Z-hcIkjNV5ilMSlqY,101
14
+ solarutils-0.4.8.dist-info/top_level.txt,sha256=z2p3nYQUAh05vyKeJYRmCvw25A1l7kJw57DP7QBoDZw,12
15
+ solarutils-0.4.8.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.2.1)
3
+ Root-Is-Purelib: false
4
+ Tag: cp313-cp313-win_amd64
5
+
@@ -0,0 +1,28 @@
1
+ Copyright (c) 2026, Mark Mikofski Corp.
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without
5
+ modification, are permitted provided that the following conditions are met:
6
+
7
+ 1. Redistributions of source code must retain the above copyright notice,
8
+ this list of conditions and the following disclaimer.
9
+
10
+ 2. Redistributions in binary form must reproduce the above copyright notice,
11
+ this list of conditions and the following disclaimer in the documentation
12
+ and/or other materials provided with the distribution.
13
+
14
+ 3. Neither the name of the copyright holder nor the names of its contributors
15
+ may be used to endorse or promote products derived from this software without
16
+ specific prior written permission.
17
+
18
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
22
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28
+ POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1 @@
1
+ solar_utils