• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

CalebBell / fluids / 5171501467

pending completion
5171501467

push

github-actions

web-flow
Merge pull request #58 from CalebBell/trypypyupgrade

See if can upgarde pypy on CI

4051 of 5352 branches covered (75.69%)

11453 of 13354 relevant lines covered (85.76%)

16.94 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

65.81
/fluids/optional/spa.py
1
"""
2
irradiance.py from pvlib
3
========================
4
Stripped down, vendorized version from:
5
https://github.com/pvlib/pvlib-python/
6

7
Calculate the solar position using the NREL SPA algorithm either using
8
numpy arrays or compiling the code to machine language with numba.
9

10
The rational for not including this library as a strict dependency is to avoid
11
including a dependency on pandas, keeping load time low, and PyPy compatibility
12

13
Created by Tony Lorenzo (@alorenzo175), Univ. of Arizona, 2015
14

15
For a full list of contributors to this file, see the `pvlib` repository.
16

17
The copyright notice (BSD-3 clause) is as follows:
18

19
BSD 3-Clause License
20

21
Copyright (c) 2013-2018, Sandia National Laboratories and pvlib python
22
Development Team
23
All rights reserved.
24

25
Redistribution and use in source and binary forms, with or without modification,
26
are permitted provided that the following conditions are met:
27

28
  Redistributions of source code must retain the above copyright notice, this
29
  list of conditions and the following disclaimer.
30

31
  Redistributions in binary form must reproduce the above copyright notice, this
32
  list of conditions and the following disclaimer in the documentation and/or
33
  other materials provided with the distribution.
34

35
  Neither the name of the {organization} nor the names of its
36
  contributors may be used to endorse or promote products derived from
37
  this software without specific prior written permission.
38

39
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
40
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
41
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
42
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
43
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
44
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
45
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
46
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
47
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
48
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
49
"""
50

51

52
from math import acos, asin, atan, atan2, cos, degrees, radians, sin, tan
20✔
53

54
from fluids.constants import deg2rad, rad2deg
20✔
55
from fluids.numerics import sincos
20✔
56

57
__all__ = ['julian_day_dt', 'julian_day', 'julian_ephemeris_day', 'julian_century',
20✔
58
           'julian_ephemeris_century', 'julian_ephemeris_millennium', 'heliocentric_longitude',
59
           'heliocentric_latitude', 'heliocentric_radius_vector', 'geocentric_longitude',
60
           'geocentric_latitude', 'mean_elongation', 'mean_anomaly_sun', 'mean_anomaly_moon',
61
           'moon_argument_latitude', 'moon_ascending_longitude', 'longitude_nutation',
62
           'obliquity_nutation', 'mean_ecliptic_obliquity', 'true_ecliptic_obliquity',
63
           'aberration_correction', 'apparent_sun_longitude', 'mean_sidereal_time',
64
           'apparent_sidereal_time', 'geocentric_sun_right_ascension', 'geocentric_sun_declination',
65
           'local_hour_angle', 'equatorial_horizontal_parallax', 'uterm', 'xterm', 'yterm',
66
           'parallax_sun_right_ascension', 'topocentric_sun_right_ascension', 'topocentric_sun_declination',
67
           'topocentric_local_hour_angle', 'topocentric_elevation_angle_without_atmosphere',
68
           'atmospheric_refraction_correction', 'topocentric_elevation_angle', 'topocentric_zenith_angle',
69
           'topocentric_astronomers_azimuth', 'topocentric_azimuth_angle', 'sun_mean_longitude',
70
           'equation_of_time', 'calculate_deltat', 'longitude_obliquity_nutation',
71
           'transit_sunrise_sunset',
72
           ]
73
nan = float("nan")
20✔
74

75

76
HELIO_RADIUS_TABLE_LIST_0 = [[100013989.0, 0.0, 0.0],
20✔
77
 [1670700.0, 3.0984635, 6283.07585],
78
 [13956.0, 3.05525, 12566.1517],
79
 [3084.0, 5.1985, 77713.7715],
80
 [1628.0, 1.1739, 5753.3849],
81
 [1576.0, 2.8469, 7860.4194],
82
 [925.0, 5.453, 11506.77],
83
 [542.0, 4.564, 3930.21],
84
 [472.0, 3.661, 5884.927],
85
 [346.0, 0.964, 5507.553],
86
 [329.0, 5.9, 5223.694],
87
 [307.0, 0.299, 5573.143],
88
 [243.0, 4.273, 11790.629],
89
 [212.0, 5.847, 1577.344],
90
 [186.0, 5.022, 10977.079],
91
 [175.0, 3.012, 18849.228],
92
 [110.0, 5.055, 5486.778],
93
 [98.0, 0.89, 6069.78],
94
 [86.0, 5.69, 15720.84],
95
 [86.0, 1.27, 161000.69],
96
 [65.0, 0.27, 17260.15],
97
 [63.0, 0.92, 529.69],
98
 [57.0, 2.01, 83996.85],
99
 [56.0, 5.24, 71430.7],
100
 [49.0, 3.25, 2544.31],
101
 [47.0, 2.58, 775.52],
102
 [45.0, 5.54, 9437.76],
103
 [43.0, 6.01, 6275.96],
104
 [39.0, 5.36, 4694.0],
105
 [38.0, 2.39, 8827.39],
106
 [37.0, 0.83, 19651.05],
107
 [37.0, 4.9, 12139.55],
108
 [36.0, 1.67, 12036.46],
109
 [35.0, 1.84, 2942.46],
110
 [33.0, 0.24, 7084.9],
111
 [32.0, 0.18, 5088.63],
112
 [32.0, 1.78, 398.15],
113
 [28.0, 1.21, 6286.6],
114
 [28.0, 1.9, 6279.55],
115
 [26.0, 4.59, 10447.39]]
116

117
HELIO_RADIUS_TABLE_LIST_1 = [[103019.0, 1.10749, 6283.07585],
20✔
118
 [1721.0, 1.0644, 12566.1517],
119
 [702.0, 3.142, 0.0],
120
 [32.0, 1.02, 18849.23],
121
 [31.0, 2.84, 5507.55],
122
 [25.0, 1.32, 5223.69],
123
 [18.0, 1.42, 1577.34],
124
 [10.0, 5.91, 10977.08],
125
 [9.0, 1.42, 6275.96],
126
 [9.0, 0.27, 5486.78],
127
]
128
HELIO_RADIUS_TABLE_LIST_2 = [[4359.0, 5.7846, 6283.0758],
20✔
129
 [124.0, 5.579, 12566.152],
130
 [12.0, 3.14, 0.0],
131
 [9.0, 3.63, 77713.77],
132
 [6.0, 1.87, 5573.14],
133
 [3.0, 5.47, 18849.23]]
134
HELIO_RADIUS_TABLE_LIST_3 = [[145.0, 4.273, 6283.076],
20✔
135
 [7.0, 3.92, 12566.15]]
136
HELIO_RADIUS_TABLE_LIST_4 = [[4.0, 2.56, 6283.08]]
20✔
137

138
NUTATION_YTERM_LIST_0 = [0.0, -2.0, 0.0, 0.0, 0.0, 0.0, -2.0, 0.0, 0.0, -2.0, -2.0, -2.0, 0.0, 2.0, 0.0, 2.0, 0.0, 0.0, -2.0, 0.0, 2.0, 0.0, 0.0, -2.0, 0.0, -2.0, 0.0, 0.0, 2.0, -2.0, 0.0, -2.0, 0.0, 0.0, 2.0, 2.0, 0.0, -2.0, 0.0, 2.0, 2.0, -2.0, -2.0, 2.0, 2.0, 0.0, -2.0, -2.0, 0.0, -2.0, -2.0, 0.0, -1.0, -2.0, 1.0, 0.0, 0.0, -1.0, 0.0, 0.0, 2.0, 0.0, 2.0]
20✔
139
NUTATION_YTERM_LIST_1 = [0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0, -1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 2.0, 0.0, 2.0, 1.0, 0.0, -1.0, 0.0, 0.0, 0.0, 1.0, 1.0, -1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, -1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, -1.0, 1.0, -1.0, -1.0, 0.0, -1.0]
20✔
140
NUTATION_YTERM_LIST_2 = [0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, -1.0, 0.0, 1.0, -1.0, -1.0, 1.0, 2.0, -2.0, 0.0, 2.0, 2.0, 1.0, 0.0, 0.0, -1.0, 0.0, -1.0, 0.0, 0.0, 1.0, 0.0, 2.0, -1.0, 1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 2.0, 1.0, -2.0, 0.0, 1.0, 0.0, 0.0, 2.0, 2.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, -2.0, 1.0, 1.0, 1.0, -1.0, 3.0, 0.0]
20✔
141
NUTATION_YTERM_LIST_3 = [0.0, 2.0, 2.0, 0.0, 0.0, 0.0, 2.0, 2.0, 2.0, 2.0, 0.0, 2.0, 2.0, 0.0, 0.0, 2.0, 0.0, 2.0, 0.0, 2.0, 2.0, 2.0, 0.0, 2.0, 2.0, 2.0, 2.0, 0.0, 0.0, 2.0, 0.0, 0.0, 0.0, -2.0, 2.0, 2.0, 2.0, 0.0, 2.0, 2.0, 0.0, 2.0, 2.0, 0.0, 0.0, 0.0, 2.0, 0.0, 2.0, 0.0, 2.0, -2.0, 0.0, 0.0, 0.0, 2.0, 2.0, 0.0, 0.0, 2.0, 2.0, 2.0, 2.0]
20✔
142
NUTATION_YTERM_LIST_4 = [1.0, 2.0, 2.0, 2.0, 0.0, 0.0, 2.0, 1.0, 2.0, 2.0, 0.0, 1.0, 2.0, 0.0, 1.0, 2.0, 1.0, 1.0, 0.0, 1.0, 2.0, 2.0, 0.0, 2.0, 0.0, 0.0, 1.0, 0.0, 1.0, 2.0, 1.0, 1.0, 1.0, 0.0, 1.0, 2.0, 2.0, 0.0, 2.0, 1.0, 0.0, 2.0, 1.0, 1.0, 1.0, 0.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 2.0, 0.0, 0.0, 2.0, 2.0, 2.0, 2.0]
20✔
143

