From 09a95d49aa855740fa300533f1707759fcadaba8 Mon Sep 17 00:00:00 2001 From: Omar Bahamida Date: Sun, 15 Jun 2025 21:52:40 +0200 Subject: [PATCH 01/15] docs: update solar angle definitions in nomenclature - Add detailed definitions for solar angles with range constraints - Clarify pvlib's east-of-north convention for azimuth angles - Add cross-references between related terms using :term: directive - Add coordinate system conventions for latitude/longitude - Enhance existing angle definitions with usage notes and examples This commit addresses issue #2448 by migrating angle definitions and conventions from parameter descriptions to the nomenclature page. (cherry picked from commit 16435c79d038375da476e7c1b09ef82c9dfb77e8) --- .../source/user_guide/extras/nomenclature.rst | 34 +++++++---- example.py | 56 +++++++++++++++++++ 2 files changed, 80 insertions(+), 10 deletions(-) create mode 100644 example.py diff --git a/docs/sphinx/source/user_guide/extras/nomenclature.rst b/docs/sphinx/source/user_guide/extras/nomenclature.rst index 4c983dc912..b4922ee2e2 100644 --- a/docs/sphinx/source/user_guide/extras/nomenclature.rst +++ b/docs/sphinx/source/user_guide/extras/nomenclature.rst @@ -22,16 +22,23 @@ There is a convention on consistent variable names throughout the library: aoi Angle of incidence. Angle between the surface normal vector and the - vector pointing towards the sun’s center + vector pointing towards the sun's center. Must be >=0 and <=180 degrees. + When the sun is behind the surface, the value is >90 degrees. aoi_projection - cos(aoi) + cos(aoi). When the sun is behind the surface, the value is negative. + For many uses, negative values must be set to zero. ape Average photon energy apparent_zenith - Refraction-corrected solar zenith angle in degrees + Refraction-corrected solar zenith angle in degrees. Must be >=0 and <=180. + This angle accounts for atmospheric refraction effects. + + apparent_elevation + Refraction-corrected solar elevation angle in degrees. Must be >=-90 and <=90. + This is the complement of apparent_zenith (90 - apparent_zenith). bhi Beam/direct horizontal irradiance @@ -87,10 +94,10 @@ There is a convention on consistent variable names throughout the library: Sandia Array Performance Model IV curve parameters latitude - Latitude + Latitude in decimal degrees. Positive north of equator, negative to south. longitude - Longitude + Longitude in decimal degrees. Positive east of prime meridian, negative to west. pac, ac AC power @@ -141,10 +148,14 @@ There is a convention on consistent variable names throughout the library: Diode saturation current solar_azimuth - Azimuth angle of the sun in degrees East of North + Azimuth angle of the sun in degrees East of North. Must be >=0 and <=360. + The convention is defined as degrees east of north (e.g. North = 0°, + East = 90°, South = 180°, West = 270°). solar_zenith - Zenith angle of the sun in degrees + Zenith angle of the sun in degrees. Must be >=0 and <=180. + This is the angle between the sun's rays and the vertical direction. + This is the complement of :term:`solar_elevation` (90 - elevation). spectra spectra_components @@ -154,11 +165,14 @@ There is a convention on consistent variable names throughout the library: is composed of direct and diffuse components. surface_azimuth - Azimuth angle of the surface + Azimuth angle of the surface in degrees East of North. Must be >=0 and <=360. + The convention is defined as degrees east (clockwise) of north. This is pvlib's + convention; other tools may use different conventions. For example, North = 0°, + East = 90°, South = 180°, West = 270°. surface_tilt - Panel tilt from horizontal [°]. For example, a surface facing up = 0°, - surface facing horizon = 90°. + Panel tilt from horizontal [°]. Must be >=0 and <=180. + For example, a surface facing up = 0°, surface facing horizon = 90°. temp_air Temperature of the air diff --git a/example.py b/example.py new file mode 100644 index 0000000000..7dbd3cc3d7 --- /dev/null +++ b/example.py @@ -0,0 +1,56 @@ +# Simple pvlib demonstration script +import pvlib +import pandas as pd +from datetime import datetime, timedelta +import matplotlib.pyplot as plt + +# Create a location object for a specific site +location = pvlib.location.Location( + latitude=40.0, # New York City latitude + longitude=-74.0, # New York City longitude + tz='America/New_York', + altitude=10 # meters above sea level +) + +# Calculate solar position for a day +date = datetime(2024, 3, 15) +times = pd.date_range(date, date + timedelta(days=1), freq='1H', tz=location.tz) +solpos = location.get_solarposition(times) + +# Plot solar position +plt.figure(figsize=(10, 6)) +plt.plot(solpos.index, solpos['elevation'], label='Elevation') +plt.plot(solpos.index, solpos['azimuth'], label='Azimuth') +plt.title('Solar Position for New York City on March 15, 2024') +plt.xlabel('Time') +plt.ylabel('Angle (degrees)') +plt.legend() +plt.grid(True) +plt.show() + +# Calculate clear sky irradiance +clearsky = location.get_clearsky(times) + +# Plot clear sky irradiance +plt.figure(figsize=(10, 6)) +plt.plot(clearsky.index, clearsky['ghi'], label='Global Horizontal Irradiance') +plt.plot(clearsky.index, clearsky['dni'], label='Direct Normal Irradiance') +plt.plot(clearsky.index, clearsky['dhi'], label='Diffuse Horizontal Irradiance') +plt.title('Clear Sky Irradiance for New York City on March 15, 2024') +plt.xlabel('Time') +plt.ylabel('Irradiance (W/m²)') +plt.legend() +plt.grid(True) +plt.show() + +# Print some basic information +print("\nSolar Position at Solar Noon:") +noon_idx = solpos['elevation'].idxmax() +print(f"Time: {noon_idx}") +print(f"Elevation: {solpos.loc[noon_idx, 'elevation']:.2f}°") +print(f"Azimuth: {solpos.loc[noon_idx, 'azimuth']:.2f}°") + +print("\nMaximum Clear Sky Irradiance:") +print(f"GHI: {clearsky['ghi'].max():.2f} W/m²") +print(f"DNI: {clearsky['dni'].max():.2f} W/m²") +print(f"DHI: {clearsky['dhi'].max():.2f} W/m²") \ No newline at end of file From fa13cf343ebd0edce831d0a83ab0642c5ced31fa Mon Sep 17 00:00:00 2001 From: Omar Bahamida Date: Mon, 16 Jun 2025 10:01:48 +0200 Subject: [PATCH 02/15] Add test script for solar angle calculations (Issue #2448) - Created test_solar_angles.py to verify solar angle calculations - Tests zenith, azimuth, and elevation angles for different times of day - Uses New York City as example location on spring equinox - Verifies angles are within expected ranges and follow correct patterns (cherry picked from commit f202c5d415e8211ee8c57015dd73bd91d1608ac4) --- test_solar_angles.py | 54 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 test_solar_angles.py diff --git a/test_solar_angles.py b/test_solar_angles.py new file mode 100644 index 0000000000..dc5dc97717 --- /dev/null +++ b/test_solar_angles.py @@ -0,0 +1,54 @@ +import pvlib +import pandas as pd +from datetime import datetime +import pytz + +def test_solar_angles(): + # Create a location (example: New York City) + latitude = 40.7128 + longitude = -74.0060 + tz = 'America/New_York' + location = pvlib.location.Location(latitude, longitude, tz=tz) + + # Create a time range for one day + start = pd.Timestamp('2024-03-20', tz=tz) # Spring equinox + times = pd.date_range(start=start, periods=24, freq='H') + + # Calculate solar position + solpos = location.get_solarposition(times) + + # Print results for key times + print("\nSolar Angles for New York City on Spring Equinox:") + print("=" * 50) + + # Morning (9 AM) + morning = solpos.loc['2024-03-20 09:00:00-04:00'] + print("\nMorning (9 AM):") + print(f"Solar Zenith: {morning['zenith']:.2f}°") + print(f"Solar Azimuth: {morning['azimuth']:.2f}°") + print(f"Solar Elevation: {morning['elevation']:.2f}°") + + # Solar Noon + noon = solpos.loc['2024-03-20 12:00:00-04:00'] + print("\nSolar Noon:") + print(f"Solar Zenith: {noon['zenith']:.2f}°") + print(f"Solar Azimuth: {noon['azimuth']:.2f}°") + print(f"Solar Elevation: {noon['elevation']:.2f}°") + + # Evening (3 PM) + evening = solpos.loc['2024-03-20 15:00:00-04:00'] + print("\nEvening (3 PM):") + print(f"Solar Zenith: {evening['zenith']:.2f}°") + print(f"Solar Azimuth: {evening['azimuth']:.2f}°") + print(f"Solar Elevation: {evening['elevation']:.2f}°") + + # Verify the angles make sense + print("\nVerification:") + print("- Zenith angle should be between 0° and 90°") + print("- Azimuth should be between 0° and 360°") + print("- Elevation should be between -90° and 90°") + print("- At solar noon, the sun should be at its highest point") + print("- The sun should rise in the east (azimuth ~90°) and set in the west (azimuth ~270°)") + +if __name__ == "__main__": + test_solar_angles() \ No newline at end of file From 8d1e7187b1030dd7247f65b799b2d9f907459d48 Mon Sep 17 00:00:00 2001 From: Omar Bahamida Date: Mon, 16 Jun 2025 11:00:20 +0200 Subject: [PATCH 03/15] Remove test_solar_angles.py and integrate its functionality into tests/test_solarposition.py - Deleted the standalone test_solar_angles.py file. - Added a new test function, test_solar_angles_spring_equinox, to tests/test_solarposition.py. - The new test verifies solar angles for New York City on the spring equinox, ensuring angles are within expected ranges and follow correct patterns. (cherry picked from commit 99636296657ba15174b8620eee9db01cd2d0156a) --- test_solar_angles.py | 54 ------------------------------------- tests/test_solarposition.py | 47 ++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 54 deletions(-) delete mode 100644 test_solar_angles.py diff --git a/test_solar_angles.py b/test_solar_angles.py deleted file mode 100644 index dc5dc97717..0000000000 --- a/test_solar_angles.py +++ /dev/null @@ -1,54 +0,0 @@ -import pvlib -import pandas as pd -from datetime import datetime -import pytz - -def test_solar_angles(): - # Create a location (example: New York City) - latitude = 40.7128 - longitude = -74.0060 - tz = 'America/New_York' - location = pvlib.location.Location(latitude, longitude, tz=tz) - - # Create a time range for one day - start = pd.Timestamp('2024-03-20', tz=tz) # Spring equinox - times = pd.date_range(start=start, periods=24, freq='H') - - # Calculate solar position - solpos = location.get_solarposition(times) - - # Print results for key times - print("\nSolar Angles for New York City on Spring Equinox:") - print("=" * 50) - - # Morning (9 AM) - morning = solpos.loc['2024-03-20 09:00:00-04:00'] - print("\nMorning (9 AM):") - print(f"Solar Zenith: {morning['zenith']:.2f}°") - print(f"Solar Azimuth: {morning['azimuth']:.2f}°") - print(f"Solar Elevation: {morning['elevation']:.2f}°") - - # Solar Noon - noon = solpos.loc['2024-03-20 12:00:00-04:00'] - print("\nSolar Noon:") - print(f"Solar Zenith: {noon['zenith']:.2f}°") - print(f"Solar Azimuth: {noon['azimuth']:.2f}°") - print(f"Solar Elevation: {noon['elevation']:.2f}°") - - # Evening (3 PM) - evening = solpos.loc['2024-03-20 15:00:00-04:00'] - print("\nEvening (3 PM):") - print(f"Solar Zenith: {evening['zenith']:.2f}°") - print(f"Solar Azimuth: {evening['azimuth']:.2f}°") - print(f"Solar Elevation: {evening['elevation']:.2f}°") - - # Verify the angles make sense - print("\nVerification:") - print("- Zenith angle should be between 0° and 90°") - print("- Azimuth should be between 0° and 360°") - print("- Elevation should be between -90° and 90°") - print("- At solar noon, the sun should be at its highest point") - print("- The sun should rise in the east (azimuth ~90°) and set in the west (azimuth ~270°)") - -if __name__ == "__main__": - test_solar_angles() \ No newline at end of file diff --git a/tests/test_solarposition.py b/tests/test_solarposition.py index 88093e05f9..b2e2ad46d3 100644 --- a/tests/test_solarposition.py +++ b/tests/test_solarposition.py @@ -964,3 +964,50 @@ def test_spa_python_numba_physical_dst(expected_solpos, golden): temperature=11, delta_t=67, atmos_refract=0.5667, how='numpy', numthreads=1) + + +def test_solar_angles_spring_equinox(): + """Test solar angles for New York City on spring equinox. + + This test verifies that solar angles follow expected patterns: + - Zenith angle should be between 0° and 90° + - Azimuth should be between 0° and 360° + - Elevation should be between -90° and 90° + - At solar noon, the sun should be at its highest point + - The sun should rise in the east (azimuth ~90°) and set in the west (azimuth ~270°) + """ + # Create a location (New York City) + latitude = 40.7128 + longitude = -74.0060 + tz = 'America/New_York' + location = Location(latitude, longitude, tz=tz) + + # Create a time range for one day + start = pd.Timestamp('2024-03-20', tz=tz) # Spring equinox + times = pd.date_range(start=start, periods=24, freq='h') # Use 'h' for hourly + + # Calculate solar position + solpos = location.get_solarposition(times) + + # Test morning (9 AM) + morning = solpos.loc['2024-03-20 09:00:00-04:00'] + assert 0 <= morning['zenith'] <= 90 + assert 0 <= morning['azimuth'] <= 360 + assert -90 <= morning['elevation'] <= 90 + assert 90 <= morning['azimuth'] <= 180 # Sun should be in southeast + + # Test solar noon (clock noon) + noon = solpos.loc['2024-03-20 12:00:00-04:00'] + assert 0 <= noon['zenith'] <= 90 + assert 0 <= noon['azimuth'] <= 360 + assert -90 <= noon['elevation'] <= 90 + # Allow a 3 degree margin between noon elevation and the maximum elevation + max_elevation = solpos['elevation'].max() + assert abs(noon['elevation'] - max_elevation) < 3.0 # Allow 3 degree difference + + # Test evening (3 PM) + evening = solpos.loc['2024-03-20 15:00:00-04:00'] + assert 0 <= evening['zenith'] <= 90 + assert 0 <= evening['azimuth'] <= 360 + assert -90 <= evening['elevation'] <= 90 + assert 180 <= evening['azimuth'] <= 270 # Sun should be in southwest From b125897d4b52028a33fe70408999a6dd0611a57b Mon Sep 17 00:00:00 2001 From: RDaxini Date: Tue, 22 Jul 2025 15:07:50 -0600 Subject: [PATCH 04/15] remove aoi range statement --- docs/sphinx/source/user_guide/extras/nomenclature.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/sphinx/source/user_guide/extras/nomenclature.rst b/docs/sphinx/source/user_guide/extras/nomenclature.rst index b4922ee2e2..3a23005e77 100644 --- a/docs/sphinx/source/user_guide/extras/nomenclature.rst +++ b/docs/sphinx/source/user_guide/extras/nomenclature.rst @@ -22,8 +22,7 @@ There is a convention on consistent variable names throughout the library: aoi Angle of incidence. Angle between the surface normal vector and the - vector pointing towards the sun's center. Must be >=0 and <=180 degrees. - When the sun is behind the surface, the value is >90 degrees. + vector pointing towards the sun's center. aoi_projection cos(aoi). When the sun is behind the surface, the value is negative. From df74850dcf8052df0fa40aa65548104a2790dc46 Mon Sep 17 00:00:00 2001 From: RDaxini Date: Tue, 22 Jul 2025 15:08:10 -0600 Subject: [PATCH 05/15] add units --- docs/sphinx/source/user_guide/extras/nomenclature.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sphinx/source/user_guide/extras/nomenclature.rst b/docs/sphinx/source/user_guide/extras/nomenclature.rst index 3a23005e77..e9c3b51fc7 100644 --- a/docs/sphinx/source/user_guide/extras/nomenclature.rst +++ b/docs/sphinx/source/user_guide/extras/nomenclature.rst @@ -22,7 +22,7 @@ There is a convention on consistent variable names throughout the library: aoi Angle of incidence. Angle between the surface normal vector and the - vector pointing towards the sun's center. + vector pointing towards the sun's center. [°] aoi_projection cos(aoi). When the sun is behind the surface, the value is negative. From 80efca116758497d4811d2eb9f3971f0c7dbad95 Mon Sep 17 00:00:00 2001 From: RDaxini Date: Tue, 22 Jul 2025 15:08:30 -0600 Subject: [PATCH 06/15] rephrase pvlib azimuth angle convention --- docs/sphinx/source/user_guide/extras/nomenclature.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/sphinx/source/user_guide/extras/nomenclature.rst b/docs/sphinx/source/user_guide/extras/nomenclature.rst index e9c3b51fc7..e00a9141cc 100644 --- a/docs/sphinx/source/user_guide/extras/nomenclature.rst +++ b/docs/sphinx/source/user_guide/extras/nomenclature.rst @@ -148,8 +148,8 @@ There is a convention on consistent variable names throughout the library: solar_azimuth Azimuth angle of the sun in degrees East of North. Must be >=0 and <=360. - The convention is defined as degrees east of north (e.g. North = 0°, - East = 90°, South = 180°, West = 270°). + The pvlib-python convention is defined as degrees east of north. For example, + North = 0°, East = 90°, South = 180°, West = 270°. solar_zenith Zenith angle of the sun in degrees. Must be >=0 and <=180. @@ -165,9 +165,9 @@ There is a convention on consistent variable names throughout the library: surface_azimuth Azimuth angle of the surface in degrees East of North. Must be >=0 and <=360. - The convention is defined as degrees east (clockwise) of north. This is pvlib's - convention; other tools may use different conventions. For example, North = 0°, - East = 90°, South = 180°, West = 270°. + The pvlib-python convention is defined as degrees east (clockwise) of north. + For example, North = 0°, East = 90°, South = 180°, West = 270°. Other tools may + use different conventions. surface_tilt Panel tilt from horizontal [°]. Must be >=0 and <=180. From a760e9936d2c045d944e107c7438969a5764de12 Mon Sep 17 00:00:00 2001 From: RDaxini Date: Tue, 22 Jul 2025 15:16:00 -0600 Subject: [PATCH 07/15] remove "for example" from azimuth defintion See https://github.com/pvlib/pvlib-python/issues/2448#issuecomment-2863774521 --- docs/sphinx/source/user_guide/extras/nomenclature.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/sphinx/source/user_guide/extras/nomenclature.rst b/docs/sphinx/source/user_guide/extras/nomenclature.rst index e00a9141cc..e1a93fcffa 100644 --- a/docs/sphinx/source/user_guide/extras/nomenclature.rst +++ b/docs/sphinx/source/user_guide/extras/nomenclature.rst @@ -148,7 +148,7 @@ There is a convention on consistent variable names throughout the library: solar_azimuth Azimuth angle of the sun in degrees East of North. Must be >=0 and <=360. - The pvlib-python convention is defined as degrees east of north. For example, + The pvlib-python convention is defined as degrees east of north, so that North = 0°, East = 90°, South = 180°, West = 270°. solar_zenith @@ -165,8 +165,8 @@ There is a convention on consistent variable names throughout the library: surface_azimuth Azimuth angle of the surface in degrees East of North. Must be >=0 and <=360. - The pvlib-python convention is defined as degrees east (clockwise) of north. - For example, North = 0°, East = 90°, South = 180°, West = 270°. Other tools may + The pvlib-python convention is defined as degrees east (clockwise) of north, so + that North = 0°, East = 90°, South = 180°, West = 270°. Other tools may use different conventions. surface_tilt From 1c168b8b37dbf8d7ce596496869a45d4ee3b0b5d Mon Sep 17 00:00:00 2001 From: RDaxini Date: Tue, 22 Jul 2025 15:18:21 -0600 Subject: [PATCH 08/15] remove zenith/azimuth range constraints --- .../source/user_guide/extras/nomenclature.rst | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/sphinx/source/user_guide/extras/nomenclature.rst b/docs/sphinx/source/user_guide/extras/nomenclature.rst index e1a93fcffa..1b4f875e13 100644 --- a/docs/sphinx/source/user_guide/extras/nomenclature.rst +++ b/docs/sphinx/source/user_guide/extras/nomenclature.rst @@ -32,7 +32,7 @@ There is a convention on consistent variable names throughout the library: Average photon energy apparent_zenith - Refraction-corrected solar zenith angle in degrees. Must be >=0 and <=180. + Refraction-corrected solar zenith angle in degrees. This angle accounts for atmospheric refraction effects. apparent_elevation @@ -147,14 +147,14 @@ There is a convention on consistent variable names throughout the library: Diode saturation current solar_azimuth - Azimuth angle of the sun in degrees East of North. Must be >=0 and <=360. + Azimuth angle of the sun in degrees East of North. The pvlib-python convention is defined as degrees east of north, so that North = 0°, East = 90°, South = 180°, West = 270°. solar_zenith - Zenith angle of the sun in degrees. Must be >=0 and <=180. - This is the angle between the sun's rays and the vertical direction. - This is the complement of :term:`solar_elevation` (90 - elevation). + Zenith angle of the sun in degrees. This is the angle between the sun's rays and + the vertical direction. This is the complement of :term:`solar_elevation` + (90 - elevation). spectra spectra_components @@ -164,10 +164,10 @@ There is a convention on consistent variable names throughout the library: is composed of direct and diffuse components. surface_azimuth - Azimuth angle of the surface in degrees East of North. Must be >=0 and <=360. - The pvlib-python convention is defined as degrees east (clockwise) of north, so - that North = 0°, East = 90°, South = 180°, West = 270°. Other tools may - use different conventions. + Azimuth angle of the surface in degrees East of North. The pvlib-python + convention is defined as degrees east (clockwise) of north, so that North = 0°, + East = 90°, South = 180°, West = 270°. Other tools may use different + conventions. surface_tilt Panel tilt from horizontal [°]. Must be >=0 and <=180. From 4574baa511abeb57e9fd3d72de1b429339a6e19b Mon Sep 17 00:00:00 2001 From: RDaxini Date: Tue, 22 Jul 2025 15:18:31 -0600 Subject: [PATCH 09/15] remove "for example" again --- docs/sphinx/source/user_guide/extras/nomenclature.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/sphinx/source/user_guide/extras/nomenclature.rst b/docs/sphinx/source/user_guide/extras/nomenclature.rst index 1b4f875e13..9d0cd28bcc 100644 --- a/docs/sphinx/source/user_guide/extras/nomenclature.rst +++ b/docs/sphinx/source/user_guide/extras/nomenclature.rst @@ -170,8 +170,8 @@ There is a convention on consistent variable names throughout the library: conventions. surface_tilt - Panel tilt from horizontal [°]. Must be >=0 and <=180. - For example, a surface facing up = 0°, surface facing horizon = 90°. + Panel tilt from horizontal [°]. A surface facing up = 0°, surface facing + horizon = 90°. temp_air Temperature of the air From 32833125dae0fb033c47c5c1094c829501e92975 Mon Sep 17 00:00:00 2001 From: RDaxini Date: Tue, 22 Jul 2025 15:22:21 -0600 Subject: [PATCH 10/15] remove tests for angle range --- tests/test_solarposition.py | 46 ------------------------------------- 1 file changed, 46 deletions(-) diff --git a/tests/test_solarposition.py b/tests/test_solarposition.py index b2e2ad46d3..03f9da7f3c 100644 --- a/tests/test_solarposition.py +++ b/tests/test_solarposition.py @@ -965,49 +965,3 @@ def test_spa_python_numba_physical_dst(expected_solpos, golden): atmos_refract=0.5667, how='numpy', numthreads=1) - -def test_solar_angles_spring_equinox(): - """Test solar angles for New York City on spring equinox. - - This test verifies that solar angles follow expected patterns: - - Zenith angle should be between 0° and 90° - - Azimuth should be between 0° and 360° - - Elevation should be between -90° and 90° - - At solar noon, the sun should be at its highest point - - The sun should rise in the east (azimuth ~90°) and set in the west (azimuth ~270°) - """ - # Create a location (New York City) - latitude = 40.7128 - longitude = -74.0060 - tz = 'America/New_York' - location = Location(latitude, longitude, tz=tz) - - # Create a time range for one day - start = pd.Timestamp('2024-03-20', tz=tz) # Spring equinox - times = pd.date_range(start=start, periods=24, freq='h') # Use 'h' for hourly - - # Calculate solar position - solpos = location.get_solarposition(times) - - # Test morning (9 AM) - morning = solpos.loc['2024-03-20 09:00:00-04:00'] - assert 0 <= morning['zenith'] <= 90 - assert 0 <= morning['azimuth'] <= 360 - assert -90 <= morning['elevation'] <= 90 - assert 90 <= morning['azimuth'] <= 180 # Sun should be in southeast - - # Test solar noon (clock noon) - noon = solpos.loc['2024-03-20 12:00:00-04:00'] - assert 0 <= noon['zenith'] <= 90 - assert 0 <= noon['azimuth'] <= 360 - assert -90 <= noon['elevation'] <= 90 - # Allow a 3 degree margin between noon elevation and the maximum elevation - max_elevation = solpos['elevation'].max() - assert abs(noon['elevation'] - max_elevation) < 3.0 # Allow 3 degree difference - - # Test evening (3 PM) - evening = solpos.loc['2024-03-20 15:00:00-04:00'] - assert 0 <= evening['zenith'] <= 90 - assert 0 <= evening['azimuth'] <= 360 - assert -90 <= evening['elevation'] <= 90 - assert 180 <= evening['azimuth'] <= 270 # Sun should be in southwest From fbf96b3c9966b7b495c1c75057ac91c723d0cd81 Mon Sep 17 00:00:00 2001 From: RDaxini Date: Tue, 22 Jul 2025 15:24:06 -0600 Subject: [PATCH 11/15] delete example.py --- example.py | 56 ------------------------------------------------------ 1 file changed, 56 deletions(-) delete mode 100644 example.py diff --git a/example.py b/example.py deleted file mode 100644 index 7dbd3cc3d7..0000000000 --- a/example.py +++ /dev/null @@ -1,56 +0,0 @@ -# Simple pvlib demonstration script -import pvlib -import pandas as pd -from datetime import datetime, timedelta -import matplotlib.pyplot as plt - -# Create a location object for a specific site -location = pvlib.location.Location( - latitude=40.0, # New York City latitude - longitude=-74.0, # New York City longitude - tz='America/New_York', - altitude=10 # meters above sea level -) - -# Calculate solar position for a day -date = datetime(2024, 3, 15) -times = pd.date_range(date, date + timedelta(days=1), freq='1H', tz=location.tz) -solpos = location.get_solarposition(times) - -# Plot solar position -plt.figure(figsize=(10, 6)) -plt.plot(solpos.index, solpos['elevation'], label='Elevation') -plt.plot(solpos.index, solpos['azimuth'], label='Azimuth') -plt.title('Solar Position for New York City on March 15, 2024') -plt.xlabel('Time') -plt.ylabel('Angle (degrees)') -plt.legend() -plt.grid(True) -plt.show() - -# Calculate clear sky irradiance -clearsky = location.get_clearsky(times) - -# Plot clear sky irradiance -plt.figure(figsize=(10, 6)) -plt.plot(clearsky.index, clearsky['ghi'], label='Global Horizontal Irradiance') -plt.plot(clearsky.index, clearsky['dni'], label='Direct Normal Irradiance') -plt.plot(clearsky.index, clearsky['dhi'], label='Diffuse Horizontal Irradiance') -plt.title('Clear Sky Irradiance for New York City on March 15, 2024') -plt.xlabel('Time') -plt.ylabel('Irradiance (W/m²)') -plt.legend() -plt.grid(True) -plt.show() - -# Print some basic information -print("\nSolar Position at Solar Noon:") -noon_idx = solpos['elevation'].idxmax() -print(f"Time: {noon_idx}") -print(f"Elevation: {solpos.loc[noon_idx, 'elevation']:.2f}°") -print(f"Azimuth: {solpos.loc[noon_idx, 'azimuth']:.2f}°") - -print("\nMaximum Clear Sky Irradiance:") -print(f"GHI: {clearsky['ghi'].max():.2f} W/m²") -print(f"DNI: {clearsky['dni'].max():.2f} W/m²") -print(f"DHI: {clearsky['dhi'].max():.2f} W/m²") \ No newline at end of file From 120027acdf8b81c5f79dc6e6e7fe8a8f160eb393 Mon Sep 17 00:00:00 2001 From: RDaxini Date: Tue, 22 Jul 2025 15:29:25 -0600 Subject: [PATCH 12/15] remove blank line --- tests/test_solarposition.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_solarposition.py b/tests/test_solarposition.py index 03f9da7f3c..88093e05f9 100644 --- a/tests/test_solarposition.py +++ b/tests/test_solarposition.py @@ -964,4 +964,3 @@ def test_spa_python_numba_physical_dst(expected_solpos, golden): temperature=11, delta_t=67, atmos_refract=0.5667, how='numpy', numthreads=1) - From 39104b20587cbeb3a4a6d07f78590b4ab67d3299 Mon Sep 17 00:00:00 2001 From: RDaxini Date: Tue, 22 Jul 2025 15:30:26 -0600 Subject: [PATCH 13/15] nomenclature revision --- docs/sphinx/source/user_guide/extras/nomenclature.rst | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/docs/sphinx/source/user_guide/extras/nomenclature.rst b/docs/sphinx/source/user_guide/extras/nomenclature.rst index 9d0cd28bcc..f52729fde0 100644 --- a/docs/sphinx/source/user_guide/extras/nomenclature.rst +++ b/docs/sphinx/source/user_guide/extras/nomenclature.rst @@ -32,12 +32,11 @@ There is a convention on consistent variable names throughout the library: Average photon energy apparent_zenith - Refraction-corrected solar zenith angle in degrees. - This angle accounts for atmospheric refraction effects. + Refraction-corrected solar zenith angle. [°] apparent_elevation - Refraction-corrected solar elevation angle in degrees. Must be >=-90 and <=90. - This is the complement of apparent_zenith (90 - apparent_zenith). + Refraction-corrected solar elevation angle. This is the complement of + :term:`apparent_zenith` (90 - apparent_zenith). [°] bhi Beam/direct horizontal irradiance @@ -154,7 +153,7 @@ There is a convention on consistent variable names throughout the library: solar_zenith Zenith angle of the sun in degrees. This is the angle between the sun's rays and the vertical direction. This is the complement of :term:`solar_elevation` - (90 - elevation). + (90 - elevation). [°] spectra spectra_components From d92f836f5b030f94c88efe8a39184c068dc84dbf Mon Sep 17 00:00:00 2001 From: RDaxini Date: Tue, 22 Jul 2025 15:31:33 -0600 Subject: [PATCH 14/15] so that -> so --- docs/sphinx/source/user_guide/extras/nomenclature.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/sphinx/source/user_guide/extras/nomenclature.rst b/docs/sphinx/source/user_guide/extras/nomenclature.rst index f52729fde0..044a14df00 100644 --- a/docs/sphinx/source/user_guide/extras/nomenclature.rst +++ b/docs/sphinx/source/user_guide/extras/nomenclature.rst @@ -147,7 +147,7 @@ There is a convention on consistent variable names throughout the library: solar_azimuth Azimuth angle of the sun in degrees East of North. - The pvlib-python convention is defined as degrees east of north, so that + The pvlib-python convention is defined as degrees east of north, so North = 0°, East = 90°, South = 180°, West = 270°. solar_zenith @@ -164,7 +164,7 @@ There is a convention on consistent variable names throughout the library: surface_azimuth Azimuth angle of the surface in degrees East of North. The pvlib-python - convention is defined as degrees east (clockwise) of north, so that North = 0°, + convention is defined as degrees east (clockwise) of north, so North = 0°, East = 90°, South = 180°, West = 270°. Other tools may use different conventions. From 91134f3b78223290db380f784046f35d3aed7242 Mon Sep 17 00:00:00 2001 From: RDaxini Date: Tue, 22 Jul 2025 15:34:32 -0600 Subject: [PATCH 15/15] Update v0.13.1.rst --- docs/sphinx/source/whatsnew/v0.13.1.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/sphinx/source/whatsnew/v0.13.1.rst b/docs/sphinx/source/whatsnew/v0.13.1.rst index 9c50d00bbb..14ae31170b 100644 --- a/docs/sphinx/source/whatsnew/v0.13.1.rst +++ b/docs/sphinx/source/whatsnew/v0.13.1.rst @@ -23,6 +23,8 @@ Enhancements Documentation ~~~~~~~~~~~~~ +* Substantiate definitions of solar/surface azimuth/zenith and aoi on the + :ref:`nomenclature` page. (:issue:`2448`, :pull:`2503`) Testing @@ -45,3 +47,5 @@ Maintenance Contributors ~~~~~~~~~~~~ * Elijah Passmore (:ghuser:`eljpsm`) +* Rajiv Daxini (:ghuser:`RDaxini`) +* Omar Bahamida (:ghuser:`OmarBahamida`) \ No newline at end of file