7
7
# Will Holmgren (@wholmgren), University of Arizona, 2014
8
8
# Tony Lorenzo (@alorenzo175), University of Arizona, 2015
9
9
# Cliff hansen (@cwhanse), Sandia National Laboratories, 2018
10
+ # Daniel Lassahn (@meteoDaniel), meteocontrol Energy and Weather Services, 2019
10
11
11
12
from __future__ import division
12
13
import os
18
19
from imp import reload
19
20
except ImportError :
20
21
pass
21
-
22
22
import numpy as np
23
23
import pandas as pd
24
24
import warnings
27
27
from pvlib .tools import datetime_to_djd , djd_to_datetime
28
28
from pvlib ._deprecation import deprecated
29
29
30
-
31
30
NS_PER_HR = 1.e9 * 3600. # nanoseconds per hour
31
+ JULIAN_2000 = 2451544.5
32
+ DT_2000 = dt .datetime (2000 , 1 , 1 )
33
+ TIMESTAMP_2000 = DT_2000 .timestamp ()
34
+ DAY_SECONDS = 60 * 60 * 24
35
+ JULIAN_YEARS = 365.2425
32
36
33
37
34
38
def get_solarposition (time , latitude , longitude ,
@@ -67,6 +71,9 @@ def get_solarposition(time, latitude, longitude,
67
71
68
72
'nrel_c' uses the NREL SPA C code [3]: :py:func:`spa_c`
69
73
74
+ 'spencer_mc' uses the Spencer formula [4] :py:func:`spencer_mc`
75
+
76
+
70
77
temperature : float, default 12
71
78
Degrees C.
72
79
@@ -81,6 +88,9 @@ def get_solarposition(time, latitude, longitude,
81
88
solar radiation applications. Solar Energy, vol. 81, no. 6, p. 838, 2007.
82
89
83
90
[3] NREL SPA code: http://rredc.nrel.gov/solar/codesandalgorithms/spa/
91
+
92
+ [4] Spencer (1972) can be found in "Solar energy fundamentals and
93
+ modeling techniques" from Zekai Sen
84
94
"""
85
95
86
96
if altitude is None and pressure is None :
@@ -114,6 +124,9 @@ def get_solarposition(time, latitude, longitude,
114
124
elif method == 'ephemeris' :
115
125
ephem_df = ephemeris (time , latitude , longitude , pressure , temperature ,
116
126
** kwargs )
127
+ elif method == 'spencer_mc' :
128
+ ephem_df = spencer_mc (time , latitude , longitude )
129
+
117
130
else :
118
131
raise ValueError ('Invalid solar position method' )
119
132
@@ -1440,3 +1453,122 @@ def sun_rise_set_transit_geometric(times, latitude, longitude, declination,
1440
1453
sunset = _local_times_from_hours_since_midnight (times , sunset_hour )
1441
1454
transit = _local_times_from_hours_since_midnight (times , transit_hour )
1442
1455
return sunrise , sunset , transit
1456
+
1457
+
1458
+ def spencer_mc (times , latitude , longitude ):
1459
+ """
1460
+ Calculate the solar position using a python implementation of the
1461
+ Spencer (1972) formulation
1462
+
1463
+ Parameters
1464
+ ----------
1465
+ times : pandas.DatetimeIndex
1466
+ Corresponding timestamps, must be localized to the timezone for the
1467
+ ``latitude`` and ``longitude``.
1468
+ latitude : float
1469
+ Latitude in degrees, positive north of equator, negative to south
1470
+ longitude : float
1471
+ Longitude in degrees, positive east of prime meridian, negative to west
1472
+
1473
+ Returns
1474
+ -------
1475
+ DataFrame
1476
+ The DataFrame will have the following columns:
1477
+ zenith (degrees),
1478
+ elevation (degrees),
1479
+ azimuth (degrees),
1480
+ equation_of_time (seconds),
1481
+ eccentricity,
1482
+ declination (degrees).
1483
+
1484
+ References
1485
+ ----------
1486
+ [4] Spencer (1972) can be found in
1487
+ Sen, Zekai. Solar energy fundamentals and modeling techniques:
1488
+ atmosphere, environment, climate change and renewable energy.
1489
+ Springer Science & Business Media, 2008.
1490
+ """
1491
+
1492
+ julians = datetime2julian (times )
1493
+ julians_2000 = np .asarray (julians , dtype = np .float ) - JULIAN_2000
1494
+
1495
+ lat , lat_deg = np .radians (latitude ), latitude
1496
+ lon , lon_deg = np .radians (longitude ), longitude
1497
+
1498
+ # Compute fractional year (gamma) in radians
1499
+ gamma = 2 * np .pi * (julians_2000 % JULIAN_YEARS ) / JULIAN_YEARS
1500
+ cos_gamma = np .cos (gamma ), np .cos (gamma * 2 ), np .cos (gamma * 3 )
1501
+ sin_gamma = np .sin (gamma ), np .sin (gamma * 2 ), np .sin (gamma * 3 )
1502
+ day_time = (julians_2000 % 1 ) * 24
1503
+
1504
+ # Eccentricity: correction factor of the earth's orbit.
1505
+ eccentricity = (1.00011 + 0.034221 * cos_gamma [0 ] + 0.001280 *
1506
+ sin_gamma [0 ] + 0.000719 * cos_gamma [1 ] + 0.000077
1507
+ * sin_gamma [1 ])
1508
+
1509
+ # declination.
1510
+ declination = (0.006918 - 0.399912 * cos_gamma [0 ] +
1511
+ 0.070257 * sin_gamma [0 ] -
1512
+ 0.006758 * cos_gamma [1 ] + 0.000907 * sin_gamma [1 ] -
1513
+ 0.002697 * cos_gamma [2 ] + 0.001480 * sin_gamma [2 ])
1514
+
1515
+ # Equation of time (difference between standard time and solar time).
1516
+ eot = (0.000075 + 0.001868 * cos_gamma [0 ] - 0.032077 * sin_gamma [0 ] -
1517
+ 0.014615 * cos_gamma [1 ] - 0.040849 * sin_gamma [1 ]) * 229.18
1518
+
1519
+ # True local time
1520
+ tlt = (day_time + lon_deg / 15 + eot / 60 ) % 24 - 12
1521
+
1522
+ # Solar hour angle
1523
+ ha = np .radians (tlt * 15 )
1524
+
1525
+ # Calculate sun elevation.
1526
+ sin_sun_elevation = (
1527
+ np .sin (declination ) * np .sin (lat ) + np .cos (declination ) *
1528
+ np .cos (lat ) * np .cos (ha )
1529
+ )
1530
+
1531
+ # Compute the sun's elevation and zenith angle.
1532
+ elevation = np .arcsin (sin_sun_elevation )
1533
+ zenith = np .pi / 2 - elevation
1534
+
1535
+ # Compute the sun's azimuth angle.
1536
+ y = - (np .sin (lat ) * np .sin (elevation ) - np .sin (declination )) / \
1537
+ (np .cos (lat ) * np .cos (elevation ))
1538
+ azimuth = np .arccos (y )
1539
+
1540
+ # Convert azimuth angle from 0-pi to 0-2pi.
1541
+ tlt_filter = 0 <= tlt
1542
+ azimuth [tlt_filter ] = 2 * np .pi - azimuth [tlt_filter ]
1543
+
1544
+ result = pd .DataFrame ({'zenith' : np .degrees (zenith ),
1545
+ 'elevation' : np .degrees (elevation ),
1546
+ 'azimuth' : np .degrees (azimuth ),
1547
+ 'eccentricity' : eccentricity ,
1548
+ 'declination' : declination ,
1549
+ 'equation_of_time' : eot },
1550
+ index = times )
1551
+ return result
1552
+
1553
+
1554
+ def datetime2julian (times ):
1555
+ """
1556
+ Transforms pd.DateTimeIndex to Julian days
1557
+
1558
+ Parameters
1559
+ ----------
1560
+ times : pandas.DatetimeIndex
1561
+ Corresponding timestamps, must be localized to the timezone for the
1562
+ ``latitude`` and ``longitude``.
1563
+
1564
+ Returns
1565
+ -------
1566
+ Float64Index
1567
+ The float index contains julian dates
1568
+ """
1569
+
1570
+ delta = times - DT_2000
1571
+ return (
1572
+ JULIAN_2000 +
1573
+ delta .days + (delta .seconds + delta .microseconds / 1e6 ) / DAY_SECONDS
1574
+ )
0 commit comments