144
NUTATION_ABCD_LIST = [[-171996.0, -174.2, 92025.0, 8.9],
20✔
145
 [-13187.0, -1.6, 5736.0, -3.1],
146
 [-2274.0, -0.2, 977.0, -0.5],
147
 [2062.0, 0.2, -895.0, 0.5],
148
 [1426.0, -3.4, 54.0, -0.1],
149
 [712.0, 0.1, -7.0, 0.0],
150
 [-517.0, 1.2, 224.0, -0.6],
151
 [-386.0, -0.4, 200.0, 0.0],
152
 [-301.0, 0.0, 129.0, -0.1],
153
 [217.0, -0.5, -95.0, 0.3],
154
 [-158.0, 0.0, 0.0, 0.0],
155
 [129.0, 0.1, -70.0, 0.0],
156
 [123.0, 0.0, -53.0, 0.0],
157
 [63.0, 0.0, 0.0, 0.0],
158
 [63.0, 0.1, -33.0, 0.0],
159
 [-59.0, 0.0, 26.0, 0.0],
160
 [-58.0, -0.1, 32.0, 0.0],
161
 [-51.0, 0.0, 27.0, 0.0],
162
 [48.0, 0.0, 0.0, 0.0],
163
 [46.0, 0.0, -24.0, 0.0],
164
 [-38.0, 0.0, 16.0, 0.0],
165
 [-31.0, 0.0, 13.0, 0.0],
166
 [29.0, 0.0, 0.0, 0.0],
167
 [29.0, 0.0, -12.0, 0.0],
168
 [26.0, 0.0, 0.0, 0.0],
169
 [-22.0, 0.0, 0.0, 0.0],
170
 [21.0, 0.0, -10.0, 0.0],
171
 [17.0, -0.1, 0.0, 0.0],
172
 [16.0, 0.0, -8.0, 0.0],
173
 [-16.0, 0.1, 7.0, 0.0],
174
 [-15.0, 0.0, 9.0, 0.0],
175
 [-13.0, 0.0, 7.0, 0.0],
176
 [-12.0, 0.0, 6.0, 0.0],
177
 [11.0, 0.0, 0.0, 0.0],
178
 [-10.0, 0.0, 5.0, 0.0],
179
 [-8.0, 0.0, 3.0, 0.0],
180
 [7.0, 0.0, -3.0, 0.0],
181
 [-7.0, 0.0, 0.0, 0.0],
182
 [-7.0, 0.0, 3.0, 0.0],
183
 [-7.0, 0.0, 3.0, 0.0],
184
 [6.0, 0.0, 0.0, 0.0],
185
 [6.0, 0.0, -3.0, 0.0],
186
 [6.0, 0.0, -3.0, 0.0],
187
 [-6.0, 0.0, 3.0, 0.0],
188
 [-6.0, 0.0, 3.0, 0.0],
189
 [5.0, 0.0, 0.0, 0.0],
190
 [-5.0, 0.0, 3.0, 0.0],
191
 [-5.0, 0.0, 3.0, 0.0],
192
 [-5.0, 0.0, 3.0, 0.0],
193
 [4.0, 0.0, 0.0, 0.0],
194
 [4.0, 0.0, 0.0, 0.0],
195
 [4.0, 0.0, 0.0, 0.0],
196
 [-4.0, 0.0, 0.0, 0.0],
197
 [-4.0, 0.0, 0.0, 0.0],
198
 [-4.0, 0.0, 0.0, 0.0],
199
 [3.0, 0.0, 0.0, 0.0],
200
 [-3.0, 0.0, 0.0, 0.0],
201
 [-3.0, 0.0, 0.0, 0.0],
202
 [-3.0, 0.0, 0.0, 0.0],
203
 [-3.0, 0.0, 0.0, 0.0],
204
 [-3.0, 0.0, 0.0, 0.0],
205
 [-3.0, 0.0, 0.0, 0.0],
206
 [-3.0, 0.0, 0.0, 0.0]]
207

208
HELIO_LAT_TABLE_LIST_0 = [[280.0, 3.199, 84334.662],
20✔
209
 [102.0, 5.422, 5507.553],
210
 [80.0, 3.88, 5223.69],
211
 [44.0, 3.7, 2352.87],
212
 [32.0, 4.0, 1577.34]]
213

214
HELIO_LAT_TABLE_LIST_1 = [[9.0, 3.9, 5507.55],
20✔
215
 [6.0, 1.73, 5223.69]]
216

217
#HELIO_LONG_TABLE_LIST = HELIO_LONG_TABLE.tolist()
218
HELIO_LONG_TABLE_LIST_0 = [[175347046.0, 0.0, 0.0],
20✔
219
 [3341656.0, 4.6692568, 6283.07585],
220
 [34894.0, 4.6261, 12566.1517],
221
 [3497.0, 2.7441, 5753.3849],
222
 [3418.0, 2.8289, 3.5231],
223
 [3136.0, 3.6277, 77713.7715],
224
 [2676.0, 4.4181, 7860.4194],
225
 [2343.0, 6.1352, 3930.2097],
226
 [1324.0, 0.7425, 11506.7698],
227
 [1273.0, 2.0371, 529.691],
228
 [1199.0, 1.1096, 1577.3435],
229
 [990.0, 5.233, 5884.927],
230
 [902.0, 2.045, 26.298],
231
 [857.0, 3.508, 398.149],
232
 [780.0, 1.179, 5223.694],
233
 [753.0, 2.533, 5507.553],
234
 [505.0, 4.583, 18849.228],
235
 [492.0, 4.205, 775.523],
236
 [357.0, 2.92, 0.067],
237
 [317.0, 5.849, 11790.629],
238
 [284.0, 1.899, 796.298],
239
 [271.0, 0.315, 10977.079],
240
 [243.0, 0.345, 5486.778],
241
 [206.0, 4.806, 2544.314],
242
 [205.0, 1.869, 5573.143],
243
 [202.0, 2.458, 6069.777],
244
 [156.0, 0.833, 213.299],
245
 [132.0, 3.411, 2942.463],
246
 [126.0, 1.083, 20.775],
247
 [115.0, 0.645, 0.98],
248
 [103.0, 0.636, 4694.003],
249
 [102.0, 0.976, 15720.839],
250
 [102.0, 4.267, 7.114],
251
 [99.0, 6.21, 2146.17],
252
 [98.0, 0.68, 155.42],
253
 [86.0, 5.98, 161000.69],
254
 [85.0, 1.3, 6275.96],
255
 [85.0, 3.67, 71430.7],
256
 [80.0, 1.81, 17260.15],
257
 [79.0, 3.04, 12036.46],
258
 [75.0, 1.76, 5088.63],
259
 [74.0, 3.5, 3154.69],
260
 [74.0, 4.68, 801.82],
261
 [70.0, 0.83, 9437.76],
262
 [62.0, 3.98, 8827.39],
263
 [61.0, 1.82, 7084.9],
264
 [57.0, 2.78, 6286.6],
265
 [56.0, 4.39, 14143.5],
266
 [56.0, 3.47, 6279.55],
267
 [52.0, 0.19, 12139.55],
268
 [52.0, 1.33, 1748.02],
269
 [51.0, 0.28, 5856.48],
270
 [49.0, 0.49, 1194.45],
271
 [41.0, 5.37, 8429.24],
272
 [41.0, 2.4, 19651.05],
273
 [39.0, 6.17, 10447.39],
274
 [37.0, 6.04, 10213.29],
275
 [37.0, 2.57, 1059.38],
276
 [36.0, 1.71, 2352.87],
277
 [36.0, 1.78, 6812.77],
278
 [33.0, 0.59, 17789.85],
279
 [30.0, 0.44, 83996.85],
280
 [30.0, 2.74, 1349.87],
281
 [25.0, 3.16, 4690.48]]
