Skip to content

Commit 16c6eac

Browse files
veronicaguowholmgren
authored andcommitted
Add analytical azimuth method to solarposition.py. (#349)
* Add analytical azimuth method. * Number linked references. * Add test for solar_azimuth_analytical. * Add an entry in the api.rst file. * Correct for different azimuth convention. * Update test tolerance. Not as accurate when azimuth angle is close to due north. * Added an entry in the v0.4.6 whatsnew file. * Update assert statements. * Update threshold for `atol=0.55` assert statement. * Update test, only when the sun is above horizon. Loops no longer needed.
1 parent 3b0a86b commit 16c6eac

File tree

4 files changed

+81
-0
lines changed

4 files changed

+81
-0
lines changed

docs/sphinx/source/api.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ calculations.
7373
:toctree: generated/
7474

7575
solarposition.solar_zenith_analytical
76+
solarposition.solar_azimuth_analytical
7677
solarposition.declination_spencer71
7778
solarposition.declination_cooper69
7879
solarposition.equation_of_time_spencer71

docs/sphinx/source/whatsnew/v0.4.6.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ Bug fixes
1717
Enhancements
1818
~~~~~~~~~~~~
1919
* Added default values to docstrings of all functions (:issue:`336`)
20+
* Added analytical method that calculates solar azimuth angle (:issue:`291`)
2021

2122
API Changes
2223
~~~~~~~~~~~
@@ -31,6 +32,7 @@ Testing
3132

3233
* Added explicit tests for aoi and aoi_projection functions.
3334
* Update test of `ModelChain.__repr__` to take in account :issue:`352`
35+
* Added a test for solar_azimuth_analytical function.
3436

3537

3638
Contributors
@@ -41,3 +43,4 @@ Contributors
4143
* Alaina Kafkes
4244
* Birgit Schachler
4345
* Jonathan Gaffiot
46+
* Siyan (Veronica) Guo

pvlib/solarposition.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1052,6 +1052,53 @@ def declination_cooper69(dayofyear):
10521052
return np.deg2rad(23.45 * np.sin(day_angle + (2.0 * np.pi / 365.0) * 285.0))
10531053

10541054

1055+
def solar_azimuth_analytical(latitude, hour_angle, declination, zenith):
1056+
"""
1057+
Analytical expression of solar azimuth angle based on spherical
1058+
trigonometry.
1059+
1060+
Parameters
1061+
----------
1062+
latitude : numeric
1063+
Latitude of location in radians.
1064+
hour_angle : numeric
1065+
Hour angle in the local solar time in radians.
1066+
declination : numeric
1067+
Declination of the sun in radians.
1068+
zenith : numeric
1069+
Solar zenith angle in radians.
1070+
1071+
Returns
1072+
-------
1073+
azimuth : numeric
1074+
Solar azimuth angle in radians.
1075+
1076+
References
1077+
----------
1078+
[1] J. A. Duffie and W. A. Beckman, "Solar Engineering of Thermal
1079+
Processes, 3rd Edition" pp. 14, J. Wiley and Sons, New York (2006)
1080+
1081+
[2] J. H. Seinfeld and S. N. Pandis, "Atmospheric Chemistry and Physics"
1082+
p. 132, J. Wiley (1998)
1083+
1084+
[3] `Wikipedia: Solar Azimuth Angle
1085+
<https://en.wikipedia.org/wiki/Solar_azimuth_angle>`_
1086+
1087+
[4] `PVCDROM: Azimuth Angle <http://www.pveducation.org/pvcdrom/2-properties
1088+
-sunlight/azimuth-angle>`_
1089+
1090+
See Also
1091+
--------
1092+
declination_spencer71
1093+
declination_cooper69
1094+
hour_angle
1095+
solar_zenith_analytical
1096+
"""
1097+
return np.sign(hour_angle) * np.abs(np.arccos((np.cos(zenith) * np.sin(
1098+
latitude) - np.sin(declination)) / (np.sin(zenith) * np.cos(
1099+
latitude)))) + np.pi
1100+
1101+
10551102
def solar_zenith_analytical(latitude, hour_angle, declination):
10561103
"""
10571104
Analytical expression of solar zenith angle based on spherical trigonometry.

pvlib/test/test_solarposition.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -420,3 +420,33 @@ def test_analytical_zenith():
420420
zenith_2 = solarposition.solar_zenith_analytical(lat_rad, hour_angle, decl)
421421
assert np.allclose(zenith_1, solar_zenith, atol=0.015)
422422
assert np.allclose(zenith_2, solar_zenith, atol=0.025)
423+
424+
425+
def test_analytical_azimuth():
426+
times = pd.DatetimeIndex(start="1/1/2015 0:00", end="12/31/2015 23:00",
427+
freq="H").tz_localize('Etc/GMT+8')
428+
lat, lon = 37.8, -122.25
429+
lat_rad = np.deg2rad(lat)
430+
output = solarposition.spa_python(times, lat, lon, 100)
431+
solar_azimuth = np.deg2rad(output['azimuth']) # spa
432+
solar_zenith = np.deg2rad(output['zenith'])
433+
# spencer
434+
eot = solarposition.equation_of_time_spencer71(times.dayofyear)
435+
hour_angle = np.deg2rad(solarposition.hour_angle(times, lon, eot))
436+
decl = solarposition.declination_spencer71(times.dayofyear)
437+
zenith = solarposition.solar_zenith_analytical(lat_rad, hour_angle, decl)
438+
azimuth_1 = solarposition.solar_azimuth_analytical(lat_rad, hour_angle,
439+
decl, zenith)
440+
# pvcdrom and cooper
441+
eot = solarposition.equation_of_time_pvcdrom(times.dayofyear)
442+
hour_angle = np.deg2rad(solarposition.hour_angle(times, lon, eot))
443+
decl = solarposition.declination_cooper69(times.dayofyear)
444+
zenith = solarposition.solar_zenith_analytical(lat_rad, hour_angle, decl)
445+
azimuth_2 = solarposition.solar_azimuth_analytical(lat_rad, hour_angle,
446+
decl, zenith)
447+
448+
idx = np.where(solar_zenith < np.pi/2)
449+
assert np.allclose(azimuth_1[idx], solar_azimuth.as_matrix()[idx],
450+
atol=0.01)
451+
assert np.allclose(azimuth_2[idx], solar_azimuth.as_matrix()[idx],
452+
atol=0.017)

0 commit comments

Comments
 (0)