282
HELIO_LONG_TABLE_LIST_1 = [[628331966747.0, 0.0, 0.0],
20✔
283
 [206059.0, 2.678235, 6283.07585],
284
 [4303.0, 2.6351, 12566.1517],
285
 [425.0, 1.59, 3.523],
286
 [119.0, 5.796, 26.298],
287
 [109.0, 2.966, 1577.344],
288
 [93.0, 2.59, 18849.23],
289
 [72.0, 1.14, 529.69],
290
 [68.0, 1.87, 398.15],
291
 [67.0, 4.41, 5507.55],
292
 [59.0, 2.89, 5223.69],
293
 [56.0, 2.17, 155.42],
294
 [45.0, 0.4, 796.3],
295
 [36.0, 0.47, 775.52],
296
 [29.0, 2.65, 7.11],
297
 [21.0, 5.34, 0.98],
298
 [19.0, 1.85, 5486.78],
299
 [19.0, 4.97, 213.3],
300
 [17.0, 2.99, 6275.96],
301
 [16.0, 0.03, 2544.31],
302
 [16.0, 1.43, 2146.17],
303
 [15.0, 1.21, 10977.08],
304
 [12.0, 2.83, 1748.02],
305
 [12.0, 3.26, 5088.63],
306
 [12.0, 5.27, 1194.45],
307
 [12.0, 2.08, 4694.0],
308
 [11.0, 0.77, 553.57],
309
 [10.0, 1.3, 6286.6],
310
 [10.0, 4.24, 1349.87],
311
 [9.0, 2.7, 242.73],
312
 [9.0, 5.64, 951.72],
313
 [8.0, 5.3, 2352.87],
314
 [6.0, 2.65, 9437.76],
315
 [6.0, 4.67, 4690.48],
316
 ]
317
HELIO_LONG_TABLE_LIST_2 = [[52919.0, 0.0, 0.0],
20✔
318
 [8720.0, 1.0721, 6283.0758],
319
 [309.0, 0.867, 12566.152],
320
 [27.0, 0.05, 3.52],
321
 [16.0, 5.19, 26.3],
322
 [16.0, 3.68, 155.42],
323
 [10.0, 0.76, 18849.23],
324
 [9.0, 2.06, 77713.77],
325
 [7.0, 0.83, 775.52],
326
 [5.0, 4.66, 1577.34],
327
 [4.0, 1.03, 7.11],
328
 [4.0, 3.44, 5573.14],
329
 [3.0, 5.14, 796.3],
330
 [3.0, 6.05, 5507.55],
331
 [3.0, 1.19, 242.73],
332
 [3.0, 6.12, 529.69],
333
 [3.0, 0.31, 398.15],
334
 [3.0, 2.28, 553.57],
335
 [2.0, 4.38, 5223.69],
336
 [2.0, 3.75, 0.98]]
337

338
HELIO_LONG_TABLE_LIST_3 = [[289.0, 5.844, 6283.076],
20✔
339
 [35.0, 0.0, 0.0],
340
 [17.0, 5.49, 12566.15],
341
 [3.0, 5.2, 155.42],
342
 [1.0, 4.72, 3.52],
343
 [1.0, 5.3, 18849.23],
344
 [1.0, 5.97, 242.73]
345
 ]
346
HELIO_LONG_TABLE_LIST_4 = [[114.0, 3.142, 0.0],
20✔
347
 [8.0, 4.13, 6283.08],
348
 [1.0, 3.84, 12566.15]]
349

350

351

352
def julian_day_dt(year, month, day, hour, minute, second, microsecond):
20✔
353
    """This is the original way to calculate the julian day from the NREL paper.
354

355
    However, it is much faster to convert to unix/epoch time and then convert to
356
    julian day. Note that the date must be UTC.
357
    """
358
    # Not used anywhere!
359
    if month <= 2:
×
360
        year = year-1
×
361
        month = month+12
×
362
    a = int(year/100)
×
363
    b = 2 - a + int(a * 0.25)
×
364
    frac_of_day = (microsecond + (second + minute * 60 + hour * 3600)
×
365
                   ) * 1.0 / (3600*24)
366
    d = day + frac_of_day
×
367
    jd = (int(365.25 * (year + 4716)) + int(30.6001 * (month + 1)) + d +
×
368
          b - 1524.5)
369
    return jd
×
370

371

372
def julian_day(unixtime):
20✔
373
    jd = unixtime*1.1574074074074073e-05 + 2440587.5
20✔
374
#    jd = unixtime/86400.0 + 2440587.5
375
    return jd
20✔
376

377

378
def julian_ephemeris_day(julian_day, delta_t):
20✔
379
    jde = julian_day + delta_t*1.1574074074074073e-05
20✔
380
#    jde = julian_day + delta_t * 1.0 / 86400.0
381
    return jde
20✔
382

383

384
def julian_century(julian_day):
20✔
385
    jc = (julian_day - 2451545.0)*2.7378507871321012e-05# * 1.0 / 36525
20✔
386
    return jc
20✔
387

388

389
def julian_ephemeris_century(julian_ephemeris_day):
20✔
390
#    1/36525.0 =  2.7378507871321012e-05
391
    jce = (julian_ephemeris_day - 2451545.0)*2.7378507871321012e-05
20✔
392
    return jce
20✔
393

394

395
def julian_ephemeris_millennium(julian_ephemeris_century):
20✔
396
    jme = julian_ephemeris_century*0.1
20✔
397
    return jme
20✔
398

399

400
def heliocentric_longitude(jme):
20✔
401
    # Might be able to replace this with a pade approximation?
402
    # Looping over rows is probably still faster than (a, b, c)
403
    # Maximum optimization
404
    l0 = 0.0
20✔
405
    l1 = 0.0
20✔
406
    l2 = 0.0
20✔
407
    l3 = 0.0
20✔
408
    l4 = 0.0
20✔
409
    l5 = 0.0
20✔
410
    for row in range(64):
20✔
411
        HELIO_LONG_TABLE_LIST_0_ROW = HELIO_LONG_TABLE_LIST_0[row]
20✔
412
        l0 += (HELIO_LONG_TABLE_LIST_0_ROW[0]
20✔
413
               * cos(HELIO_LONG_TABLE_LIST_0_ROW[1]
414
                        + HELIO_LONG_TABLE_LIST_0_ROW[2] * jme)
415
               )
416
    for row in range(34):
20✔
417
        HELIO_LONG_TABLE_LIST_1_ROW = HELIO_LONG_TABLE_LIST_1[row]
20✔
418
        l1 += (HELIO_LONG_TABLE_LIST_1_ROW[0]
20✔
419
               * cos(HELIO_LONG_TABLE_LIST_1_ROW[1]
420
                        + HELIO_LONG_TABLE_LIST_1_ROW[2] * jme)
421
               )
422
    for row in range(20):
20✔
423
        HELIO_LONG_TABLE_LIST_2_ROW = HELIO_LONG_TABLE_LIST_2[row]
20✔
424
        l2 += (HELIO_LONG_TABLE_LIST_2_ROW[0]
20✔
425
               * cos(HELIO_LONG_TABLE_LIST_2_ROW[1]
426
                        + HELIO_LONG_TABLE_LIST_2_ROW[2] * jme)
427
               )
428

429
    for row in range(7):
20✔
430
        HELIO_LONG_TABLE_LIST_3_ROW = HELIO_LONG_TABLE_LIST_3[row]
20✔
431
        l3 += (HELIO_LONG_TABLE_LIST_3_ROW[0]
20✔
432
               * cos(HELIO_LONG_TABLE_LIST_3_ROW[1]
433
                        + HELIO_LONG_TABLE_LIST_3_ROW[2] * jme)
434
               )
435
    for row in range(3):
20✔
436
        HELIO_LONG_TABLE_LIST_4_ROW = HELIO_LONG_TABLE_LIST_4[row]
20✔
437
        l4 += (HELIO_LONG_TABLE_LIST_4_ROW[0]
20✔
438
               * cos(HELIO_LONG_TABLE_LIST_4_ROW[1]
439
                        + HELIO_LONG_TABLE_LIST_4_ROW[2] * jme)
440
               )
441
#    l5 = (HELIO_LONG_TABLE_LIST_5[0][0]*cos(HELIO_LONG_TABLE_LIST_5[0][1]))
442
    l5 = -0.9999987317275395
20✔
443
    l_rad = (jme*(jme*(jme*(jme*(jme*l5 + l4) + l3) + l2) + l1) + l0)*1E-8
20✔
444
    l = rad2deg*l_rad
20✔
445
    return l % 360
20✔
446

447

448
def heliocentric_latitude(jme):
20✔
449
    b0 = 0.0
20✔
450
    b1 = 0.0
20✔
451
    for row in range(5):
20✔
452
        HELIO_LAT_TABLE_LIST_0_ROW = HELIO_LAT_TABLE_LIST_0[row]
20✔
453
        b0 += (HELIO_LAT_TABLE_LIST_0_ROW[0]
20✔
454
               * cos(HELIO_LAT_TABLE_LIST_0_ROW[1]
455
                        + HELIO_LAT_TABLE_LIST_0_ROW[2] * jme)
456
               )
457
    HELIO_LAT_TABLE_LIST_1_ROW = HELIO_LAT_TABLE_LIST_1[0]
20✔
458
    b1 += (HELIO_LAT_TABLE_LIST_1_ROW[0]
20✔
459
           * cos(HELIO_LAT_TABLE_LIST_1_ROW[1]
460
                    + HELIO_LAT_TABLE_LIST_1_ROW[2] * jme))
461

462
    HELIO_LAT_TABLE_LIST_1_ROW = HELIO_LAT_TABLE_LIST_1[1]
20✔
463
    b1 += (HELIO_LAT_TABLE_LIST_1_ROW[0]
20✔
464
           * cos(HELIO_LAT_TABLE_LIST_1_ROW[1]
465
                    + HELIO_LAT_TABLE_LIST_1_ROW[2] * jme))
466
    b_rad = (b0 + b1 * jme)*1E-8
20✔
467
    b = rad2deg*b_rad
20✔
468
    return b
20✔
469

470

471
def heliocentric_radius_vector(jme):
20✔
472
    # no optimizations can be thought of
473
    r0 = 0.0
20✔
474
    r1 = 0.0
20✔
475
    r2 = 0.0
20✔
476
    r3 = 0.0
20✔
477
    r4 = 0.0
20✔
478
    # Would be possible to save a few multiplies of table1row[2]*jme, table1row[1]*jme as they are dups
479
    for row in range(40):
20✔
480
        table0row = HELIO_RADIUS_TABLE_LIST_0[row]
20✔
481
        r0 += (table0row[0]*cos(table0row[1] + table0row[2]*jme))
20✔
482
    for row in range(10):
20✔
483
        table1row = HELIO_RADIUS_TABLE_LIST_1[row]
20✔
484
        r1 += (table1row[0]*cos(table1row[1] + table1row[2]*jme))
20✔
485
    for row in range(6):
20✔
486
        table2row = HELIO_RADIUS_TABLE_LIST_2[row]
20✔
487
        r2 += (table2row[0]*cos(table2row[1] + table2row[2]*jme))
20✔
488

489
    table3row = HELIO_RADIUS_TABLE_LIST_3[0]
20✔
490
    r3 += (table3row[0]*cos(table3row[1] + table3row[2]*jme))
20✔
491
    table3row = HELIO_RADIUS_TABLE_LIST_3[1]
20✔
492
    r3 += (table3row[0]*cos(table3row[1] + table3row[2]*jme))
20✔
493

494
#    table4row = HELIO_RADIUS_TABLE_LIST_4[0]
495
#    r4 = (table4row[0]*cos(table4row[1] + table4row[2]*jme))
496
    r4 = (4.0*cos(2.56 + 6283.08*jme))
20✔
497
    return (jme*(jme*(jme*(jme*r4 + r3) + r2) + r1) + r0)*1E-8
20✔
498

499

500
def geocentric_longitude(heliocentric_longitude):
20✔
501
    theta = heliocentric_longitude + 180.0
20✔
502
    return theta % 360
20✔
503

504

505
def geocentric_latitude(heliocentric_latitude):
20✔
506
    beta = -heliocentric_latitude
20✔
507
    return beta
20✔
508

509

510
def mean_elongation(julian_ephemeris_century):
20✔
511
    return (julian_ephemeris_century*(julian_ephemeris_century
20✔
512
            *(5.27776898149614e-6*julian_ephemeris_century - 0.0019142)
513
            + 445267.11148) + 297.85036)
514
#    x0 = (297.85036
515
#          + 445267.111480 * julian_ephemeris_century
516
#          - 0.0019142 * julian_ephemeris_century**2
517
#          + julian_ephemeris_century**3 / 189474.0)
518
#    return x0
519

520

521
def mean_anomaly_sun(julian_ephemeris_century):
20✔
522
    return (julian_ephemeris_century*(julian_ephemeris_century*(
20✔
523
            -3.33333333333333e-6*julian_ephemeris_century - 0.0001603)
524
            + 35999.05034) + 357.52772)
525
#    x1 = (357.52772
526
#          + 35999.050340 * julian_ephemeris_century
527
#          - 0.0001603 * julian_ephemeris_century**2
528
#          - julian_ephemeris_century**3 / 300000.0)
529
#    return x1
530

531

532
def mean_anomaly_moon(julian_ephemeris_century):
20✔
533
    return (julian_ephemeris_century*(julian_ephemeris_century*(
20✔
534
            1.77777777777778e-5*julian_ephemeris_century + 0.0086972)
535
        + 477198.867398) + 134.96298)
536
#    x2 = (134.96298
537
#          + 477198.867398 * julian_ephemeris_century
538
#          + 0.0086972 * julian_ephemeris_century**2
539
#          + julian_ephemeris_century**3 / 56250)
540
#    return x2
541

542

543
def moon_argument_latitude(julian_ephemeris_century):
20✔
544
    return julian_ephemeris_century*(julian_ephemeris_century*(
20✔
545
            3.05558101873071e-6*julian_ephemeris_century - 0.0036825)
546
        + 483202.017538) + 93.27191
547
#    x3 = (93.27191
548
#          + 483202.017538 * julian_ephemeris_century
549
#          - 0.0036825 * julian_ephemeris_century**2
550
#          + julian_ephemeris_century**3 / 327270)
551
#    return x3
552

553

554
def moon_ascending_longitude(julian_ephemeris_century):
20✔
555
    return (julian_ephemeris_century*(julian_ephemeris_century*(
20✔
556
            2.22222222222222e-6*julian_ephemeris_century + 0.0020708)
557
            - 1934.136261) + 125.04452)
558
#    x4 = (125.04452
559
#          - 1934.136261 * julian_ephemeris_century
560
#          + 0.0020708 * julian_ephemeris_century**2
561
#          + julian_ephemeris_century**3 / 450000)
562
#    return x4
563

564

565
def longitude_obliquity_nutation(julian_ephemeris_century, x0, x1, x2, x3, x4):
20✔
566
    x0, x1, x2, x3, x4 = deg2rad*x0, deg2rad*x1, deg2rad*x2, deg2rad*x3, deg2rad*x4
20✔
567
    delta_psi_sum = 0.0
20✔
568
    delta_eps_sum = 0.0
20✔
569
    # If the sincos formulation is used, the speed up is ~8% with numba.
570
    for row in range(63):
20✔
571
        arg = (NUTATION_YTERM_LIST_0[row]*x0 +
20✔
572
               NUTATION_YTERM_LIST_1[row]*x1 +
573
               NUTATION_YTERM_LIST_2[row]*x2 +
574
               NUTATION_YTERM_LIST_3[row]*x3 +
575
               NUTATION_YTERM_LIST_4[row]*x4)
576
        arr = NUTATION_ABCD_LIST[row]
20✔
577
        sinarg, cosarg = sincos(arg)
20✔
578
#        sinarg = sin(arg)
579
#        cosarg = sqrt(1.0 - sinarg*sinarg)
580
        t0 = (arr[0] + julian_ephemeris_century*arr[1])
20✔
581
        delta_psi_sum += t0*sinarg
20✔
582
#        delta_psi_sum += t0*sin(arg)
583
        t0 = (arr[2] + julian_ephemeris_century*arr[3])
20✔
584
        delta_eps_sum += t0*cosarg
20✔
585
#        delta_eps_sum += t0*cos(arg)
586
    delta_psi = delta_psi_sum/36000000.0
20✔
587
    delta_eps = delta_eps_sum/36000000.0
20✔
588
    res = [0.0]*2
20✔
589
    res[0] = delta_psi
20✔
590
    res[1] = delta_eps
20✔
591
    return res
20✔
592

593
def longitude_nutation(julian_ephemeris_century, x0, x1, x2, x3, x4):
20✔
594
    x0, x1, x2, x3, x4 = deg2rad*x0, deg2rad*x1, deg2rad*x2, deg2rad*x3, deg2rad*x4
×
595
    delta_psi_sum = 0.0
×
596
    for row in range(63):
×
597
#       # None can be skipped but the multiplies can be with effort -2 to 2 with dict - just might be slower
598
        argsin = (NUTATION_YTERM_LIST_0[row]*x0 +
×
599
                  NUTATION_YTERM_LIST_1[row]*x1 +
600
                  NUTATION_YTERM_LIST_2[row]*x2 +
601
                  NUTATION_YTERM_LIST_3[row]*x3 +
602
                  NUTATION_YTERM_LIST_4[row]*x4)
603
        term = (NUTATION_ABCD_LIST[row][0] + NUTATION_ABCD_LIST[row][1]
×
604
                * julian_ephemeris_century)*sin(argsin)
605
        delta_psi_sum += term
×
606
    delta_psi = delta_psi_sum/36000000.0
×
607
    return delta_psi
×
608

609

610
def obliquity_nutation(julian_ephemeris_century, x0, x1, x2, x3, x4):
20✔
611
    delta_eps_sum = 0.0
×
612
    x0, x1, x2, x3, x4 = deg2rad*x0, deg2rad*x1, deg2rad*x2, deg2rad*x3, deg2rad*x4
×
613
    for row in range(63):
×
614
        argcos = (NUTATION_YTERM_LIST_0[row]*x0 +
×
615
                  NUTATION_YTERM_LIST_1[row]*x1 +
616
                  NUTATION_YTERM_LIST_2[row]*x2 +
617
                  NUTATION_YTERM_LIST_3[row]*x3 +
618
                  NUTATION_YTERM_LIST_4[row]*x4)
619
        term = (NUTATION_ABCD_LIST[row][2]
×
620
               + NUTATION_ABCD_LIST[row][3]*julian_ephemeris_century)*cos(argcos)
621
        delta_eps_sum += term
×
622
    delta_eps = delta_eps_sum/36000000.0
×
623
    return delta_eps
×
624

625

626
def mean_ecliptic_obliquity(julian_ephemeris_millennium):
20✔
627
    U = 0.1*julian_ephemeris_millennium
20✔
628
    e0 =  (U*(U*(U*(U*(U*(U*(U*(U*(U*(2.45*U + 5.79) + 27.87) + 7.12) - 39.05)
20✔
629
           - 249.67) - 51.38) + 1999.25) - 1.55) - 4680.93) + 84381.448)
630
    return e0
20✔
631

632

633
def true_ecliptic_obliquity(mean_ecliptic_obliquity, obliquity_nutation):
20✔
634
#    e0 = mean_ecliptic_obliquity
635
#    deleps = obliquity_nutation
636
    return mean_ecliptic_obliquity*0.0002777777777777778  + obliquity_nutation
20✔
637
#    e = e0/3600.0 + deleps
638
#    return e
639

640

641
def aberration_correction(earth_radius_vector):
20✔
642
    # -20.4898 / (3600)
643
    deltau = -0.005691611111111111/earth_radius_vector
20✔
644
    return deltau
20✔
645

646

647
def apparent_sun_longitude(geocentric_longitude, longitude_nutation,
20✔
648
                           aberration_correction):
649
    lamd = geocentric_longitude + longitude_nutation + aberration_correction
20✔
650
    return lamd
20✔
651

652

653
def mean_sidereal_time(julian_day, julian_century):
20✔
654
    julian_century2 = julian_century*julian_century
20✔
655
    v0 = (280.46061837 + 360.98564736629*(julian_day - 2451545.0)
20✔
656
          + 0.000387933*julian_century2
657
          - julian_century2*julian_century/38710000.0)
658
    return v0 % 360.0
20✔
659

660

661
def apparent_sidereal_time(mean_sidereal_time, longitude_nutation,
20✔
662
                           true_ecliptic_obliquity):
663
    v = mean_sidereal_time + longitude_nutation*cos(deg2rad*true_ecliptic_obliquity)
20✔
664
    return v
20✔
665

666

667
def geocentric_sun_right_ascension(apparent_sun_longitude,
20✔
668
                                   true_ecliptic_obliquity,
669
                                   geocentric_latitude):
670
    num = (sin(deg2rad*apparent_sun_longitude)
20✔
671
           * cos(deg2rad*true_ecliptic_obliquity)
672
           - tan(deg2rad*geocentric_latitude)
673
           * sin(deg2rad*true_ecliptic_obliquity))
674
    alpha = degrees(atan2(num, cos(
20✔
675
        deg2rad*apparent_sun_longitude)))
676
    return alpha % 360
20✔
677

678

679
def geocentric_sun_declination(apparent_sun_longitude, true_ecliptic_obliquity,
20✔
680
                               geocentric_latitude):
681
    delta = degrees(asin(sin(deg2rad*geocentric_latitude) *
20✔
682
                                 cos(deg2rad*true_ecliptic_obliquity) +
683
                                 cos(deg2rad*geocentric_latitude) *
684
                                 sin(deg2rad*true_ecliptic_obliquity) *
685
                                 sin(deg2rad*apparent_sun_longitude)))
686
    return delta
20✔
687

688

689
def local_hour_angle(apparent_sidereal_time, observer_longitude,
20✔
690
                     sun_right_ascension):
691
    """Measured westward from south."""
692
    H = apparent_sidereal_time + observer_longitude - sun_right_ascension
20✔
693
    return H % 360
20✔
694

695

696
def equatorial_horizontal_parallax(earth_radius_vector):
20✔
697
    return 8.794 / (3600.0 * earth_radius_vector)
20✔
698

699

700
def uterm(observer_latitude):
20✔
701
    u = atan(0.99664719*tan(deg2rad*observer_latitude))
20✔
702
    return u
20✔
703

704

705
def xterm(u, observer_latitude, observer_elevation):
20✔
706
    # 1/6378140.0 = const
707
    x = (cos(u) + observer_elevation*1.5678552054360676e-07*cos(deg2rad*observer_latitude))
20✔
708
    return x
20✔
709

710

711
def yterm(u, observer_latitude, observer_elevation):
20✔
712
    # 1/6378140.0 = const
713
    y = (0.99664719 * sin(u) + observer_elevation*1.5678552054360676e-07
20✔
714
         * sin(deg2rad*observer_latitude))
715
    return y
20✔
716

717

718
def parallax_sun_right_ascension(xterm, equatorial_horizontal_parallax,
20✔
719
                                 local_hour_angle, geocentric_sun_declination):
720
    x0 = sin(deg2rad*equatorial_horizontal_parallax)
20✔
721
    x1 = deg2rad*local_hour_angle
20✔
722
    num = -xterm*x0*sin(x1)
20✔
723
    denom = (cos(deg2rad*geocentric_sun_declination) - xterm*x0 * cos(x1))
20✔
724
    delta_alpha = degrees(atan2(num, denom))
20✔
725
    return delta_alpha
20✔
726

727

728
def topocentric_sun_right_ascension(geocentric_sun_right_ascension,
20✔
729
                                    parallax_sun_right_ascension):
730
    alpha_prime = geocentric_sun_right_ascension + parallax_sun_right_ascension
×
731
    return alpha_prime
×
732

733

734
def topocentric_sun_declination(geocentric_sun_declination, xterm, yterm,
20✔
735
                                equatorial_horizontal_parallax,
736
                                parallax_sun_right_ascension,
737
                                local_hour_angle):
738
    x0 = sin(deg2rad*equatorial_horizontal_parallax)
20✔
739
    num = ((sin(deg2rad*geocentric_sun_declination) - yterm
20✔
740
            * x0)
741
           * cos(deg2rad*parallax_sun_right_ascension))
742
    denom = (cos(deg2rad*geocentric_sun_declination) - xterm
20✔
743
             * x0
744
             * cos(deg2rad*local_hour_angle))
745
    delta = degrees(atan2(num, denom))
20✔
746
    return delta
20✔
747

748

749
def topocentric_local_hour_angle(local_hour_angle,
20✔
750
                                 parallax_sun_right_ascension):
751
    H_prime = local_hour_angle - parallax_sun_right_ascension
20✔
752
    return H_prime
20✔
753

754

755
def topocentric_elevation_angle_without_atmosphere(observer_latitude,
20✔
756
                                                   topocentric_sun_declination,
757
                                                   topocentric_local_hour_angle
758
                                                   ):
759
    observer_latitude = observer_latitude
20✔
760
    topocentric_sun_declination = topocentric_sun_declination
20✔
761
    topocentric_local_hour_angle = topocentric_local_hour_angle
20✔
762

763
    r_observer_latitude = deg2rad*observer_latitude
20✔
764
    r_topocentric_sun_declination = deg2rad*topocentric_sun_declination
20✔
765
    e0 = degrees(asin(
20✔
766
        sin(r_observer_latitude)
767
        * sin(r_topocentric_sun_declination)
768
        + cos(r_observer_latitude)
769
        * cos(r_topocentric_sun_declination)
770
        * cos(deg2rad*topocentric_local_hour_angle)))
771

772
    return e0
20✔
773

774

775
def atmospheric_refraction_correction(local_pressure, local_temp,
20✔
776
                                      topocentric_elevation_angle_wo_atmosphere,
777
                                      atmos_refract):
778
    # switch sets delta_e when the sun is below the horizon
779
    switch = topocentric_elevation_angle_wo_atmosphere >= -1.0 * (
20✔
780
        0.26667 + atmos_refract)
781
    delta_e = ((local_pressure / 1010.0) * (283.0 / (273.0 + local_temp))
20✔
782
               * 1.02 / (60.0 * tan(deg2rad*(
783
                   topocentric_elevation_angle_wo_atmosphere
784
                   + 10.3 / (topocentric_elevation_angle_wo_atmosphere
785
                             + 5.11))))) * switch
786
    return delta_e
20✔
787

788

789
def topocentric_elevation_angle(topocentric_elevation_angle_without_atmosphere,
20✔
790
                                atmospheric_refraction_correction):
791
    e = (topocentric_elevation_angle_without_atmosphere
20✔
792
         + atmospheric_refraction_correction)
793
    return e
20✔
794

795

796
def topocentric_zenith_angle(topocentric_elevation_angle):
20✔
797
    theta = 90.0 - topocentric_elevation_angle
20✔
798
    return theta
20✔
799

800

801
def topocentric_astronomers_azimuth(topocentric_local_hour_angle,
20✔
802
                                    topocentric_sun_declination,
803
                                    observer_latitude):
804
    num = sin(deg2rad*topocentric_local_hour_angle)
20✔
805
    denom = (cos(deg2rad*topocentric_local_hour_angle)
20✔
806
             * sin(deg2rad*observer_latitude)
807
             - tan(deg2rad*topocentric_sun_declination)
808
             * cos(deg2rad*observer_latitude))
809
    gamma = degrees(atan2(num, denom))
20✔
810

811
    return gamma % 360.0
20✔
812

813

814
def topocentric_azimuth_angle(topocentric_astronomers_azimuth):
20✔
815
    phi = topocentric_astronomers_azimuth + 180.0
20✔
816
    return phi % 360.0
20✔
817

818

819
def sun_mean_longitude(julian_ephemeris_millennium):
20✔
820
    M = julian_ephemeris_millennium*(julian_ephemeris_millennium*(
20✔
821
            julian_ephemeris_millennium*(julian_ephemeris_millennium*(
822
                    -5.0e-7*julian_ephemeris_millennium - 6.5359477124183e-5)
823
        + 2.00276381406341e-5) + 0.03032028) + 360007.6982779) + 280.4664567
824
    return M
20✔
825

826

827
#@jcompile('float64(float64, float64, float64, float64)', nopython=True)
828
def equation_of_time(sun_mean_longitude, geocentric_sun_right_ascension,
20✔
829
                     longitude_nutation, true_ecliptic_obliquity):
830
    term = cos(deg2rad*true_ecliptic_obliquity)
20✔
831
    E = (sun_mean_longitude - 0.0057183 - geocentric_sun_right_ascension +
20✔
832
         longitude_nutation * term)
833
    # limit between 0 and 360
834
    E = E % 360
20✔
835
    # convert to minutes
836
    E *= 4.0
20✔
837
    greater = E > 20.0
20✔
838
    less = E < -20.0
20✔
839
    other = (E <= 20.0) & (E >= -20.0)
20✔
840
    E = greater * (E - 1440.0) + less * (E + 1440.0) + other * E
20✔
841
    return E
20✔
842

843

844
def earthsun_distance(unixtime, delta_t):
20✔
845
    """Calculates the distance from the earth to the sun using the NREL SPA
846
    algorithm described in [1].
847

848
    Parameters
849
    ----------
850
    unixtime : numpy array
851
        Array of unix/epoch timestamps to calculate solar position for.
852
        Unixtime is the number of seconds since Jan. 1, 1970 00:00:00 UTC.
853
        A pandas.DatetimeIndex is easily converted using .astype(np.int64)/10**9
854
    delta_t : float
855
        Difference between terrestrial time and UT. USNO has tables.
856

857
    Returns
858
    -------
859
    R : array
860
        Earth-Sun distance in AU.
861

862
    References
863
    ----------
864
    [1] Reda, I., Andreas, A., 2003. Solar position algorithm for solar
865
    radiation applications. Technical report: NREL/TP-560- 34302. Golden,
866
    USA, http://www.nrel.gov.
867
    """
868
    jd = julian_day(unixtime)
20✔
869
    jde = julian_ephemeris_day(jd, delta_t)
20✔
870
    jce = julian_ephemeris_century(jde)
20✔
871
    jme = julian_ephemeris_millennium(jce)
20✔
872
    R = heliocentric_radius_vector(jme)
20✔
873
    return R
20✔
874

875

876
def solar_position(unixtime, lat, lon, elev, pressure, temp, delta_t,
20✔
877
                   atmos_refract, sst=False):
878
    """Calculate the solar position using the NREL SPA algorithm described in
879
    [1].
880

881
    If numba is installed, the functions can be compiled
882
    and the code runs quickly. If not, the functions
883
    still evaluate but use numpy instead.
884

885
    Parameters
886
    ----------
887
    unixtime : numpy array
888
        Array of unix/epoch timestamps to calculate solar position for.
889
        Unixtime is the number of seconds since Jan. 1, 1970 00:00:00 UTC.
890
        A pandas.DatetimeIndex is easily converted using .astype(np.int64)/10**9
891
    lat : float
892
        Latitude to calculate solar position for
893
    lon : float
894
        Longitude to calculate solar position for
895
    elev : float
896
        Elevation of location in meters
897
    pressure : int or float
898
        avg. yearly pressure at location in millibars;
899
        used for atmospheric correction
900
    temp : int or float
901
        avg. yearly temperature at location in
902
        degrees C; used for atmospheric correction
903
    delta_t : float, optional
904
        If delta_t is None, uses spa.calculate_deltat
905
        using time.year and time.month from pandas.DatetimeIndex.
906
        For most simulations specifying delta_t is sufficient.
907
        Difference between terrestrial time and UT1.
908
        *Note: delta_t = None will break code using nrel_numba,
909
        this will be fixed in a future version.
910
        By default, use USNO historical data and predictions
911
    atmos_refrac : float, optional
912
        The approximate atmospheric refraction (in degrees)
913
        at sunrise and sunset.
914
    numthreads: int, optional, default None
915
        Number of threads to use for computation if numba>=0.17
916
        is installed.
917
    sst : bool, default False
918
        If True, return only data needed for sunrise, sunset, and transit
919
        calculations.
920

921
    Returns
922
    -------
923
    list with elements:
924
        apparent zenith,
925
        zenith,
926
        elevation,
927
        apparent_elevation,
928
        azimuth,
929
        equation_of_time
930

931
    References
932
    ----------
933
    .. [1] I. Reda and A. Andreas, Solar position algorithm for solar radiation
934
    applications. Solar Energy, vol. 76, no. 5, pp. 577-589, 2004.
935
    .. [2] I. Reda and A. Andreas, Corrigendum to Solar position algorithm for
936
    solar radiation applications. Solar Energy, vol. 81, no. 6, p. 838, 2007.
937
    """
938
    jd = julian_day(unixtime)
20✔
939
    jde = julian_ephemeris_day(jd, delta_t)
20✔
940
    jc = julian_century(jd)
20✔
941
    jce = julian_ephemeris_century(jde)
20✔
942
    jme = julian_ephemeris_millennium(jce)
20✔
943
    R = heliocentric_radius_vector(jme)
20✔
944
    L = heliocentric_longitude(jme)
20✔
945
    B = heliocentric_latitude(jme)
20✔
946
    Theta = geocentric_longitude(L)
20✔
947
    beta = geocentric_latitude(B)
20✔
948
    x0 = mean_elongation(jce)
20✔
949
    x1 = mean_anomaly_sun(jce)
20✔
950
    x2 = mean_anomaly_moon(jce)
20✔
951
    x3 = moon_argument_latitude(jce)
20✔
952
    x4 = moon_ascending_longitude(jce)
20✔
953
    delta_psi, delta_epsilon = longitude_obliquity_nutation(jce, x0, x1, x2, x3, x4)
20✔
954

955
    epsilon0 = mean_ecliptic_obliquity(jme)
20✔
956
    epsilon = true_ecliptic_obliquity(epsilon0, delta_epsilon)
20✔
957
    delta_tau = aberration_correction(R)
20✔
958
    lamd = apparent_sun_longitude(Theta, delta_psi, delta_tau)
20✔
959
    v0 = mean_sidereal_time(jd, jc)
20✔
960
    v = apparent_sidereal_time(v0, delta_psi, epsilon)
20✔
961
    alpha = geocentric_sun_right_ascension(lamd, epsilon, beta)
20✔
962
    delta = geocentric_sun_declination(lamd, epsilon, beta)
20✔
963
    if sst: # numba: delete
20✔
964
        return v, alpha, delta # numba: delete
20✔
965

966
    m = sun_mean_longitude(jme)
20✔
967
    eot = equation_of_time(m, alpha, delta_psi, epsilon)
20✔
968
    H = local_hour_angle(v, lon, alpha)
20✔
969
    xi = equatorial_horizontal_parallax(R)
20✔
970
    u = uterm(lat)
20✔
971
    x = xterm(u, lat, elev)
20✔
972
    y = yterm(u, lat, elev)
20✔
973
    delta_alpha = parallax_sun_right_ascension(x, xi, H, delta)
20✔
974
    delta_prime = topocentric_sun_declination(delta, x, y, xi, delta_alpha, H)
20✔
975
    H_prime = topocentric_local_hour_angle(H, delta_alpha)
20✔
976
    e0 = topocentric_elevation_angle_without_atmosphere(lat, delta_prime,
20✔
977
                                                        H_prime)
978
    delta_e = atmospheric_refraction_correction(pressure, temp, e0,
20✔
979
                                                atmos_refract)
980
    e = topocentric_elevation_angle(e0, delta_e)
20✔
981
    theta = topocentric_zenith_angle(e)
20✔
982
    theta0 = topocentric_zenith_angle(e0)
20✔
983
    gamma = topocentric_astronomers_azimuth(H_prime, delta_prime, lat)
20✔
984
    phi = topocentric_azimuth_angle(gamma)
20✔
985
    return [theta, theta0, e, e0, phi, eot]
20✔
986

987

988
try:
20✔
989
    if IS_NUMBA:  # type: ignore # noqa: F821
20!
990
        import threading
19✔
991

992
        import numba
19✔
993
        import numpy as np
19✔
994
        # This is 3x slower without nogil
995
        @numba.njit(nogil=True)
19✔
996
        def solar_position_loop(unixtime, loc_args, out):
12✔
997
            """Loop through the time array and calculate the solar position."""
998
            lat = loc_args[0]
×
999
            lon = loc_args[1]
×
1000
            elev = loc_args[2]
×
1001
            pressure = loc_args[3]
×
1002
            temp = loc_args[4]
×
1003
            delta_t = loc_args[5]
×
1004
            atmos_refract = loc_args[6]
×
1005
            sst = loc_args[7]
×
1006
            esd = loc_args[8]
×
1007

1008
            for i in range(len(unixtime)):
×
1009
                utime = unixtime[i]
×
1010
                jd = julian_day(utime)
×
1011
                jde = julian_ephemeris_day(jd, delta_t)
×
1012
                jc = julian_century(jd)
×
1013
                jce = julian_ephemeris_century(jde)
×
1014
                jme = julian_ephemeris_millennium(jce)
×
1015
                R = heliocentric_radius_vector(jme)
×
1016
                L = heliocentric_longitude(jme)
×
1017
                B = heliocentric_latitude(jme)
×
1018

1019
                Theta = geocentric_longitude(L)
×
1020
                beta = geocentric_latitude(B)
×
1021
                x0 = mean_elongation(jce)
×
1022
                x1 = mean_anomaly_sun(jce)
×
1023
                x2 = mean_anomaly_moon(jce)
×
1024
                x3 = moon_argument_latitude(jce)
×
1025
                x4 = moon_ascending_longitude(jce)
×
1026
#                delta_psi = longitude_nutation(jce, x0, x1, x2, x3, x4)
1027
#                delta_epsilon = obliquity_nutation(jce, x0, x1, x2, x3, x4)
1028
                delta_psi, delta_epsilon = longitude_obliquity_nutation(jce, x0, x1, x2, x3, x4)
×
1029
                epsilon0 = mean_ecliptic_obliquity(jme)
×
1030
                epsilon = true_ecliptic_obliquity(epsilon0, delta_epsilon)
×
1031
                delta_tau = aberration_correction(R)
×
1032
                lamd = apparent_sun_longitude(Theta, delta_psi, delta_tau)
×
1033
                v0 = mean_sidereal_time(jd, jc)
×
1034
                v = apparent_sidereal_time(v0, delta_psi, epsilon)
×
1035
                alpha = geocentric_sun_right_ascension(lamd, epsilon, beta)
×
1036
                delta = geocentric_sun_declination(lamd, epsilon, beta)
×
1037
#                if sst:
1038
#                    out[0, i] = v
1039
#                    out[1, i] = alpha
1040
#                    out[2, i] = delta
1041
#                    continue
1042
                m = sun_mean_longitude(jme)
×
1043
                eot = equation_of_time(m, alpha, delta_psi, epsilon)
×
1044
                H = local_hour_angle(v, lon, alpha)
×
1045
                xi = equatorial_horizontal_parallax(R)
×
1046
                u = uterm(lat)
×
1047
                x = xterm(u, lat, elev)
×
1048
                y = yterm(u, lat, elev)
×
1049
                delta_alpha = parallax_sun_right_ascension(x, xi, H, delta)
×
1050
                delta_prime = topocentric_sun_declination(delta, x, y, xi, delta_alpha,
×
1051
                                                          H)
1052
                H_prime = topocentric_local_hour_angle(H, delta_alpha)
×
1053
                e0 = topocentric_elevation_angle_without_atmosphere(lat, delta_prime,
×
1054
                                                                    H_prime)
1055
                delta_e = atmospheric_refraction_correction(pressure, temp, e0,
×
1056
                                                            atmos_refract)
1057
                e = topocentric_elevation_angle(e0, delta_e)
×
1058
                theta = topocentric_zenith_angle(e)
×
1059
                theta0 = topocentric_zenith_angle(e0)
×
1060
                gamma = topocentric_astronomers_azimuth(H_prime, delta_prime, lat)
×
1061
                phi = topocentric_azimuth_angle(gamma)
×
1062
                out[0, i] = theta
×
1063
                out[1, i] = theta0
×
1064
                out[2, i] = e
×
1065
                out[3, i] = e0
×
1066
                out[4, i] = phi
×
1067
                out[5, i] = eot
×
1068

1069

1070
        def solar_position_numba(unixtime, lat, lon, elev, pressure, temp, delta_t,
19✔
1071
                                 atmos_refract, numthreads, sst=False, esd=False):
1072
            """Calculate the solar position using the numba compiled functions
1073
            and multiple threads.
1074

1075
            Very slow if functions are not numba compiled.
1076
            """
1077
            # these args are the same for each thread
1078
            loc_args = np.array([lat, lon, elev, pressure, temp, delta_t,
×
1079
                                 atmos_refract, sst, esd])
1080

1081
            # construct dims x ulength array to put the results in
1082
            ulength = unixtime.shape[0]
×
1083
            if sst:
×
1084
                dims = 3
×
1085
            elif esd:
×
1086
                dims = 1
×
1087
            else:
1088
                dims = 6
×
1089
            result = np.empty((dims, ulength), dtype=np.float64)
×
1090

1091
            if unixtime.dtype != np.float64:
×
1092
                unixtime = unixtime.astype(np.float64)
×
1093

1094
            if ulength < numthreads:
×
1095
                numthreads = ulength
×
1096

1097
            if numthreads <= 1:
×
1098
                solar_position_loop(unixtime, loc_args, result)
×
1099
                return result
×
1100

1101
            # split the input and output arrays into numthreads chunks
1102
            split0 = np.array_split(unixtime, numthreads)
×
1103
            split2 = np.array_split(result, numthreads, axis=1)
×
1104
            chunks = [[a0, loc_args, split2[i]] for i, a0 in enumerate(split0)]
×
1105
            # Spawn one thread per chunk
1106
            threads = [threading.Thread(target=solar_position_loop, args=chunk)
×
1107
                       for chunk in chunks]
1108
            for thread in threads:
×
1109
                thread.start()
×
1110
            for thread in threads:
×
1111
                thread.join()
×
1112
            return result
×
1113

1114

1115
except:
20✔
1116
    pass
20✔
1117

1118

1119
def transit_sunrise_sunset(dates, lat, lon, delta_t):
20✔
1120
    """Calculate the sun transit, sunrise, and sunset for a set of dates at a
1121
    given location.
1122

1123
    Parameters
1124
    ----------
1125
    dates : array
1126
        Numpy array of ints/floats corresponding to the Unix time
1127
        for the dates of interest, must be midnight UTC (00:00+00:00)
1128
        on the day of interest.
1129
    lat : float
1130
        Latitude of location to perform calculation for
1131
    lon : float
1132
        Longitude of location
1133
    delta_t : float
1134
        Difference between terrestrial time and UT. USNO has tables.
1135

1136
    Returns
1137
    -------
1138
    tuple : (transit, sunrise, sunset) localized to UTC
1139

1140
    >>> transit_sunrise_sunset(1523836800, 51.0486, -114.07, 70.68302220312503)
1141
    (1523907360.3863413, 1523882341.570479, 1523932345.7781625)
1142
    """
1143
    condition = (dates % 86400) != 0.0
20✔
1144
    if condition:
20!
1145
        raise ValueError('Input dates must be at 00:00 UTC')
×
1146

1147
    utday = (dates // 86400) * 86400
20✔
1148
    ttday0 = utday - delta_t
20✔
1149
    ttdayn1 = ttday0 - 86400.0
20✔
1150
    ttdayp1 = ttday0 + 86400.0
20✔
1151

1152
    # index 0 is v, 1 is alpha, 2 is delta
1153
    utday_res = solar_position(utday, 0, 0, 0, 0, 0, delta_t,
20✔
1154
                               0, sst=True)
1155
    v = utday_res[0]
20✔
1156

1157
    ttday0_res = solar_position(ttday0, 0, 0, 0, 0, 0, delta_t,
20✔
1158
                                0, sst=True)
1159
    ttdayn1_res = solar_position(ttdayn1, 0, 0, 0, 0, 0, delta_t,
20✔
1160
                                 0, sst=True)
1161
    ttdayp1_res = solar_position(ttdayp1, 0, 0, 0, 0, 0, delta_t,
20✔
1162
                                 0, sst=True)
1163
    m0 = (ttday0_res[1] - lon - v) / 360
20✔
1164
    cos_arg = ((-0.014543315936696236 - sin(radians(lat)) # sin(radians(-0.8333)) = -0.0145...
20✔
1165
               * sin(radians(ttday0_res[2]))) /
1166
               (cos(radians(lat)) * cos(radians(ttday0_res[2]))))
1167
    if abs(cos_arg) > 1:
20!
1168
        cos_arg = nan
×
1169

1170
    H0 = degrees(acos(cos_arg)) % 180
20✔
1171

1172
    m = [0.0]*3
20✔
1173
    m[0] = m0 % 1
20✔
1174
    m[1] = (m[0] - H0 / 360.0)
20✔
1175
    m[2] = (m[0] + H0 / 360.0)
20✔
1176

1177
    # need to account for fractions of day that may be the next or previous
1178
    # day in UTC
1179
    add_a_day = m[2] >= 1
20✔
1180
    sub_a_day = m[1] < 0
20✔
1181
    m[1] = m[1] % 1
20✔
1182
    m[2] = m[2] % 1
20✔
1183
    vs = [0.0]*3
20✔
1184
    for i in range(3):
20✔
1185
        vs[i] = v + 360.985647*m[i]
20✔
1186
    n = [0.0]*3
20✔
1187
    for i in range(3):
20✔
1188
        n[i] = m[i] + delta_t / 86400.0
20✔
1189

1190
    a = ttday0_res[1] - ttdayn1_res[1]
20✔
1191

1192
    if abs(a) > 2:
20!
1193
        a = a %1
×
1194
    ap = ttday0_res[2] - ttdayn1_res[2]
20✔
1195
    if (abs(ap) > 2):
20!
1196
        ap = ap % 1
×
1197
    b = ttdayp1_res[1] - ttday0_res[1]
20✔
1198
    if (abs(b) > 2):
20!
1199
        b = b % 1
×
1200
    bp = ttdayp1_res[2] - ttday0_res[2]
20✔
1201
    if abs(bp) > 2:
20!
1202
        bp = bp % 1
×
1203

1204

1205
    c = b - a
20✔
1206
    cp = bp - ap
20✔
1207

1208
    alpha_prime = [0.0]*3
20✔
1209
    delta_prime = [0.0]*3
20✔
1210
    Hp = [0.0]*3
20✔
1211
    for i in range(3):
20✔
1212
        alpha_prime[i] = ttday0_res[1] + (n[i] * (a + b + c * n[i]))*0.5
20✔
1213
        delta_prime[i] = ttday0_res[2] + (n[i] * (ap + bp + cp * n[i]))*0.5
20✔
1214
        Hp[i] = (vs[i] + lon - alpha_prime[i]) % 360
20✔
1215
        if Hp[i] >= 180.0:
20✔
1216
            Hp[i] = Hp[i] - 360.0
20✔
1217

1218

1219
    #alpha_prime = ttday0_res[1] + (n * (a + b + c * n)) / 2 # this is vect
1220
    #delta_prime = ttday0_res[2] + (n * (ap + bp + cp * n)) / 2 # this is vect
1221
    #Hp = (vs + lon - alpha_prime) % 360
1222
    #Hp[Hp >= 180] = Hp[Hp >= 180] - 360
1223
    x1 = sin(radians(lat))
20✔
1224
    x2 = cos(radians(lat))
20✔
1225

1226
    h = [0.0]*3
20✔
1227
    for i in range(3):
20✔
1228
        h[i] = degrees(asin(x1*sin(radians(delta_prime[i])) + x2 * cos(radians(delta_prime[i])) * cos(radians(Hp[i]))))
20✔
1229

1230
    T = float((m[0] - Hp[0] / 360.0) * 86400.0)
20✔
1231
    R = float((m[1] + (h[1] + 0.8333) / (360.0 * cos(radians(delta_prime[1])) *
20✔
1232
                                   cos(radians(lat)) *
1233
                                   sin(radians(Hp[1])))) * 86400.0)
1234
    S = float((m[2] + (h[2] + 0.8333) / (360.0 * cos(radians(delta_prime[2])) *
20✔
1235
                                   cos(radians(lat)) *
1236
                                   sin(radians(Hp[2])))) * 86400.0)
1237

1238
    if add_a_day:
20!
1239
        S += 86400.0
20✔
1240
    if sub_a_day:
20!
1241
        R -= 86400.0
×
1242

1243
    transit = T + utday
20✔
1244
    sunrise = R + utday
20✔
1245
    sunset = S + utday
20✔
1246

1247
    return transit, sunrise, sunset
20✔
1248

1249

1250
def calculate_deltat(year, month):
20✔
1251
    y = year + (month - 0.5)/12
20✔
1252
    if (2005 <= year) & (year < 2050):
20✔
1253
        t1 = (y-2000.0)
20✔
1254
        deltat = (62.92+0.32217*t1 + 0.005589*t1*t1)
20✔
1255
    elif  (1986 <= year) & (year < 2005):
20!
1256
        t1 = y - 2000.0
20✔
1257
        deltat = (63.86+0.3345*t1
20✔
1258
                      - 0.060374*t1**2
1259
                      + 0.0017275*t1**3
1260
                      + 0.000651814*t1**4
1261
                      + 0.00002373599*t1**5)
1262
    elif (2050 <= year) & (year < 2150):
×
1263
        deltat = (-20+32*((y-1820)/100)**2
×
1264
                      - 0.5628*(2150-y))
1265
    elif year < -500.0:
×
1266
        deltat = -20.0 + 32*(0.01*(y-1820.0))**2
×
1267

1268
    elif (-500 <= year) & (year < 500):
×
1269
        t1 = y/100
×
1270
        deltat = (10583.6-1014.41*(y/100)
×
1271
                      + 33.78311*(y/100)**2
1272
                      - 5.952053*(y/100)**3
1273
                      - 0.1798452*(y/100)**4
1274
                      + 0.022174192*(y/100)**5
1275
                      + 0.0090316521*(y/100)**6)
1276
    elif (500 <= year) & (year < 1600):
×
1277
        t1 = (y-1000)/100
×
1278
        deltat = (1574.2-556.01*((y-1000)/100)
×
1279
                      + 71.23472*((y-1000)/100)**2
1280
                      + 0.319781*((y-1000)/100)**3
1281
                      - 0.8503463*((y-1000)/100)**4
1282
                      - 0.005050998*((y-1000)/100)**5
1283
                      + 0.0083572073*((y-1000)/100)**6)
1284
    elif (1600 <= year) & (year < 1700):
×
1285
        t1 = (y-1600.0)
×
1286
        deltat = (120-0.9808*(y-1600)
×
1287
                      - 0.01532*(y-1600)**2
1288
                      + (y-1600)**3/7129)
1289
    elif (1700 <= year) & (year < 1800):
×
1290
        t1 = (y - 1700.0)
×
1291
        deltat = (8.83+0.1603*(y-1700)
×
1292
                      - 0.0059285*(y-1700)**2
1293
                      + 0.00013336*(y-1700)**3
1294
                      - (y-1700)**4/1174000)
1295
    elif (1800 <= year) & (year < 1860):
×
1296
        t1 = y - 1800.0
×
1297
        deltat = (13.72-0.332447*(y-1800)
×
1298
                      + 0.0068612*(y-1800)**2
1299
                      + 0.0041116*(y-1800)**3
1300
                      - 0.00037436*(y-1800)**4
1301
                      + 0.0000121272*(y-1800)**5
1302
                      - 0.0000001699*(y-1800)**6
1303
                      + 0.000000000875*(y-1800)**7)
1304
    elif (1860 <= year) & (year < 1900):
×
1305
        t1 = y-1860.0
×
1306
        deltat = (7.62+0.5737*(y-1860)
×
1307
                      - 0.251754*(y-1860)**2
1308
                      + 0.01680668*(y-1860)**3
1309
                      - 0.0004473624*(y-1860)**4
1310
                      + (y-1860)**5/233174)
1311
    elif (1900 <= year) & (year < 1920):
×
1312
        t1 = y - 1900.0
×
1313
        deltat = (-2.79+1.494119*(y-1900)
×
1314
                      - 0.0598939*(y-1900)**2
1315
                      + 0.0061966*(y-1900)**3
1316
                      - 0.000197*(y-1900)**4)
1317
    elif (1920 <= year) & (year < 1941):
×
1318
        t1 = y - 1920.0
×
1319
        deltat = (21.20+0.84493*(y-1920)
×
1320
                      - 0.076100*(y-1920)**2
1321
                      + 0.0020936*(y-1920)**3)
1322
    elif (1941 <= year) & (year < 1961):
×
1323
        t1 = y - 1950.0
×
1324
        deltat = (29.07+0.407*(y-1950)
×
1325
                      - (y-1950)**2/233
1326
                      + (y-1950)**3/2547)
1327
    elif (1961 <= year) & (year < 1986):
×
1328
        t1 = y-1975
×
1329
        deltat = (45.45+1.067*(y-1975)
×
1330
                      - (y-1975)**2/260
1331
                      - (y-1975)**3/718)
1332

1333
    elif year >= 2150:
×
1334
        deltat = -20+32*((y-1820)/100)**2
×
1335

1336

1337
    return deltat
20✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc