From 96697f11dbba4d0a61fce465d40858649b0096c3 Mon Sep 17 00:00:00 2001 From: echedey-ls <80125792+echedey-ls@users.noreply.github.com> Date: Wed, 8 Jan 2025 21:41:21 +0000 Subject: [PATCH 01/15] Add cross_axis_tilt to nomenclature.rst Definition via https://pvlib-python.readthedocs.io/en/stable/reference/generated/pvlib.tracking.calc_cross_axis_tilt.html --- docs/sphinx/source/user_guide/nomenclature.rst | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/docs/sphinx/source/user_guide/nomenclature.rst b/docs/sphinx/source/user_guide/nomenclature.rst index f361690f73..61285211e8 100644 --- a/docs/sphinx/source/user_guide/nomenclature.rst +++ b/docs/sphinx/source/user_guide/nomenclature.rst @@ -36,6 +36,18 @@ There is a convention on consistent variable names throughout the library: bhi Beam/direct horizontal irradiance + cross_axis_tilt + Cross-axis tilt angle [°]. + Consider two parallel rows of modules at different height; + ``cross_axis_tilt`` is the angle formed by the line formed by the + intersection between the slope containing the tracker axes and a plane + perpendicular to the tracker axes, and the horizontal plane. + Cross-axis tilt is measured by using a right-handed convention. + For example, trackers with axis azimuth of 180 degrees (heading south) + will have a negative cross-axis tilt if the tracker axes plane slopes + down to the east and positive cross-axis tilt if the tracker axes plane + slopes up to the east. + dhi Diffuse horizontal irradiance From 8eebd5faab3c68992fb29ef9a3e7d74227b23889 Mon Sep 17 00:00:00 2001 From: echedey-ls <80125792+echedey-ls@users.noreply.github.com> Date: Wed, 8 Jan 2025 21:43:44 +0000 Subject: [PATCH 02/15] Rename, cross_axis_slope -> cross_axis_tilt --- .../shading/plot_martinez_shade_loss.py | 2 +- .../plot_shaded_fraction1d_ns_hsat_example.py | 12 +++++------ pvlib/shading.py | 20 +++++++++---------- pvlib/tests/test_shading.py | 4 ++-- 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/docs/examples/shading/plot_martinez_shade_loss.py b/docs/examples/shading/plot_martinez_shade_loss.py index 10ce77f741..b30b6e4c12 100644 --- a/docs/examples/shading/plot_martinez_shade_loss.py +++ b/docs/examples/shading/plot_martinez_shade_loss.py @@ -116,7 +116,7 @@ shading_row_rotation=tracker_theta, collector_width=width, pitch=pitch, - cross_axis_slope=cross_axis_tilt, + cross_axis_tilt=cross_axis_tilt, ) # %% diff --git a/docs/examples/shading/plot_shaded_fraction1d_ns_hsat_example.py b/docs/examples/shading/plot_shaded_fraction1d_ns_hsat_example.py index 7eae2ed7aa..9a1e942e5d 100644 --- a/docs/examples/shading/plot_shaded_fraction1d_ns_hsat_example.py +++ b/docs/examples/shading/plot_shaded_fraction1d_ns_hsat_example.py @@ -49,7 +49,7 @@ collector_width = 3.2 # m pitch = 4.15 # m gcr = collector_width / pitch -cross_axis_slope = -5 # degrees +cross_axis_tilt = -5 # degrees surface_to_axis_offset = 0.07 # m # Generate a time range for the simulation @@ -76,7 +76,7 @@ max_angle=(-50, 50), # (min, max) degrees backtrack=False, gcr=gcr, - cross_axis_tilt=cross_axis_slope, + cross_axis_tilt=cross_axis_tilt, )["tracker_theta"] # %% @@ -112,7 +112,7 @@ collector_width=collector_width, pitch=pitch, surface_to_axis_offset=surface_to_axis_offset, - cross_axis_slope=cross_axis_slope, + cross_axis_tilt=cross_axis_tilt, shading_row_rotation=rotation_angle, ), ) @@ -130,7 +130,7 @@ collector_width=collector_width, pitch=pitch, surface_to_axis_offset=surface_to_axis_offset, - cross_axis_slope=cross_axis_slope, + cross_axis_tilt=cross_axis_tilt, shading_row_rotation=rotation_angle, ), # shaded fraction in the evening @@ -143,7 +143,7 @@ collector_width=collector_width, pitch=pitch, surface_to_axis_offset=surface_to_axis_offset, - cross_axis_slope=cross_axis_slope, + cross_axis_tilt=cross_axis_tilt, shading_row_rotation=rotation_angle, ), ) @@ -161,7 +161,7 @@ collector_width=collector_width, pitch=pitch, surface_to_axis_offset=surface_to_axis_offset, - cross_axis_slope=cross_axis_slope, + cross_axis_tilt=cross_axis_tilt, shading_row_rotation=rotation_angle, ), 0, # no shaded fraction in the evening diff --git a/pvlib/shading.py b/pvlib/shading.py index 42aef892f7..0bdd40dbfb 100644 --- a/pvlib/shading.py +++ b/pvlib/shading.py @@ -354,7 +354,7 @@ def shaded_fraction1d( pitch, axis_tilt=0, surface_to_axis_offset=0, - cross_axis_slope=0, + cross_axis_tilt=0, shading_row_rotation=None, ): r""" @@ -397,7 +397,7 @@ def shaded_fraction1d( surface_to_axis_offset : numeric, default 0 Distance between the rotating axis and the collector surface. May be used to account for a torque tube offset. - cross_axis_slope : numeric, default 0 + cross_axis_tilt : numeric, default 0 Angle of the plane containing the rows' axes from horizontal. Right-handed rotation with respect to the rows axes. In degrees :math:`^{\circ}`. @@ -430,7 +430,7 @@ def shaded_fraction1d( +------------------+----------------------------+ | | :math:`\theta_2` | ``shaded_row_rotation`` | Degrees | +------------------+----------------------------+ :math:`^{\circ}` | - | :math:`\beta_c` | ``cross_axis_slope`` | | + | :math:`\beta_c` | ``cross_axis_tilt`` | | +------------------+----------------------------+---------------------+ | :math:`p` | ``pitch`` | Any consistent | +------------------+----------------------------+ length unit across | @@ -452,7 +452,7 @@ def shaded_fraction1d( >>> shaded_fraction1d(solar_zenith=80, solar_azimuth=135, ... axis_azimuth=90, shaded_row_rotation=30, shading_row_rotation=30, ... collector_width=2, pitch=3, axis_tilt=0, - ... surface_to_axis_offset=0.05, cross_axis_slope=0) + ... surface_to_axis_offset=0.05, cross_axis_tilt=0) 0.47755694708090535 **Fixed-tilt north-facing array on sloped terrain** @@ -466,7 +466,7 @@ def shaded_fraction1d( >>> shaded_fraction1d(solar_zenith=80, solar_azimuth=75.5, ... axis_azimuth=270, shaded_row_rotation=50, shading_row_rotation=30, ... collector_width=2.5, pitch=4, axis_tilt=10, - ... surface_to_axis_offset=0.05, cross_axis_slope=0) + ... surface_to_axis_offset=0.05, cross_axis_tilt=0) 0.793244836197256 **N-S single-axis tracker on sloped terrain** @@ -478,7 +478,7 @@ def shaded_fraction1d( >>> shaded_fraction1d(solar_zenith=80, solar_azimuth=90, axis_azimuth=180, ... shaded_row_rotation=-30, collector_width=1.4, pitch=3, axis_tilt=0, - ... surface_to_axis_offset=0.10, cross_axis_slope=7) + ... surface_to_axis_offset=0.10, cross_axis_tilt=7) 0.8242176864434579 Note the previous example only is valid for the shaded fraction of the @@ -493,7 +493,7 @@ def shaded_fraction1d( >>> shaded_fraction1d(solar_zenith=80, solar_azimuth=270, axis_azimuth=180, ... shaded_row_rotation=30, collector_width=1.4, pitch=3, axis_tilt=0, - ... surface_to_axis_offset=0.10, cross_axis_slope=7) + ... surface_to_axis_offset=0.10, cross_axis_tilt=7) 0.018002567182254348 You must switch the input/output depending on the @@ -528,7 +528,7 @@ def shaded_fraction1d( # calculate repeated elements thetas_1_S_diff = shading_row_rotation - projected_solar_zenith thetas_2_S_diff = shaded_row_rotation - projected_solar_zenith - thetaS_rotation_diff = projected_solar_zenith - cross_axis_slope + thetaS_rotation_diff = projected_solar_zenith - cross_axis_tilt cos_theta_2_S_diff_abs = np.abs(cosd(thetas_2_S_diff)) @@ -548,7 +548,7 @@ def shaded_fraction1d( / collector_width * cosd(thetaS_rotation_diff) / cos_theta_2_S_diff_abs - / cosd(cross_axis_slope) + / cosd(cross_axis_tilt) ) ) @@ -660,7 +660,7 @@ def direct_martinez( >>> solar_zenith=80, solar_azimuth=180, >>> axis_azimuth=90, shaded_row_rotation=25, >>> collector_width=0.5, pitch=1, surface_to_axis_offset=0, - >>> cross_axis_slope=5.711, shading_row_rotation=50) + >>> cross_axis_tilt=5.711, shading_row_rotation=50) >>> # calculation of the number of shaded blocks >>> shaded_blocks = np.ceil(total_blocks*shaded_fraction) >>> # apply the Martinez power losses to the calculated shading diff --git a/pvlib/tests/test_shading.py b/pvlib/tests/test_shading.py index f83f2db47b..3887dd8fd9 100644 --- a/pvlib/tests/test_shading.py +++ b/pvlib/tests/test_shading.py @@ -258,7 +258,7 @@ def sf1d_premises_and_expected(): ), ) # fmt: skip - test_data["cross_axis_slope"] = atand( + test_data["cross_axis_tilt"] = atand( (test_data["z_R"] - test_data["z_L"]) / (test_data["x_L"] - test_data["x_R"]) ) @@ -314,7 +314,7 @@ def test_shaded_fraction1d_unprovided_shading_row_rotation(): test_data = pd.DataFrame( columns=[ "shaded_row_rotation", "surface_to_axis_offset", "collector_width", - "solar_zenith", "cross_axis_slope", "pitch", "solar_azimuth", + "solar_zenith", "cross_axis_tilt", "pitch", "solar_azimuth", "axis_azimuth", "expected_sf", ], data=[ From 0aa883697bab97a7e85c5e0849d1f195a93070dc Mon Sep 17 00:00:00 2001 From: echedey-ls <80125792+echedey-ls@users.noreply.github.com> Date: Wed, 8 Jan 2025 21:53:10 +0000 Subject: [PATCH 03/15] Add renamed_kwarg_warning --- pvlib/shading.py | 8 ++++++++ pvlib/tests/test_shading.py | 19 +++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/pvlib/shading.py b/pvlib/shading.py index 0bdd40dbfb..b3e9f8822e 100644 --- a/pvlib/shading.py +++ b/pvlib/shading.py @@ -7,6 +7,8 @@ import pandas as pd from pvlib.tools import sind, cosd +from pvlib._deprecation import renamed_kwarg_warning + def ground_angle(surface_tilt, gcr, slant_height): """ @@ -344,6 +346,12 @@ def projected_solar_zenith_angle(solar_zenith, solar_azimuth, return theta_T +@renamed_kwarg_warning( + since="0.11.3", + old_param_name="cross_axis_slope", + new_param_name="cross_axis_tilt", + removal="0.13.0", +) def shaded_fraction1d( solar_zenith, solar_azimuth, diff --git a/pvlib/tests/test_shading.py b/pvlib/tests/test_shading.py index 3887dd8fd9..8a0529222b 100644 --- a/pvlib/tests/test_shading.py +++ b/pvlib/tests/test_shading.py @@ -9,6 +9,9 @@ from pvlib import shading from pvlib.tools import atand +from pvlib.tests.conftest import fail_on_pvlib_version +from pvlib._deprecation import pvlibDeprecationWarning + @pytest.fixture def test_system(): @@ -329,6 +332,22 @@ def test_shaded_fraction1d_unprovided_shading_row_rotation(): assert_allclose(sf, expected_sf, atol=1e-2) +@fail_on_pvlib_version("0.13.0") +def test_shaded_fraction1d_renamed_cross_axis_slope2cross_axis_tilt(): + # Tests shaded_fraction1d with cross_axis_slope instead of cross_axis_tilt + with pytest.warns(pvlibDeprecationWarning, match="cross_axis_slope"): + shading.shaded_fraction1d( + solar_zenith=60, + solar_azimuth=90, + axis_azimuth=180, + shaded_row_rotation=30, + collector_width=3, + pitch=7, + surface_to_axis_offset=0, + cross_axis_slope=0, + ) + + @pytest.fixture def direct_martinez_Table2(): """ From 01d5c0b11be881bfec44277304eb386b1d6391d3 Mon Sep 17 00:00:00 2001 From: echedey-ls <80125792+echedey-ls@users.noreply.github.com> Date: Wed, 8 Jan 2025 21:57:31 +0000 Subject: [PATCH 04/15] Fix typo in renamed_kwarg_warning docstring --- pvlib/_deprecation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pvlib/_deprecation.py b/pvlib/_deprecation.py index aedb4d5096..06d35959e0 100644 --- a/pvlib/_deprecation.py +++ b/pvlib/_deprecation.py @@ -334,7 +334,7 @@ def renamed_kwarg_warning(since, old_param_name, new_param_name, removal=""): Not compatible with positional-only arguments. .. note:: - Documentation for the function may updated to reflect the new parameter + Affected function docstring may be updated to reflect the new parameter name; it is suggested to add a |.. versionchanged::| directive. Parameters From e72ca3215b0cc98ac20403d5028ebbabe667a2cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Echedey=20Luis=20=C3=81lvarez?= <80125792+echedey-ls@users.noreply.github.com> Date: Sun, 7 Sep 2025 20:20:47 +0100 Subject: [PATCH 05/15] Deprecate in v0.13.1 / Removal in v0.15.0 --- pvlib/shading.py | 4 ++-- pvlib/tests/test_shading.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pvlib/shading.py b/pvlib/shading.py index b3e9f8822e..fa5f534701 100644 --- a/pvlib/shading.py +++ b/pvlib/shading.py @@ -347,10 +347,10 @@ def projected_solar_zenith_angle(solar_zenith, solar_azimuth, @renamed_kwarg_warning( - since="0.11.3", + since="0.13.1", old_param_name="cross_axis_slope", new_param_name="cross_axis_tilt", - removal="0.13.0", + removal="0.15.0", ) def shaded_fraction1d( solar_zenith, diff --git a/pvlib/tests/test_shading.py b/pvlib/tests/test_shading.py index 8a0529222b..986cab3054 100644 --- a/pvlib/tests/test_shading.py +++ b/pvlib/tests/test_shading.py @@ -332,7 +332,7 @@ def test_shaded_fraction1d_unprovided_shading_row_rotation(): assert_allclose(sf, expected_sf, atol=1e-2) -@fail_on_pvlib_version("0.13.0") +@fail_on_pvlib_version("0.15.0") def test_shaded_fraction1d_renamed_cross_axis_slope2cross_axis_tilt(): # Tests shaded_fraction1d with cross_axis_slope instead of cross_axis_tilt with pytest.warns(pvlibDeprecationWarning, match="cross_axis_slope"): From 08bcde47a31521e1d9487f7f6d7f619a961e27ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Echedey=20Luis=20=C3=81lvarez?= <80125792+echedey-ls@users.noreply.github.com> Date: Sun, 7 Sep 2025 20:47:46 +0100 Subject: [PATCH 06/15] move descriptions, link to nomenclature term --- docs/sphinx/source/user_guide/nomenclature.rst | 2 ++ pvlib/shading.py | 4 ++-- pvlib/tracking.py | 14 ++++---------- 3 files changed, 8 insertions(+), 12 deletions(-) diff --git a/docs/sphinx/source/user_guide/nomenclature.rst b/docs/sphinx/source/user_guide/nomenclature.rst index 61285211e8..827fb99edb 100644 --- a/docs/sphinx/source/user_guide/nomenclature.rst +++ b/docs/sphinx/source/user_guide/nomenclature.rst @@ -47,6 +47,8 @@ There is a convention on consistent variable names throughout the library: will have a negative cross-axis tilt if the tracker axes plane slopes down to the east and positive cross-axis tilt if the tracker axes plane slopes up to the east. + Use :func:`~pvlib.tracking.calc_cross_axis_tilt` to calculate + ``cross_axis_tilt`` dhi Diffuse horizontal irradiance diff --git a/pvlib/shading.py b/pvlib/shading.py index fa5f534701..7be2b62ca5 100644 --- a/pvlib/shading.py +++ b/pvlib/shading.py @@ -406,9 +406,9 @@ def shaded_fraction1d( Distance between the rotating axis and the collector surface. May be used to account for a torque tube offset. cross_axis_tilt : numeric, default 0 - Angle of the plane containing the rows' axes from + Angle of the plane containing the rows' axes relative to horizontal. Right-handed rotation with respect to the rows axes. - In degrees :math:`^{\circ}`. + See :term:`cross_axis_tilt`. In degrees :math:`^{\circ}`. shading_row_rotation : numeric, optional Right-handed rotation of the row casting the shadow, with respect to the row axis. In degrees :math:`^{\circ}`. diff --git a/pvlib/tracking.py b/pvlib/tracking.py index afdaab2adf..3e04e7cc5f 100644 --- a/pvlib/tracking.py +++ b/pvlib/tracking.py @@ -9,7 +9,7 @@ def singleaxis(apparent_zenith, apparent_azimuth, axis_tilt=0, axis_azimuth=0, max_angle=90, backtrack=True, gcr=2.0/7.0, cross_axis_tilt=0): - """ + r""" Determine the rotation angle of a single-axis tracker when given particular solar zenith and azimuth angles. @@ -75,15 +75,9 @@ def singleaxis(apparent_zenith, apparent_azimuth, 2/7 is default. ``gcr`` must be <=1. cross_axis_tilt : float, default 0.0 - The angle, relative to horizontal, of the line formed by the - intersection between the slope containing the tracker axes and a plane - perpendicular to the tracker axes. The cross-axis tilt should be - specified using a right-handed convention. For example, trackers with - axis azimuth of 180 degrees (heading south) will have a negative - cross-axis tilt if the tracker axes plane slopes down to the east and - positive cross-axis tilt if the tracker axes plane slopes down to the - west. Use :func:`~pvlib.tracking.calc_cross_axis_tilt` to calculate - ``cross_axis_tilt``. [degrees] + Angle of the plane containing the rows' axes relative to + horizontal. Right-handed rotation with respect to the rows axes. + See :term:`cross_axis_tilt`. In degrees :math:`^{\circ}`. Returns ------- From 3ca225e7352bdeaf22f26b8b9e7ba659a0b32de9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Echedey=20Luis=20=C3=81lvarez?= <80125792+echedey-ls@users.noreply.github.com> Date: Sun, 7 Sep 2025 20:57:35 +0100 Subject: [PATCH 07/15] merge fix --- tests/test_shading.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_shading.py b/tests/test_shading.py index 986cab3054..a19917774b 100644 --- a/tests/test_shading.py +++ b/tests/test_shading.py @@ -9,7 +9,7 @@ from pvlib import shading from pvlib.tools import atand -from pvlib.tests.conftest import fail_on_pvlib_version +from .conftest import fail_on_pvlib_version from pvlib._deprecation import pvlibDeprecationWarning From c74127c4138233cb5e7e6cddf7253387aa5f2101 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Echedey=20Luis=20=C3=81lvarez?= <80125792+echedey-ls@users.noreply.github.com> Date: Sun, 7 Sep 2025 20:57:42 +0100 Subject: [PATCH 08/15] whatsnew --- docs/sphinx/source/whatsnew/v0.13.1.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/sphinx/source/whatsnew/v0.13.1.rst b/docs/sphinx/source/whatsnew/v0.13.1.rst index 26a591e534..58bf5664a4 100644 --- a/docs/sphinx/source/whatsnew/v0.13.1.rst +++ b/docs/sphinx/source/whatsnew/v0.13.1.rst @@ -13,6 +13,7 @@ Deprecations * Deprecate :py:func:`~pvlib.modelchain.get_orientation`. (:pull:`2495`) * Rename parameter name ``aparent_azimuth`` to ``solar_azimuth`` in :py:func:`~pvlib.tracking.singleaxis`. (:issue:`2479`, :pull:`2480`) +* Rename parameter ``cross_axis_slope`` to ``cross_axis_tilt`` in :py:func:`pvlib.shading.shaded_fraction1d`. (:issue:`2334`, :pull:`2543`) Bug fixes ~~~~~~~~~ From 3006aa15acefb7726099effed42e76466458a553 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Echedey=20Luis=20=C3=81lvarez?= <80125792+echedey-ls@users.noreply.github.com> Date: Sat, 20 Sep 2025 22:07:50 +0200 Subject: [PATCH 09/15] Dax review Co-Authored-By: RDaxini <143435106+RDaxini@users.noreply.github.com> --- 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 e5bf41091c..6c6ad92514 100644 --- a/docs/sphinx/source/user_guide/extras/nomenclature.rst +++ b/docs/sphinx/source/user_guide/extras/nomenclature.rst @@ -45,13 +45,13 @@ There is a convention on consistent variable names throughout the library: Beam/direct horizontal irradiance cross_axis_tilt - Cross-axis tilt angle [°]. + Cross-axis tilt angle. [°] Consider two parallel rows of modules at different height; - ``cross_axis_tilt`` is the angle formed by the line formed by the + ``cross_axis_tilt`` is the angle formed the line formed by the intersection between the slope containing the tracker axes and a plane perpendicular to the tracker axes, and the horizontal plane. Cross-axis tilt is measured by using a right-handed convention. - For example, trackers with axis azimuth of 180 degrees (heading south) + For example, trackers with axis azimuth of 180° (heading south) will have a negative cross-axis tilt if the tracker axes plane slopes down to the east and positive cross-axis tilt if the tracker axes plane slopes up to the east. From 2cebae7d71cef29a87096e158c8f8103cc52f800 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Echedey=20Luis=20=C3=81lvarez?= <80125792+echedey-ls@users.noreply.github.com> Date: Wed, 24 Sep 2025 11:41:40 +0200 Subject: [PATCH 10/15] Change: deprecate *axis_tilt in favour of *axis_slope --- benchmarks/benchmarks/infinite_sheds.py | 2 +- benchmarks/benchmarks/tracking.py | 2 +- docs/examples/bifacial/plot_bifi_model_mc.py | 4 +- .../plot_transposition_gain.py | 2 +- .../shading/plot_martinez_shade_loss.py | 14 +- .../plot_shaded_fraction1d_ns_hsat_example.py | 26 ++-- .../plot_discontinuous_tracking.py | 6 +- .../plot_single_axis_tracking.py | 4 +- ..._single_axis_tracking_on_sloped_terrain.py | 28 ++-- docs/sphinx/source/reference/tracking.rst | 4 +- .../source/user_guide/extras/nomenclature.rst | 8 +- docs/sphinx/source/whatsnew/v0.13.1.rst | 12 +- docs/sphinx/source/whatsnew/v0.8.0.rst | 6 +- pvlib/modelchain.py | 2 +- pvlib/pvsystem.py | 93 +++++++++++-- pvlib/shading.py | 83 +++++++----- pvlib/tracking.py | 121 ++++++++++++----- tests/test_pvsystem.py | 42 +++++- tests/test_shading.py | 50 ++++--- tests/test_tracking.py | 127 ++++++++++++------ 20 files changed, 441 insertions(+), 195 deletions(-) diff --git a/benchmarks/benchmarks/infinite_sheds.py b/benchmarks/benchmarks/infinite_sheds.py index 755c2ec3ef..d5a3de1033 100644 --- a/benchmarks/benchmarks/infinite_sheds.py +++ b/benchmarks/benchmarks/infinite_sheds.py @@ -35,7 +35,7 @@ def setup(self, vectorize): self.tracking = tracking.singleaxis( self.solar_position['apparent_zenith'], self.solar_position['azimuth'], - axis_tilt=0, + axis_slope=0, axis_azimuth=0, max_angle=60, backtrack=True, diff --git a/benchmarks/benchmarks/tracking.py b/benchmarks/benchmarks/tracking.py index 29f5ac4c6e..0308a65d38 100644 --- a/benchmarks/benchmarks/tracking.py +++ b/benchmarks/benchmarks/tracking.py @@ -22,7 +22,7 @@ def time_singleaxis(self): with np.errstate(invalid='ignore'): tracking.singleaxis(self.solar_position.apparent_zenith, self.solar_position.azimuth, - axis_tilt=0, + axis_slope=0, axis_azimuth=0, max_angle=60, backtrack=True, diff --git a/docs/examples/bifacial/plot_bifi_model_mc.py b/docs/examples/bifacial/plot_bifi_model_mc.py index 679ff22921..61e801277d 100644 --- a/docs/examples/bifacial/plot_bifi_model_mc.py +++ b/docs/examples/bifacial/plot_bifi_model_mc.py @@ -43,7 +43,7 @@ times = pd.date_range('2021-06-21', '2021-6-22', freq='1T', tz=tz) # create site system characteristics -axis_tilt = 0 +axis_slope = 0 axis_azimuth = 180 gcr = 0.35 max_angle = 60 @@ -65,7 +65,7 @@ cs = site_location.get_clearsky(times) # load solar position and tracker orientation for use in pvsystem object -sat_mount = pvsystem.SingleAxisTrackerMount(axis_tilt=axis_tilt, +sat_mount = pvsystem.SingleAxisTrackerMount(axis_slope=axis_slope, axis_azimuth=axis_azimuth, max_angle=max_angle, backtrack=True, diff --git a/docs/examples/irradiance-transposition/plot_transposition_gain.py b/docs/examples/irradiance-transposition/plot_transposition_gain.py index e0b7031f0b..02fe7e634a 100644 --- a/docs/examples/irradiance-transposition/plot_transposition_gain.py +++ b/docs/examples/irradiance-transposition/plot_transposition_gain.py @@ -85,7 +85,7 @@ def calculate_poa(tmy, solar_position, surface_tilt, surface_azimuth): # single-axis tracking: orientation = tracking.singleaxis(solar_position['apparent_zenith'], solar_position['azimuth'], - axis_tilt=0, # flat array + axis_slope=0, # flat array axis_azimuth=180, # south-facing azimuth max_angle=60, # a common maximum rotation backtrack=True, # backtrack for a c-Si array diff --git a/docs/examples/shading/plot_martinez_shade_loss.py b/docs/examples/shading/plot_martinez_shade_loss.py index 6423017567..d8f0d7e0f3 100644 --- a/docs/examples/shading/plot_martinez_shade_loss.py +++ b/docs/examples/shading/plot_martinez_shade_loss.py @@ -54,8 +54,8 @@ gcr = width / pitch # ground coverage ratio N_modules_per_row = 6 axis_azimuth = 180 # N-S axis -axis_tilt = 0 # flat because the axis is perpendicular to the slope -cross_axis_tilt = -7 # 7 degrees downward to the east +axis_slope = 0 # flat because the axis is perpendicular to the slope +cross_axis_slope = -7 # 7 degrees downward to the east latitude, longitude = 40.2712, -3.7277 locus = pvlib.location.Location( @@ -91,12 +91,12 @@ tracking_result = pvlib.tracking.singleaxis( apparent_zenith=solar_apparent_zenith, solar_azimuth=solar_azimuth, - axis_tilt=axis_tilt, + axis_slope=axis_slope, axis_azimuth=axis_azimuth, - max_angle=(-90 + cross_axis_tilt, 90 + cross_axis_tilt), # (min, max) + max_angle=(-90 + cross_axis_slope, 90 + cross_axis_slope), # (min, max) backtrack=False, gcr=gcr, - cross_axis_tilt=cross_axis_tilt, + cross_axis_slope=cross_axis_slope, ) tracker_theta, aoi, surface_tilt, surface_azimuth = ( @@ -111,12 +111,12 @@ solar_apparent_zenith, solar_azimuth, axis_azimuth, - axis_tilt=axis_tilt, + axis_slope=axis_slope, shaded_row_rotation=tracker_theta, shading_row_rotation=tracker_theta, collector_width=width, pitch=pitch, - cross_axis_tilt=cross_axis_tilt, + cross_axis_slope=cross_axis_slope, ) # %% diff --git a/docs/examples/shading/plot_shaded_fraction1d_ns_hsat_example.py b/docs/examples/shading/plot_shaded_fraction1d_ns_hsat_example.py index 9a1e942e5d..f216732f5d 100644 --- a/docs/examples/shading/plot_shaded_fraction1d_ns_hsat_example.py +++ b/docs/examples/shading/plot_shaded_fraction1d_ns_hsat_example.py @@ -44,12 +44,12 @@ latitude, longitude = 28.51, -13.89 altitude = pvlib.location.lookup_altitude(latitude, longitude) -axis_tilt = 3 # degrees, positive is upwards in the axis_azimuth direction +axis_slope = 3 # degrees, positive is upwards in the axis_azimuth direction axis_azimuth = 180 # degrees, N-S tracking axis collector_width = 3.2 # m pitch = 4.15 # m gcr = collector_width / pitch -cross_axis_tilt = -5 # degrees +cross_axis_slope = -5 # degrees surface_to_axis_offset = 0.07 # m # Generate a time range for the simulation @@ -71,12 +71,12 @@ rotation_angle = pvlib.tracking.singleaxis( solar_zenith, solar_azimuth, - axis_tilt, + axis_slope, axis_azimuth, max_angle=(-50, 50), # (min, max) degrees backtrack=False, gcr=gcr, - cross_axis_tilt=cross_axis_tilt, + cross_axis_slope=cross_axis_slope, )["tracker_theta"] # %% @@ -95,7 +95,7 @@ # failure or with a different system configuration. psza = pvlib.shading.projected_solar_zenith_angle( - solar_zenith, solar_azimuth, axis_tilt, axis_azimuth + solar_zenith, solar_azimuth, axis_slope, axis_azimuth ) # Calculate the shaded fraction for the eastmost row @@ -108,11 +108,11 @@ solar_azimuth, axis_azimuth, shaded_row_rotation=rotation_angle, - axis_tilt=axis_tilt, + axis_slope=axis_slope, collector_width=collector_width, pitch=pitch, surface_to_axis_offset=surface_to_axis_offset, - cross_axis_tilt=cross_axis_tilt, + cross_axis_slope=cross_axis_slope, shading_row_rotation=rotation_angle, ), ) @@ -126,11 +126,11 @@ solar_azimuth, axis_azimuth, shaded_row_rotation=rotation_angle, - axis_tilt=axis_tilt, + axis_slope=axis_slope, collector_width=collector_width, pitch=pitch, surface_to_axis_offset=surface_to_axis_offset, - cross_axis_tilt=cross_axis_tilt, + cross_axis_slope=cross_axis_slope, shading_row_rotation=rotation_angle, ), # shaded fraction in the evening @@ -139,11 +139,11 @@ solar_azimuth, axis_azimuth, shaded_row_rotation=rotation_angle, - axis_tilt=axis_tilt, + axis_slope=axis_slope, collector_width=collector_width, pitch=pitch, surface_to_axis_offset=surface_to_axis_offset, - cross_axis_tilt=cross_axis_tilt, + cross_axis_slope=cross_axis_slope, shading_row_rotation=rotation_angle, ), ) @@ -157,11 +157,11 @@ solar_azimuth, axis_azimuth, shaded_row_rotation=rotation_angle, - axis_tilt=axis_tilt, + axis_slope=axis_slope, collector_width=collector_width, pitch=pitch, surface_to_axis_offset=surface_to_axis_offset, - cross_axis_tilt=cross_axis_tilt, + cross_axis_slope=cross_axis_slope, shading_row_rotation=rotation_angle, ), 0, # no shaded fraction in the evening diff --git a/docs/examples/solar-tracking/plot_discontinuous_tracking.py b/docs/examples/solar-tracking/plot_discontinuous_tracking.py index f385675d57..fd8a03c500 100644 --- a/docs/examples/solar-tracking/plot_discontinuous_tracking.py +++ b/docs/examples/solar-tracking/plot_discontinuous_tracking.py @@ -26,7 +26,7 @@ class DiscontinuousTrackerMount(pvsystem.SingleAxisTrackerMount): # inherit from SingleAxisTrackerMount so that we get the - # constructor and tracking attributes (axis_tilt etc) automatically + # constructor and tracking attributes (axis_slope etc) automatically def get_orientation(self, solar_zenith, solar_azimuth): # Different trackers update at different rates; in this example we'll @@ -37,9 +37,9 @@ def get_orientation(self, solar_zenith, solar_azimuth): tracking_data_15min = tracking.singleaxis( zenith_subset, azimuth_subset, - self.axis_tilt, self.axis_azimuth, + self.axis_slope, self.axis_azimuth, self.max_angle, self.backtrack, - self.gcr, self.cross_axis_tilt + self.gcr, self.cross_axis_slope ) # propagate the 15-minute positions to 1-minute stair-stepped values: tracking_data_1min = tracking_data_15min.reindex(solar_zenith.index, diff --git a/docs/examples/solar-tracking/plot_single_axis_tracking.py b/docs/examples/solar-tracking/plot_single_axis_tracking.py index 4f7d62cb80..72172630c1 100644 --- a/docs/examples/solar-tracking/plot_single_axis_tracking.py +++ b/docs/examples/solar-tracking/plot_single_axis_tracking.py @@ -34,7 +34,7 @@ truetracking_angles = tracking.singleaxis( apparent_zenith=solpos['apparent_zenith'], solar_azimuth=solpos['azimuth'], - axis_tilt=0, + axis_slope=0, axis_azimuth=180, max_angle=90, backtrack=False, # for true-tracking @@ -62,7 +62,7 @@ backtracking_angles = tracking.singleaxis( apparent_zenith=solpos['apparent_zenith'], solar_azimuth=solpos['azimuth'], - axis_tilt=0, + axis_slope=0, axis_azimuth=180, max_angle=90, backtrack=True, diff --git a/docs/examples/solar-tracking/plot_single_axis_tracking_on_sloped_terrain.py b/docs/examples/solar-tracking/plot_single_axis_tracking_on_sloped_terrain.py index b3a204ce69..7131513a71 100644 --- a/docs/examples/solar-tracking/plot_single_axis_tracking_on_sloped_terrain.py +++ b/docs/examples/solar-tracking/plot_single_axis_tracking_on_sloped_terrain.py @@ -14,8 +14,8 @@ # calculating the backtracking angle requires knowledge of the relative spacing # of adjacent tracker rows. This example shows how the backtracking angle # changes based on a vertical offset between rows caused by sloped terrain. -# It uses :py:func:`pvlib.tracking.calc_axis_tilt` and -# :py:func:`pvlib.tracking.calc_cross_axis_tilt` to calculate the necessary +# It uses :py:func:`pvlib.tracking.calc_axis_slope` and +# :py:func:`pvlib.tracking.calc_cross_axis_slope` to calculate the necessary # array geometry parameters and :py:func:`pvlib.tracking.singleaxis` to # calculate the backtracking angles. # @@ -97,20 +97,20 @@ # compare the backtracking angle at various terrain slopes fig, ax = plt.subplots() -for cross_axis_tilt in [0, 5, 10]: +for cross_axis_slope in [0, 5, 10]: tracker_data = tracking.singleaxis( apparent_zenith=solpos['apparent_zenith'], solar_azimuth=solpos['azimuth'], - axis_tilt=0, # flat because the axis is perpendicular to the slope + axis_slope=0, # flat because the axis is perpendicular to the slope axis_azimuth=180, # N-S axis, azimuth facing south max_angle=90, backtrack=True, gcr=gcr, - cross_axis_tilt=cross_axis_tilt) + cross_axis_slope=cross_axis_slope) # tracker rotation is undefined at night backtracking_position = tracker_data['tracker_theta'].fillna(0) - label = 'cross-axis tilt: {}°'.format(cross_axis_tilt) + label = 'cross-axis tilt: {}°'.format(cross_axis_slope) backtracking_position.plot(label=label, ax=ax) plt.legend() @@ -141,14 +141,14 @@ axis_azimuth = 180 # tracker axis is still N-S # calculate the tracker axis tilt, assuming that the axis follows the terrain: -axis_tilt = tracking.calc_axis_tilt(slope_azimuth, slope_tilt, axis_azimuth) +axis_slope = tracking.calc_axis_slope(slope_azimuth, slope_tilt, axis_azimuth) # calculate the cross-axis tilt: -cross_axis_tilt = tracking.calc_cross_axis_tilt(slope_azimuth, slope_tilt, - axis_azimuth, axis_tilt) +cross_axis_slope = tracking.calc_cross_axis_slope(slope_azimuth, slope_tilt, + axis_azimuth, axis_slope) -print('Axis tilt:', '{:0.01f}°'.format(axis_tilt)) -print('Cross-axis tilt:', '{:0.01f}°'.format(cross_axis_tilt)) +print('Axis tilt:', '{:0.01f}°'.format(axis_slope)) +print('Cross-axis tilt:', '{:0.01f}°'.format(cross_axis_slope)) # %% # And now we can pass use these values to generate the tracker curve as @@ -157,18 +157,18 @@ tracker_data = tracking.singleaxis( apparent_zenith=solpos['apparent_zenith'], solar_azimuth=solpos['azimuth'], - axis_tilt=axis_tilt, # no longer flat because the terrain imparts a tilt + axis_slope=axis_slope, # no longer flat because the terrain imparts a tilt axis_azimuth=axis_azimuth, max_angle=90, backtrack=True, gcr=gcr, - cross_axis_tilt=cross_axis_tilt) + cross_axis_slope=cross_axis_slope) backtracking_position = tracker_data['tracker_theta'].fillna(0) backtracking_position.plot() title_template = 'Axis tilt: {:0.01f}° Cross-axis tilt: {:0.01f}°' -plt.title(title_template.format(axis_tilt, cross_axis_tilt)) +plt.title(title_template.format(axis_slope, cross_axis_slope)) plt.show() # %% diff --git a/docs/sphinx/source/reference/tracking.rst b/docs/sphinx/source/reference/tracking.rst index 4e85ffa3de..1cf67bab8f 100644 --- a/docs/sphinx/source/reference/tracking.rst +++ b/docs/sphinx/source/reference/tracking.rst @@ -7,6 +7,6 @@ Tracking :toctree: generated/ tracking.singleaxis - tracking.calc_axis_tilt - tracking.calc_cross_axis_tilt + tracking.calc_axis_slope + tracking.calc_cross_axis_slope tracking.calc_surface_orientation diff --git a/docs/sphinx/source/user_guide/extras/nomenclature.rst b/docs/sphinx/source/user_guide/extras/nomenclature.rst index 6c6ad92514..1467b49bdd 100644 --- a/docs/sphinx/source/user_guide/extras/nomenclature.rst +++ b/docs/sphinx/source/user_guide/extras/nomenclature.rst @@ -44,10 +44,10 @@ There is a convention on consistent variable names throughout the library: bhi Beam/direct horizontal irradiance - cross_axis_tilt + cross_axis_slope Cross-axis tilt angle. [°] Consider two parallel rows of modules at different height; - ``cross_axis_tilt`` is the angle formed the line formed by the + ``cross_axis_slope`` is the angle formed the line formed by the intersection between the slope containing the tracker axes and a plane perpendicular to the tracker axes, and the horizontal plane. Cross-axis tilt is measured by using a right-handed convention. @@ -55,8 +55,8 @@ There is a convention on consistent variable names throughout the library: will have a negative cross-axis tilt if the tracker axes plane slopes down to the east and positive cross-axis tilt if the tracker axes plane slopes up to the east. - Use :func:`~pvlib.tracking.calc_cross_axis_tilt` to calculate - ``cross_axis_tilt`` + Use :func:`~pvlib.tracking.calc_cross_axis_slope` to calculate + ``cross_axis_slope`` dhi Diffuse horizontal irradiance diff --git a/docs/sphinx/source/whatsnew/v0.13.1.rst b/docs/sphinx/source/whatsnew/v0.13.1.rst index 58bf5664a4..ff5089aa15 100644 --- a/docs/sphinx/source/whatsnew/v0.13.1.rst +++ b/docs/sphinx/source/whatsnew/v0.13.1.rst @@ -13,7 +13,17 @@ Deprecations * Deprecate :py:func:`~pvlib.modelchain.get_orientation`. (:pull:`2495`) * Rename parameter name ``aparent_azimuth`` to ``solar_azimuth`` in :py:func:`~pvlib.tracking.singleaxis`. (:issue:`2479`, :pull:`2480`) -* Rename parameter ``cross_axis_slope`` to ``cross_axis_tilt`` in :py:func:`pvlib.shading.shaded_fraction1d`. (:issue:`2334`, :pull:`2543`) +* Rename ``axis_tilt`` and ``cross_axis_tilt`` terms to ``axis_slope`` and ``cross_axis_slope`` all through the project. Affects: + + - :py:func:`pvlib.shading.shaded_fraction1d` parameters ``cross_axis_tilt`` and ``axis_tilt`` + - :py:func:`pvlib.shading.projected_solar_zenith_angle` parameter ``axis_tilt`` + - :py:func:`pvlib.tracking.singleaxis` parameters ``cross_axis_tilt`` and ``axis_tilt`` + - :py:func:`pvlib.tracking.calc_surface_orientation` parameter ``axis_tilt`` + - :py:func:`!pvlib.tracking.calc_axis_tilt` function signature is renamed to :py:func:`pvlib.tracking.calc_axis_slope` + - :py:func:`!pvlib.tracking.calc_cross_axis_tilt` function signature is renamed to :py:func:`pvlib.tracking.calc_cross_axis_slope` and parameter ``axis_tilt`` + - :py:class:`pvlib.pvsystem.SingleAxisTrackerMount` dataclass fields ``axis_tilt`` and ``cross_axis_tilt`` + + (:issue:`2334`, :pull:`2543`) Bug fixes ~~~~~~~~~ diff --git a/docs/sphinx/source/whatsnew/v0.8.0.rst b/docs/sphinx/source/whatsnew/v0.8.0.rst index 86fb81574f..04e2d16765 100644 --- a/docs/sphinx/source/whatsnew/v0.8.0.rst +++ b/docs/sphinx/source/whatsnew/v0.8.0.rst @@ -110,14 +110,14 @@ Enhancements * Added ``racking_model``, ``module_type``, and ``temperature_model_parameters`` to :py:class:`~pvlib.pvsystem.PVSystem` and :py:class:`~pvlib.tracking.SingleAxisTracker` repr methods. (:issue:`1027`) -* Added :py:func:`~pvlib.tracking.calc_axis_tilt` to calculate the - tracker axes tilt and :py:func:`~pvlib.tracking.calc_cross_axis_tilt` to +* Added :py:func:`!~pvlib.tracking.calc_axis_tilt` to calculate the + tracker axes tilt and :py:func:`!~pvlib.tracking.calc_cross_axis_tilt` to calculate the cross-axis tilt, which is the angle, relative to horizontal, of the line formed by the intersection between the slope containing the tracker axes and a plane perpendicular to the tracker axes. (:pull:`823`) * Added ``cross_axis_tilt`` argument to :py:func:`~pvlib.tracking.singleaxis` and :py:func:`~pvlib.tracking.SingleAxisTracker` which defaults to zero. Use - :py:func:`~pvlib.tracking.calc_cross_axis_tilt` to calculate the cross-axis + :py:func:`!~pvlib.tracking.calc_cross_axis_tilt` to calculate the cross-axis tilt angle if necessary. (:pull:`823`) * Added ability for :py:func:`pvlib.soiling.hsu` to accept arbitrary time intervals. (:pull:`980`) * Added :py:func:`pvlib.temperature.fuentes` for cell temperature modeling. (:pull:`1037`) diff --git a/pvlib/modelchain.py b/pvlib/modelchain.py index 09e4434e84..992399ccab 100644 --- a/pvlib/modelchain.py +++ b/pvlib/modelchain.py @@ -1257,7 +1257,7 @@ def _prep_inputs_tracking(self): self.results.solar_position['azimuth']) self.results.tracking['surface_tilt'] = ( self.results.tracking['surface_tilt'] - .fillna(self.system.axis_tilt)) + .fillna(self.system.axis_slope)) self.results.tracking['surface_azimuth'] = ( self.results.tracking['surface_azimuth'] .fillna(self.system.axis_azimuth)) diff --git a/pvlib/pvsystem.py b/pvlib/pvsystem.py index 23ca1a934a..185c2d598e 100644 --- a/pvlib/pvsystem.py +++ b/pvlib/pvsystem.py @@ -8,6 +8,7 @@ import io import itertools from pathlib import Path +from warnings import warn import inspect from urllib.request import urlopen import numpy as np @@ -16,7 +17,7 @@ from dataclasses import dataclass from abc import ABC, abstractmethod from typing import Optional, Union -from pvlib._deprecation import renamed_kwarg_warning +from pvlib._deprecation import renamed_kwarg_warning, pvlibDeprecationWarning import pvlib # used to avoid albedo name collision in the Array class from pvlib import (atmosphere, iam, inverter, irradiance, singlediode as _singlediode, spectrum, temperature) @@ -1431,10 +1432,13 @@ class SingleAxisTrackerMount(AbstractMount): Parameters ---------- - axis_tilt : float, default 0 + axis_slope : float, default 0 The tilt of the axis of rotation (i.e, the y-axis defined by axis_azimuth) with respect to horizontal. [degrees] + .. versionchanged:: 0.13.1 + Renamed from ``axis_tilt`` to ``axis_slope``. + axis_azimuth : float, default 180 A value denoting the compass direction along which the axis of rotation lies, measured east of north. [degrees] @@ -1442,7 +1446,7 @@ class SingleAxisTrackerMount(AbstractMount): max_angle : float or tuple, default 90 A value denoting the maximum rotation angle, in decimal degrees, of the one-axis tracker from its horizontal position (horizontal - if axis_tilt = 0). If a float is provided, it represents the maximum + if axis_slope = 0). If a float is provided, it represents the maximum rotation angle, and the minimum rotation angle is assumed to be the opposite of the maximum angle. If a tuple of (min_angle, max_angle) is provided, it represents both the minimum and maximum rotation angles. @@ -1468,7 +1472,7 @@ class SingleAxisTrackerMount(AbstractMount): between the tracking axes has a gcr of 2/6=0.333. If gcr is not provided, a gcr of 2/7 is default. gcr must be <=1. [unitless] - cross_axis_tilt : float, default 0.0 + cross_axis_slope : float, default 0.0 The angle, relative to horizontal, of the line formed by the intersection between the slope containing the tracker axes and a plane perpendicular to the tracker axes. Cross-axis tilt should be specified @@ -1476,8 +1480,11 @@ class SingleAxisTrackerMount(AbstractMount): azimuth of 180 degrees (heading south) will have a negative cross-axis tilt if the tracker axes plane slopes down to the east and positive cross-axis tilt if the tracker axes plane slopes up to the east. Use - :func:`~pvlib.tracking.calc_cross_axis_tilt` to calculate - `cross_axis_tilt`. [degrees] + :func:`~pvlib.tracking.calc_cross_axis_slope` to calculate + `cross_axis_slope`. [degrees] + + .. versionchanged:: 0.13.1 + Renamed from ``cross_axis_tilt`` to ``cross_axis_slope``. racking_model : str, optional Valid strings are ``'open_rack'``, ``'close_mount'``, @@ -1493,23 +1500,89 @@ class SingleAxisTrackerMount(AbstractMount): The height above ground of the center of the module [m]. Used for the Fuentes cell temperature model. """ - axis_tilt: float = 0.0 + axis_slope: float = 0.0 axis_azimuth: float = 0.0 max_angle: Union[float, tuple] = 90.0 backtrack: bool = True gcr: float = 2.0/7.0 - cross_axis_tilt: float = 0.0 + cross_axis_slope: float = 0.0 racking_model: Optional[str] = None module_height: Optional[float] = None + # __init__, and properties getters and setters are explicit to deprecate + # field axis_tilt, renamed to axis_slope; and + # field cross_axis_tilt, renamed to cross_axis_slope; GH#2334 & GH#2543 + @renamed_kwarg_warning( + since="0.13.1", + old_param_name="axis_tilt", + new_param_name="axis_slope", + ) + @renamed_kwarg_warning( + since="0.13.1", + old_param_name="cross_axis_tilt", + new_param_name="cross_axis_slope", + ) + def __init__(self, axis_slope=0.0, axis_azimuth=0.0, max_angle=90.0, + backtrack=True, gcr=2.0/7.0, cross_axis_slope=0.0, + racking_model: Optional[str] = None, + module_height: Optional[float] = None): + self.axis_slope = axis_slope + self.axis_azimuth = axis_azimuth + self.max_angle = max_angle + self.backtrack = backtrack + self.gcr = gcr + self.cross_axis_slope = cross_axis_slope + self.racking_model = racking_model + self.module_height = module_height + + @property + def axis_tilt(self): + warn( + "'axis_tilt' is deprecated since v0.13.1. " + "Use 'axis_slope' instead.", + pvlibDeprecationWarning, + stacklevel=2, + ) + return self.axis_slope + + @axis_tilt.setter + def axis_tilt(self, new_value): + warn( + "'axis_tilt' is deprecated since v0.13.1. " + "Use 'axis_slope' instead.", + pvlibDeprecationWarning, + stacklevel=2, + ) + self.axis_slope = new_value + + @property + def cross_axis_tilt(self): + warn( + "'cross_axis_tilt' is deprecated since v0.13.1. " + "Use 'cross_axis_slope' instead.", + pvlibDeprecationWarning, + stacklevel=2, + ) + return self.cross_axis_slope + + @cross_axis_tilt.setter + def cross_axis_tilt(self, new_value): + warn( + "'cross_axis_tilt' is deprecated since v0.13.1. " + "Use 'cross_axis_slope' instead.", + pvlibDeprecationWarning, + stacklevel=2, + ) + self.cross_axis_slope = new_value + def get_orientation(self, solar_zenith, solar_azimuth): # note -- docstring is automatically inherited from AbstractMount from pvlib import tracking # avoid circular import issue tracking_data = tracking.singleaxis( solar_zenith, solar_azimuth, - self.axis_tilt, self.axis_azimuth, + self.axis_slope, self.axis_azimuth, self.max_angle, self.backtrack, - self.gcr, self.cross_axis_tilt + self.gcr, self.cross_axis_slope ) return tracking_data diff --git a/pvlib/shading.py b/pvlib/shading.py index 7be2b62ca5..e6a096e9a1 100644 --- a/pvlib/shading.py +++ b/pvlib/shading.py @@ -236,15 +236,20 @@ def sky_diffuse_passias(masking_angle): return 1 - cosd(masking_angle/2)**2 +@renamed_kwarg_warning( + since="0.13.1", + old_param_name="axis_tilt", + new_param_name="axis_slope", +) def projected_solar_zenith_angle(solar_zenith, solar_azimuth, - axis_tilt, axis_azimuth): + axis_slope, axis_azimuth): r""" Calculate projected solar zenith angle in degrees. This solar zenith angle is projected onto the plane whose normal vector is - defined by ``axis_tilt`` and ``axis_azimuth``. The normal vector is in the + defined by ``axis_slope`` and ``axis_azimuth``. The normal vector is in the direction of ``axis_azimuth`` (clockwise from north) and tilted from - horizontal by ``axis_tilt``. See Figure 5 in [1]_: + horizontal by ``axis_slope``. See Figure 5 in [1]_: .. figure:: ../../_images/Anderson_Mikofski_2020_Fig5.jpg :alt: Wire diagram of coordinates systems to obtain the projected angle. @@ -259,8 +264,12 @@ def projected_solar_zenith_angle(solar_zenith, solar_azimuth, Sun's apparent zenith in degrees. solar_azimuth : numeric Sun's azimuth in degrees. - axis_tilt : numeric + axis_slope : numeric Axis tilt angle in degrees. From horizontal plane to array plane. + + .. versionchanged:: 0.13.1 + Renamed from ``axis_tilt`` to ``axis_slope`` + axis_azimuth : numeric Axis azimuth angle in degrees. North = 0°; East = 90°; South = 180°; West = 270° @@ -294,14 +303,14 @@ def projected_solar_zenith_angle(solar_zenith, solar_azimuth, single-axis tracker: >>> rotation = projected_solar_zenith_angle(solar_zenith, solar_azimuth, - >>> axis_tilt=0, axis_azimuth=180) + >>> axis_slope=0, axis_azimuth=180) Calculate the projected zenith angle in a south-facing fixed tilt array (note: the ``axis_azimuth`` of a fixed-tilt row points along the length of the row): >>> psza = projected_solar_zenith_angle(solar_zenith, solar_azimuth, - >>> axis_tilt=0, axis_azimuth=90) + >>> axis_slope=0, axis_azimuth=90) References ---------- @@ -328,7 +337,7 @@ def projected_solar_zenith_angle(solar_zenith, solar_azimuth, sind_solar_zenith = sind(solar_zenith) cosd_axis_azimuth = cosd(axis_azimuth) sind_axis_azimuth = sind(axis_azimuth) - sind_axis_tilt = sind(axis_tilt) + sind_axis_slope = sind(axis_slope) # Sun's x, y, z coords sx = sind_solar_zenith * sind(solar_azimuth) @@ -337,9 +346,9 @@ def projected_solar_zenith_angle(solar_zenith, solar_azimuth, # Eq. (4); sx', sz' values from sun coordinates projected onto surface sx_prime = sx * cosd_axis_azimuth - sy * sind_axis_azimuth sz_prime = ( - sx * sind_axis_azimuth * sind_axis_tilt - + sy * sind_axis_tilt * cosd_axis_azimuth - + sz * cosd(axis_tilt) + sx * sind_axis_azimuth * sind_axis_slope + + sy * sind_axis_slope * cosd_axis_azimuth + + sz * cosd(axis_slope) ) # Eq. (5); angle between sun's beam and surface theta_T = np.degrees(np.arctan2(sx_prime, sz_prime)) @@ -348,9 +357,13 @@ def projected_solar_zenith_angle(solar_zenith, solar_azimuth, @renamed_kwarg_warning( since="0.13.1", - old_param_name="cross_axis_slope", - new_param_name="cross_axis_tilt", - removal="0.15.0", + old_param_name="axis_tilt", + new_param_name="axis_slope", +) +@renamed_kwarg_warning( + since="0.13.1", + old_param_name="cross_axis_tilt", + new_param_name="cross_axis_slope", ) def shaded_fraction1d( solar_zenith, @@ -360,9 +373,9 @@ def shaded_fraction1d( *, collector_width, pitch, - axis_tilt=0, + axis_slope=0, surface_to_axis_offset=0, - cross_axis_tilt=0, + cross_axis_slope=0, shading_row_rotation=None, ): r""" @@ -400,15 +413,23 @@ def shaded_fraction1d( is the ratio of the shadow over this value. pitch : numeric Axis-to-axis horizontal spacing of the row. - axis_tilt : numeric, default 0 + axis_slope : numeric, default 0 Tilt of the rows axis from horizontal. In degrees :math:`^{\circ}`. + + .. versionchanged:: 0.13.1 + Renamed from ``axis_tilt`` to ``axis_slope`` + surface_to_axis_offset : numeric, default 0 Distance between the rotating axis and the collector surface. May be used to account for a torque tube offset. - cross_axis_tilt : numeric, default 0 + cross_axis_slope : numeric, default 0 Angle of the plane containing the rows' axes relative to horizontal. Right-handed rotation with respect to the rows axes. - See :term:`cross_axis_tilt`. In degrees :math:`^{\circ}`. + See :term:`cross_axis_slope`. In degrees :math:`^{\circ}`. + + .. versionchanged:: 0.13.1 + Renamed from ``cross_axis_tilt`` to ``cross_axis_slope`` + shading_row_rotation : numeric, optional Right-handed rotation of the row casting the shadow, with respect to the row axis. In degrees :math:`^{\circ}`. @@ -438,7 +459,7 @@ def shaded_fraction1d( +------------------+----------------------------+ | | :math:`\theta_2` | ``shaded_row_rotation`` | Degrees | +------------------+----------------------------+ :math:`^{\circ}` | - | :math:`\beta_c` | ``cross_axis_tilt`` | | + | :math:`\beta_c` | ``cross_axis_slope`` | | +------------------+----------------------------+---------------------+ | :math:`p` | ``pitch`` | Any consistent | +------------------+----------------------------+ length unit across | @@ -459,8 +480,8 @@ def shaded_fraction1d( >>> shaded_fraction1d(solar_zenith=80, solar_azimuth=135, ... axis_azimuth=90, shaded_row_rotation=30, shading_row_rotation=30, - ... collector_width=2, pitch=3, axis_tilt=0, - ... surface_to_axis_offset=0.05, cross_axis_tilt=0) + ... collector_width=2, pitch=3, axis_slope=0, + ... surface_to_axis_offset=0.05, cross_axis_slope=0) 0.47755694708090535 **Fixed-tilt north-facing array on sloped terrain** @@ -473,8 +494,8 @@ def shaded_fraction1d( >>> shaded_fraction1d(solar_zenith=80, solar_azimuth=75.5, ... axis_azimuth=270, shaded_row_rotation=50, shading_row_rotation=30, - ... collector_width=2.5, pitch=4, axis_tilt=10, - ... surface_to_axis_offset=0.05, cross_axis_tilt=0) + ... collector_width=2.5, pitch=4, axis_slope=10, + ... surface_to_axis_offset=0.05, cross_axis_slope=0) 0.793244836197256 **N-S single-axis tracker on sloped terrain** @@ -485,8 +506,8 @@ def shaded_fraction1d( tracker is higher than the west-most tracker). >>> shaded_fraction1d(solar_zenith=80, solar_azimuth=90, axis_azimuth=180, - ... shaded_row_rotation=-30, collector_width=1.4, pitch=3, axis_tilt=0, - ... surface_to_axis_offset=0.10, cross_axis_tilt=7) + ... shaded_row_rotation=-30, collector_width=1.4, pitch=3, + ... axis_slope=0, surface_to_axis_offset=0.10, cross_axis_slope=7) 0.8242176864434579 Note the previous example only is valid for the shaded fraction of the @@ -500,8 +521,8 @@ def shaded_fraction1d( in the afternoon. >>> shaded_fraction1d(solar_zenith=80, solar_azimuth=270, axis_azimuth=180, - ... shaded_row_rotation=30, collector_width=1.4, pitch=3, axis_tilt=0, - ... surface_to_axis_offset=0.10, cross_axis_tilt=7) + ... shaded_row_rotation=30, collector_width=1.4, pitch=3, axis_slope=0, + ... surface_to_axis_offset=0.10, cross_axis_slope=7) 0.018002567182254348 You must switch the input/output depending on the @@ -529,14 +550,14 @@ def shaded_fraction1d( projected_solar_zenith = projected_solar_zenith_angle( solar_zenith, solar_azimuth, - axis_tilt, + axis_slope, axis_azimuth, ) # calculate repeated elements thetas_1_S_diff = shading_row_rotation - projected_solar_zenith thetas_2_S_diff = shaded_row_rotation - projected_solar_zenith - thetaS_rotation_diff = projected_solar_zenith - cross_axis_tilt + thetaS_rotation_diff = projected_solar_zenith - cross_axis_slope cos_theta_2_S_diff_abs = np.abs(cosd(thetas_2_S_diff)) @@ -556,7 +577,7 @@ def shaded_fraction1d( / collector_width * cosd(thetaS_rotation_diff) / cos_theta_2_S_diff_abs - / cosd(cross_axis_tilt) + / cosd(cross_axis_slope) ) ) @@ -668,7 +689,7 @@ def direct_martinez( >>> solar_zenith=80, solar_azimuth=180, >>> axis_azimuth=90, shaded_row_rotation=25, >>> collector_width=0.5, pitch=1, surface_to_axis_offset=0, - >>> cross_axis_tilt=5.711, shading_row_rotation=50) + >>> cross_axis_slope=5.711, shading_row_rotation=50) >>> # calculation of the number of shaded blocks >>> shaded_blocks = np.ceil(total_blocks*shaded_fraction) >>> # apply the Martinez power losses to the calculated shading diff --git a/pvlib/tracking.py b/pvlib/tracking.py index a30cc27822..5f261fc3eb 100644 --- a/pvlib/tracking.py +++ b/pvlib/tracking.py @@ -4,16 +4,26 @@ from pvlib.tools import cosd, sind, tand, acosd, asind from pvlib import irradiance from pvlib import shading -from pvlib._deprecation import renamed_kwarg_warning +from pvlib._deprecation import renamed_kwarg_warning, deprecated +@renamed_kwarg_warning( + since="0.13.1", + old_param_name="axis_tilt", + new_param_name="axis_slope", +) +@renamed_kwarg_warning( + since="0.13.1", + old_param_name="cross_axis_tilt", + new_param_name="cross_axis_slope", +) @renamed_kwarg_warning( since='0.13.1', old_param_name='apparent_azimuth', new_param_name='solar_azimuth') def singleaxis(apparent_zenith, solar_azimuth, - axis_tilt=0, axis_azimuth=0, max_angle=90, - backtrack=True, gcr=2.0/7.0, cross_axis_tilt=0): + axis_slope=0, axis_azimuth=0, max_angle=90, + backtrack=True, gcr=2.0/7.0, cross_axis_slope=0): r""" Determine the rotation angle of a single-axis tracker when given particular solar zenith and azimuth angles. @@ -28,7 +38,7 @@ def singleaxis(apparent_zenith, solar_azimuth, oriented skyward. Rotation angle ``tracker_theta`` is a right-handed rotation around the y-axis in the x, y, z coordinate system and indicates tracker position relative to horizontal. For example, if tracker - ``axis_azimuth`` is 180 (oriented south) and ``axis_tilt`` is zero, then a + ``axis_azimuth`` is 180 (oriented south) and ``axis_slope`` is zero, then a ``tracker_theta`` of zero is horizontal, a ``tracker_theta`` of 30 degrees is a rotation of 30 degrees towards the west, and a ``tracker_theta`` of -90 degrees is a rotation to the vertical plane facing east. @@ -41,10 +51,13 @@ def singleaxis(apparent_zenith, solar_azimuth, solar_azimuth : float, 1d array, or Series Solar apparent azimuth angles in decimal degrees. - axis_tilt : float, default 0 + axis_slope : float, default 0 The tilt of the axis of rotation (i.e, the y-axis defined by ``axis_azimuth``) with respect to horizontal. - ``axis_tilt`` must be >= 0 and <= 90. [degrees] + ``axis_slope`` must be >= 0 and <= 90. [degrees] + + .. versionchanged:: 0.13.1 + Renamed from ``axis_tilt`` to ``axis_slope``. axis_azimuth : float, default 0 A value denoting the compass direction along which the axis of @@ -53,7 +66,7 @@ def singleaxis(apparent_zenith, solar_azimuth, max_angle : float or tuple, default 90 A value denoting the maximum rotation angle, in decimal degrees, of the one-axis tracker from its horizontal position (horizontal - if axis_tilt = 0). If a float is provided, it represents the maximum + if axis_slope = 0). If a float is provided, it represents the maximum rotation angle, and the minimum rotation angle is assumed to be the opposite of the maximum angle. If a tuple of (min_angle, max_angle) is provided, it represents both the minimum and maximum rotation angles. @@ -79,10 +92,13 @@ def singleaxis(apparent_zenith, solar_azimuth, has a ``gcr`` of 2/6=0.333. If ``gcr`` is not provided, a ``gcr`` of 2/7 is default. ``gcr`` must be <=1. - cross_axis_tilt : float, default 0.0 + cross_axis_slope : float, default 0.0 Angle of the plane containing the rows' axes relative to horizontal. Right-handed rotation with respect to the rows axes. - See :term:`cross_axis_tilt`. In degrees :math:`^{\circ}`. + See :term:`cross_axis_slope`. In degrees :math:`^{\circ}`. + + .. versionchanged:: 0.13.1 + Renamed from ``cross_axis_tilt`` to ``cross_axis_slope``. Returns ------- @@ -100,8 +116,8 @@ def singleaxis(apparent_zenith, solar_azimuth, See also -------- - pvlib.tracking.calc_axis_tilt - pvlib.tracking.calc_cross_axis_tilt + pvlib.tracking.calc_axis_slope + pvlib.tracking.calc_cross_axis_slope pvlib.tracking.calc_surface_orientation References @@ -138,7 +154,7 @@ def singleaxis(apparent_zenith, solar_azimuth, # rotation to the west is positive. This is a right-handed rotation # around the tracker y-axis. omega_ideal = shading.projected_solar_zenith_angle( - axis_tilt=axis_tilt, + axis_slope=axis_slope, axis_azimuth=axis_azimuth, solar_zenith=apparent_zenith, solar_azimuth=solar_azimuth, @@ -152,10 +168,10 @@ def singleaxis(apparent_zenith, solar_azimuth, if backtrack: # distance between rows in terms of rack lengths relative to cross-axis # tilt - axes_distance = 1/(gcr * cosd(cross_axis_tilt)) + axes_distance = 1/(gcr * cosd(cross_axis_slope)) # NOTE: account for rare angles below array, see GH 824 - temp = np.abs(axes_distance * cosd(omega_ideal - cross_axis_tilt)) + temp = np.abs(axes_distance * cosd(omega_ideal - cross_axis_slope)) # backtrack angle using [1], Eq. 14 with np.errstate(invalid='ignore'): @@ -186,7 +202,7 @@ def singleaxis(apparent_zenith, solar_azimuth, tracker_theta = np.clip(tracker_theta, min_angle, max_angle) # Calculate auxiliary angles - surface = calc_surface_orientation(tracker_theta, axis_tilt, axis_azimuth) + surface = calc_surface_orientation(tracker_theta, axis_slope, axis_azimuth) surface_tilt = surface['surface_tilt'] surface_azimuth = surface['surface_azimuth'] aoi = irradiance.aoi(surface_tilt, surface_azimuth, @@ -204,7 +220,12 @@ def singleaxis(apparent_zenith, solar_azimuth, return out -def calc_surface_orientation(tracker_theta, axis_tilt=0, axis_azimuth=0): +@renamed_kwarg_warning( + since="0.13.1", + old_param_name="axis_tilt", + new_param_name="axis_slope", +) +def calc_surface_orientation(tracker_theta, axis_slope=0, axis_azimuth=0): """ Calculate the surface tilt and azimuth angles for a given tracker rotation. @@ -212,13 +233,17 @@ def calc_surface_orientation(tracker_theta, axis_tilt=0, axis_azimuth=0): ---------- tracker_theta : numeric Tracker rotation angle as a right-handed rotation around - the axis defined by ``axis_tilt`` and ``axis_azimuth``. For example, - with ``axis_tilt=0`` and ``axis_azimuth=180``, ``tracker_theta > 0`` + the axis defined by ``axis_slope`` and ``axis_azimuth``. For example, + with ``axis_slope=0`` and ``axis_azimuth=180``, ``tracker_theta > 0`` results in ``surface_azimuth`` to the West while ``tracker_theta < 0`` results in ``surface_azimuth`` to the East. [degree] - axis_tilt : float, default 0 + axis_slope : float, default 0 The tilt of the axis of rotation with respect to horizontal. - ``axis_tilt`` must be >= 0 and <= 90. [degree] + ``axis_slope`` must be >= 0 and <= 90. [degree] + + .. versionchanged:: 0.13.1 + Renamed from ``axis_tilt`` to ``axis_slope``. + axis_azimuth : float, default 0 A value denoting the compass direction along which the axis of rotation lies. Measured east of north. [degree] @@ -237,7 +262,7 @@ def calc_surface_orientation(tracker_theta, axis_tilt=0, axis_azimuth=0): July 2013. :doi:`10.2172/1089596` """ with np.errstate(invalid='ignore', divide='ignore'): - surface_tilt = acosd(cosd(tracker_theta) * cosd(axis_tilt)) + surface_tilt = acosd(cosd(tracker_theta) * cosd(axis_slope)) # clip(..., -1, +1) to prevent arcsin(1 + epsilon) issues: azimuth_delta = asind(np.clip(sind(tracker_theta) / sind(surface_tilt), @@ -259,13 +284,16 @@ def calc_surface_orientation(tracker_theta, axis_tilt=0, axis_azimuth=0): return out -def calc_axis_tilt(slope_azimuth, slope_tilt, axis_azimuth): +def calc_axis_slope(slope_azimuth, slope_tilt, axis_azimuth): """ Calculate tracker axis tilt in the global reference frame when on a sloped plane. Axis tilt is the inclination of the tracker rotation axis with respect to horizontal, ranging from 0 degrees (horizontal axis) to 90 degrees (vertical axis). + .. versionchanged:: 0.13.1 + Renamed function ``calc_axis_tilt`` to ``calc_axis_slope``. + Parameters ---------- slope_azimuth : float @@ -277,13 +305,13 @@ def calc_axis_tilt(slope_azimuth, slope_tilt, axis_azimuth): Returns ------- - axis_tilt : float + axis_slope : float tilt of tracker [degrees] See also -------- pvlib.tracking.singleaxis - pvlib.tracking.calc_cross_axis_tilt + pvlib.tracking.calc_cross_axis_slope Notes ----- @@ -297,8 +325,8 @@ def calc_axis_tilt(slope_azimuth, slope_tilt, axis_azimuth): """ delta_gamma = axis_azimuth - slope_azimuth # equations 18-19 - tan_axis_tilt = cosd(delta_gamma) * tand(slope_tilt) - return np.degrees(np.arctan(tan_axis_tilt)) + tan_axis_slope = cosd(delta_gamma) * tand(slope_tilt) + return np.degrees(np.arctan(tan_axis_slope)) def _calc_tracker_norm(ba, bg, dg): @@ -354,8 +382,13 @@ def _calc_beta_c(v, dg, ba): return beta_c -def calc_cross_axis_tilt( - slope_azimuth, slope_tilt, axis_azimuth, axis_tilt): +@renamed_kwarg_warning( + since="0.13.1", + old_param_name="axis_tilt", + new_param_name="axis_slope", +) +def calc_cross_axis_slope( + slope_azimuth, slope_tilt, axis_azimuth, axis_slope): """ Calculate the angle, relative to horizontal, of the line formed by the intersection between the slope containing the tracker axes and a plane @@ -368,6 +401,9 @@ def calc_cross_axis_tilt( if the tracker axes plane slopes down to the east and positive cross-axis tilt if the tracker axes plane slopes down to the west. + .. versionchanged:: 0.13.1 + Renamed function ``calc_cross_axis_tilt`` to ``calc_cross_axis_slope``. + Parameters ---------- slope_azimuth : float @@ -378,13 +414,16 @@ def calc_cross_axis_tilt( [degrees] axis_azimuth : float direction of tracker axes projected on the horizontal [degrees] - axis_tilt : float - tilt of trackers relative to horizontal. ``axis_tilt`` must be >= 0 + axis_slope : float + tilt of trackers relative to horizontal. ``axis_slope`` must be >= 0 and <= 90. [degree] + .. versionchanged:: 0.13.1 + Renamed from ``axis_tilt`` to ``axis_slope``. + Returns ------- - cross_axis_tilt : float + cross_axis_slope : float angle, relative to horizontal, of the line formed by the intersection between the slope containing the tracker axes and a plane perpendicular to the tracker axes [degrees] @@ -392,7 +431,7 @@ def calc_cross_axis_tilt( See also -------- pvlib.tracking.singleaxis - pvlib.tracking.calc_axis_tilt + pvlib.tracking.calc_axis_slope Notes ----- @@ -407,7 +446,21 @@ def calc_cross_axis_tilt( # delta-gamma, difference between axis and slope azimuths delta_gamma = axis_azimuth - slope_azimuth # equation 22 - v = _calc_tracker_norm(axis_tilt, slope_tilt, delta_gamma) + v = _calc_tracker_norm(axis_slope, slope_tilt, delta_gamma) # equation 26 - beta_c = _calc_beta_c(v, delta_gamma, axis_tilt) + beta_c = _calc_beta_c(v, delta_gamma, axis_slope) return np.degrees(beta_c) + + +# allow deprecated names of calc_cross_axis_slope and calc_axis_slope +calc_axis_tilt = deprecated( + since="0.13.1", + name="calc_axis_tilt", # else it uses calc_axis_slope by introspection + alternative="pvlib.tracking.calc_axis_slope" +)(calc_axis_slope) + +calc_cross_axis_tilt = deprecated( + since="0.13.1", + name="calc_cross_axis_tilt", # else it uses calc_cross_axis_slope + alternative="pvlib.tracking.calc_cross_axis_slope" +)(calc_cross_axis_slope) diff --git a/tests/test_pvsystem.py b/tests/test_pvsystem.py index b58f9fd9e4..364bef1dee 100644 --- a/tests/test_pvsystem.py +++ b/tests/test_pvsystem.py @@ -6,6 +6,7 @@ import pandas as pd import pytest +from pvlib._deprecation import pvlibDeprecationWarning from .conftest import assert_series_equal, assert_frame_equal from numpy.testing import assert_allclose import unittest.mock as mock @@ -2368,9 +2369,9 @@ def fixed_mount(): @pytest.fixture def single_axis_tracker_mount(): - return pvsystem.SingleAxisTrackerMount(axis_tilt=10, axis_azimuth=170, + return pvsystem.SingleAxisTrackerMount(axis_slope=10, axis_azimuth=170, max_angle=45, backtrack=False, - gcr=0.4, cross_axis_tilt=-5) + gcr=0.4, cross_axis_slope=-5) def test_FixedMount_constructor(fixed_mount): @@ -2384,8 +2385,8 @@ def test_FixedMount_get_orientation(fixed_mount): def test_SingleAxisTrackerMount_constructor(single_axis_tracker_mount): - expected = dict(axis_tilt=10, axis_azimuth=170, max_angle=45, - backtrack=False, gcr=0.4, cross_axis_tilt=-5) + expected = dict(axis_slope=10, axis_azimuth=170, max_angle=45, + backtrack=False, gcr=0.4, cross_axis_slope=-5) for attr_name, expected_value in expected.items(): assert getattr(single_axis_tracker_mount, attr_name) == expected_value @@ -2519,3 +2520,36 @@ def test_Array_temperature_missing_parameters(model, keys): array.temperature_model_parameters = params with pytest.raises(KeyError, match=match): array.get_cell_temperature(irrads, temps, winds, model) + + +def test_SingleAxisTrackerMount_renamed_attributes_tilt_to_slope( + single_axis_tracker_mount, +): # renamed attributes tested here are + # axis_tilt -> axis_slope and cross_axis_tilt -> cross_axis_slope + + # test constructor + with pytest.warns(pvlibDeprecationWarning, match="axis_slope"): + pvsystem.SingleAxisTrackerMount(axis_tilt=10, axis_azimuth=170, + max_angle=45, backtrack=False, + gcr=0.4, cross_axis_slope=-5) + with pytest.warns(pvlibDeprecationWarning, match="cross_axis_slope"): + pvsystem.SingleAxisTrackerMount(axis_slope=10, axis_azimuth=170, + max_angle=45, backtrack=False, + gcr=0.4, cross_axis_tilt=-5) + with (pytest.warns(pvlibDeprecationWarning, match="axis_slope"), + pytest.warns(pvlibDeprecationWarning, match="cross_axis_slope")): + pvsystem.SingleAxisTrackerMount(axis_tilt=10, axis_azimuth=170, + max_angle=45, backtrack=False, + gcr=0.4, cross_axis_tilt=-5) + + # read values + with pytest.warns(pvlibDeprecationWarning, match="axis_slope"): + single_axis_tracker_mount.axis_tilt + with pytest.warns(pvlibDeprecationWarning, match="cross_axis_slope"): + single_axis_tracker_mount.cross_axis_tilt + + # set values + with pytest.warns(pvlibDeprecationWarning, match="axis_slope"): + single_axis_tracker_mount.axis_tilt = 25.132 + with pytest.warns(pvlibDeprecationWarning, match="cross_axis_slope"): + single_axis_tracker_mount.cross_axis_tilt = 15.657 diff --git a/tests/test_shading.py b/tests/test_shading.py index a19917774b..de9e10179a 100644 --- a/tests/test_shading.py +++ b/tests/test_shading.py @@ -9,7 +9,6 @@ from pvlib import shading from pvlib.tools import atand -from .conftest import fail_on_pvlib_version from pvlib._deprecation import pvlibDeprecationWarning @@ -118,7 +117,7 @@ def true_tracking_angle_and_inputs_NREL(): # data from NREL 'Slope-Aware Backtracking for Single-Axis Trackers' # doi.org/10.2172/1660126 ; Accessed on 2023-11-06. tzinfo = timezone(timedelta(hours=-5)) - axis_tilt_angle = 9.666 # deg + axis_slope_angle = 9.666 # deg axis_azimuth_angle = 195.0 # deg timedata = pd.DataFrame( columns=("Apparent Elevation", "Solar Azimuth", "True-Tracking"), @@ -139,7 +138,7 @@ def true_tracking_angle_and_inputs_NREL(): "2019-01-01T08", "2019-01-01T17", freq="1h", tz=tzinfo ) timedata["Apparent Zenith"] = 90.0 - timedata["Apparent Elevation"] - return (axis_tilt_angle, axis_azimuth_angle, timedata) + return (axis_slope_angle, axis_azimuth_angle, timedata) @pytest.fixture @@ -161,7 +160,7 @@ def projected_solar_zenith_angle_edge_cases(): [ 45, 45, 90, 180, -135], [ 45, 315, 90, 180, 135], ], - columns=["solar_zenith", "solar_azimuth", "axis_tilt", "axis_azimuth", + columns=["solar_zenith", "solar_azimuth", "axis_slope", "axis_azimuth", "psza"], ) return premises_and_result_matrix @@ -172,12 +171,12 @@ def test_projected_solar_zenith_angle_numeric( projected_solar_zenith_angle_edge_cases ): psza_func = shading.projected_solar_zenith_angle - axis_tilt, axis_azimuth, timedata = true_tracking_angle_and_inputs_NREL + axis_slope, axis_azimuth, timedata = true_tracking_angle_and_inputs_NREL # test against data provided by NREL psz = psza_func( timedata["Apparent Zenith"], timedata["Solar Azimuth"], - axis_tilt, + axis_slope, axis_azimuth, ) assert_allclose(psz, timedata["True-Tracking"], atol=1e-3) @@ -185,19 +184,19 @@ def test_projected_solar_zenith_angle_numeric( psza = psza_func( timedata["Apparent Zenith"], timedata["Solar Azimuth"], - -axis_tilt, + -axis_slope, axis_azimuth - 180, ) assert_allclose(psza, -timedata["True-Tracking"], atol=1e-3) # test edge cases - solar_zenith, solar_azimuth, axis_tilt, axis_azimuth, psza_expected = ( + solar_zenith, solar_azimuth, axis_slope, axis_azimuth, psza_expected = ( v for _, v in projected_solar_zenith_angle_edge_cases.items() ) psza = psza_func( solar_zenith, solar_azimuth, - axis_tilt, + axis_slope, axis_azimuth, ) assert_allclose(psza, psza_expected, atol=1e-9) @@ -215,17 +214,17 @@ def test_projected_solar_zenith_angle_datatypes( cast_type, cast_func, true_tracking_angle_and_inputs_NREL ): psz_func = shading.projected_solar_zenith_angle - axis_tilt, axis_azimuth, timedata = true_tracking_angle_and_inputs_NREL + axis_slope, axis_azimuth, timedata = true_tracking_angle_and_inputs_NREL sun_apparent_zenith = timedata["Apparent Zenith"].iloc[0] sun_azimuth = timedata["Solar Azimuth"].iloc[0] - axis_tilt, axis_azimuth, sun_apparent_zenith, sun_azimuth = ( + axis_slope, axis_azimuth, sun_apparent_zenith, sun_azimuth = ( cast_func(sun_apparent_zenith), cast_func(sun_azimuth), - cast_func(axis_tilt), + cast_func(axis_slope), cast_func(axis_azimuth), ) - psz = psz_func(sun_apparent_zenith, axis_azimuth, axis_tilt, axis_azimuth) + psz = psz_func(sun_apparent_zenith, axis_azimuth, axis_slope, axis_azimuth) assert isinstance(psz, cast_type) @@ -261,7 +260,7 @@ def sf1d_premises_and_expected(): ), ) # fmt: skip - test_data["cross_axis_tilt"] = atand( + test_data["cross_axis_slope"] = atand( (test_data["z_R"] - test_data["z_L"]) / (test_data["x_L"] - test_data["x_R"]) ) @@ -317,7 +316,7 @@ def test_shaded_fraction1d_unprovided_shading_row_rotation(): test_data = pd.DataFrame( columns=[ "shaded_row_rotation", "surface_to_axis_offset", "collector_width", - "solar_zenith", "cross_axis_tilt", "pitch", "solar_azimuth", + "solar_zenith", "cross_axis_slope", "pitch", "solar_azimuth", "axis_azimuth", "expected_sf", ], data=[ @@ -332,8 +331,7 @@ def test_shaded_fraction1d_unprovided_shading_row_rotation(): assert_allclose(sf, expected_sf, atol=1e-2) -@fail_on_pvlib_version("0.15.0") -def test_shaded_fraction1d_renamed_cross_axis_slope2cross_axis_tilt(): +def test_shaded_fraction1d_renamed_cross_axis_tilt2cross_axis_slope(): # Tests shaded_fraction1d with cross_axis_slope instead of cross_axis_tilt with pytest.warns(pvlibDeprecationWarning, match="cross_axis_slope"): shading.shaded_fraction1d( @@ -344,7 +342,7 @@ def test_shaded_fraction1d_renamed_cross_axis_slope2cross_axis_tilt(): collector_width=3, pitch=7, surface_to_axis_offset=0, - cross_axis_slope=0, + cross_axis_tilt=0, ) @@ -408,3 +406,19 @@ def test_direct_martinez(direct_martinez_Table2): test_data, power_losses_expected = direct_martinez_Table2 power_losses = shading.direct_martinez(**test_data) assert_allclose(power_losses, power_losses_expected, atol=5e-3) + + +def test_projected_solar_zenith_angle_renamed_axis_tilt_to_axis_slope(): + with pytest.warns(pvlibDeprecationWarning, match="axis_slope"): + shading.projected_solar_zenith_angle( + 10, 180, axis_tilt=20, axis_azimuth=30 + ) + + +def test_shaded_fraction1d_renamed_tilt_to_slope(): + with (pytest.warns(pvlibDeprecationWarning, match="axis_slope"), + pytest.warns(pvlibDeprecationWarning, match="cross_axis_slope")): + shading.shaded_fraction1d( + 10, 10, 10, 10, collector_width=2, pitch=0.3, + axis_tilt=10, cross_axis_tilt=10 + ) diff --git a/tests/test_tracking.py b/tests/test_tracking.py index 8f3cb5824d..a0118870ae 100644 --- a/tests/test_tracking.py +++ b/tests/test_tracking.py @@ -7,6 +7,7 @@ import pvlib from pvlib import tracking +from pvlib._deprecation import pvlibDeprecationWarning from .conftest import TESTS_DATA_DIR, assert_frame_equal, assert_series_equal SINGLEAXIS_COL_ORDER = ['tracker_theta', 'aoi', @@ -18,7 +19,7 @@ def test_solar_noon(): apparent_zenith = pd.Series([10], index=index) apparent_azimuth = pd.Series([180], index=index) tracker_data = tracking.singleaxis(apparent_zenith, apparent_azimuth, - axis_tilt=0, axis_azimuth=0, + axis_slope=0, axis_azimuth=0, max_angle=90, backtrack=True, gcr=2.0/7.0) @@ -34,7 +35,7 @@ def test_scalars(): apparent_zenith = 10 apparent_azimuth = 180 tracker_data = tracking.singleaxis(apparent_zenith, apparent_azimuth, - axis_tilt=0, axis_azimuth=0, + axis_slope=0, axis_azimuth=0, max_angle=90, backtrack=True, gcr=2.0/7.0) assert isinstance(tracker_data, dict) @@ -48,7 +49,7 @@ def test_arrays(): apparent_zenith = np.array([10]) apparent_azimuth = np.array([180]) tracker_data = tracking.singleaxis(apparent_zenith, apparent_azimuth, - axis_tilt=0, axis_azimuth=0, + axis_slope=0, axis_azimuth=0, max_angle=90, backtrack=True, gcr=2.0/7.0) assert isinstance(tracker_data, dict) @@ -63,7 +64,7 @@ def test_nans(): apparent_azimuth = np.array([180, 180, np.nan]) with np.errstate(invalid='ignore'): tracker_data = tracking.singleaxis(apparent_zenith, apparent_azimuth, - axis_tilt=0, axis_azimuth=0, + axis_slope=0, axis_azimuth=0, max_angle=90, backtrack=True, gcr=2.0/7.0) expect = {'tracker_theta': np.array([0, nan, nan]), @@ -78,7 +79,7 @@ def test_nans(): apparent_azimuth = pd.Series(apparent_azimuth) with np.errstate(invalid='ignore'): tracker_data = tracking.singleaxis(apparent_zenith, apparent_azimuth, - axis_tilt=0, axis_azimuth=0, + axis_slope=0, axis_azimuth=0, max_angle=90, backtrack=True, gcr=2.0/7.0) expect = pd.DataFrame(np.array( @@ -95,7 +96,7 @@ def test_arrays_multi(): # singleaxis should fail for num dim > 1 with pytest.raises(ValueError): tracking.singleaxis(apparent_zenith, apparent_azimuth, - axis_tilt=0, axis_azimuth=0, + axis_slope=0, axis_azimuth=0, max_angle=90, backtrack=True, gcr=2.0/7.0) # uncomment if we ever get singleaxis to support num dim > 1 arrays @@ -113,7 +114,7 @@ def test_azimuth_north_south(): apparent_azimuth = pd.Series([90]) tracker_data = tracking.singleaxis(apparent_zenith, apparent_azimuth, - axis_tilt=0, axis_azimuth=180, + axis_slope=0, axis_azimuth=180, max_angle=90, backtrack=True, gcr=2.0/7.0) @@ -125,7 +126,7 @@ def test_azimuth_north_south(): assert_frame_equal(expect, tracker_data) tracker_data = tracking.singleaxis(apparent_zenith, apparent_azimuth, - axis_tilt=0, axis_azimuth=0, + axis_slope=0, axis_azimuth=0, max_angle=90, backtrack=True, gcr=2.0/7.0) @@ -138,7 +139,7 @@ def test_max_angle(): apparent_zenith = pd.Series([60]) apparent_azimuth = pd.Series([90]) tracker_data = tracking.singleaxis(apparent_zenith, apparent_azimuth, - axis_tilt=0, axis_azimuth=0, + axis_slope=0, axis_azimuth=0, max_angle=45, backtrack=True, gcr=2.0/7.0) @@ -154,7 +155,7 @@ def test_min_angle(): apparent_zenith = pd.Series([60]) apparent_azimuth = pd.Series([270]) tracker_data = tracking.singleaxis(apparent_zenith, apparent_azimuth, - axis_tilt=0, axis_azimuth=0, + axis_slope=0, axis_azimuth=0, max_angle=(-45, 50), backtrack=True, gcr=2.0/7.0) @@ -171,7 +172,7 @@ def test_backtrack(): apparent_azimuth = pd.Series([90]) tracker_data = tracking.singleaxis(apparent_zenith, apparent_azimuth, - axis_tilt=0, axis_azimuth=0, + axis_slope=0, axis_azimuth=0, max_angle=90, backtrack=False, gcr=2.0/7.0) @@ -183,7 +184,7 @@ def test_backtrack(): assert_frame_equal(expect, tracker_data) tracker_data = tracking.singleaxis(apparent_zenith, apparent_azimuth, - axis_tilt=0, axis_azimuth=0, + axis_slope=0, axis_azimuth=0, max_angle=90, backtrack=True, gcr=2.0/7.0) @@ -195,12 +196,12 @@ def test_backtrack(): assert_frame_equal(expect, tracker_data) -def test_axis_tilt(): +def test_axis_slope(): apparent_zenith = pd.Series([30]) apparent_azimuth = pd.Series([135]) tracker_data = tracking.singleaxis(apparent_zenith, apparent_azimuth, - axis_tilt=30, axis_azimuth=180, + axis_slope=30, axis_azimuth=180, max_angle=90, backtrack=True, gcr=2.0/7.0) @@ -213,7 +214,7 @@ def test_axis_tilt(): assert_frame_equal(expect, tracker_data) tracker_data = tracking.singleaxis(apparent_zenith, apparent_azimuth, - axis_tilt=30, axis_azimuth=0, + axis_slope=30, axis_azimuth=0, max_angle=90, backtrack=True, gcr=2.0/7.0) @@ -230,7 +231,7 @@ def test_axis_azimuth(): apparent_azimuth = pd.Series([90]) tracker_data = tracking.singleaxis(apparent_zenith, apparent_azimuth, - axis_tilt=0, axis_azimuth=90, + axis_slope=0, axis_azimuth=90, max_angle=90, backtrack=True, gcr=2.0/7.0) @@ -245,7 +246,7 @@ def test_axis_azimuth(): apparent_azimuth = pd.Series([180]) tracker_data = tracking.singleaxis(apparent_zenith, apparent_azimuth, - axis_tilt=0, axis_azimuth=90, + axis_slope=0, axis_azimuth=90, max_angle=90, backtrack=True, gcr=2.0/7.0) @@ -265,7 +266,7 @@ def test_horizon_flat(): solar_zenith = pd.Series(solar_zenith) # depending on platform and numpy versions this will generate # RuntimeWarning: invalid value encountered in > < >= - out = tracking.singleaxis(solar_zenith, solar_azimuth, axis_tilt=0, + out = tracking.singleaxis(solar_zenith, solar_azimuth, axis_slope=0, axis_azimuth=180, backtrack=False, max_angle=180) expected = pd.DataFrame(np.array( [[ nan, nan, nan, nan], @@ -281,7 +282,7 @@ def test_horizon_tilted(): solar_zenith = np.full_like(solar_azimuth, 45) solar_azimuth = pd.Series(solar_azimuth) solar_zenith = pd.Series(solar_zenith) - out = tracking.singleaxis(solar_zenith, solar_azimuth, axis_tilt=90, + out = tracking.singleaxis(solar_zenith, solar_azimuth, axis_slope=90, axis_azimuth=180, backtrack=False, max_angle=180) expected = pd.DataFrame(np.array( [[-180., 45., 0., 90.], @@ -294,7 +295,7 @@ def test_horizon_tilted(): def test_low_sun_angles(): # GH 656, 824 result = tracking.singleaxis( - apparent_zenith=80, apparent_azimuth=338, axis_tilt=30, + apparent_zenith=80, apparent_azimuth=338, axis_slope=30, axis_azimuth=180, max_angle=60, backtrack=True, gcr=0.35) expected = { 'tracker_theta': np.array([60.0]), @@ -305,9 +306,9 @@ def test_low_sun_angles(): assert_allclose(expected[k], v) -def test_calc_axis_tilt(): +def test_calc_axis_slope(): # expected values - expected_axis_tilt = 2.239 # [degrees] + expected_axis_slope = 2.239 # [degrees] expected_side_slope = 9.86649274360294 # [degrees] expected = TESTS_DATA_DIR / 'singleaxis_tracker_wslope.csv' expected = pd.read_csv(expected, index_col='timestamp', parse_dates=True) @@ -324,16 +325,16 @@ def test_calc_axis_tilt(): # Note: GCR is relative to horizontal distance between rows gcr = 0.33292759 # GCR = length / horizontal_pitch = 1.64 / 5 / cos(9.86) # calculate tracker axis zenith - axis_tilt = tracking.calc_axis_tilt( + axis_slope = tracking.calc_axis_slope( slope_azimuth, slope_tilt, axis_azimuth=axis_azimuth) - assert np.isclose(axis_tilt, expected_axis_tilt) + assert np.isclose(axis_slope, expected_axis_slope) # calculate cross-axis tilt and relative rotation - cross_axis_tilt = tracking.calc_cross_axis_tilt( - slope_azimuth, slope_tilt, axis_azimuth, axis_tilt) - assert np.isclose(cross_axis_tilt, expected_side_slope) + cross_axis_slope = tracking.calc_cross_axis_slope( + slope_azimuth, slope_tilt, axis_azimuth, axis_slope) + assert np.isclose(cross_axis_slope, expected_side_slope) sat = tracking.singleaxis( - solpos.apparent_zenith, solpos.azimuth, axis_tilt, axis_azimuth, - max_angle, backtrack=True, gcr=gcr, cross_axis_tilt=cross_axis_tilt) + solpos.apparent_zenith, solpos.azimuth, axis_slope, axis_azimuth, + max_angle, backtrack=True, gcr=gcr, cross_axis_slope=cross_axis_slope) np.testing.assert_allclose( sat['tracker_theta'], expected['tracker_theta'], atol=1e-7) np.testing.assert_allclose(sat['aoi'], expected['aoi'], atol=1e-7) @@ -362,28 +363,28 @@ def test_slope_aware_backtracking(): ( 0.524817, 239.330401, 79.530, 5.490), ], columns=['ApparentElevation', 'SolarAzimuth', 'TrueTracking', 'Backtracking']) - expected_axis_tilt = 9.666 + expected_axis_slope = 9.666 expected_slope_angle = -2.576 slope_azimuth, slope_tilt = 180.0, 10.0 axis_azimuth = 195.0 - axis_tilt = tracking.calc_axis_tilt( + axis_slope = tracking.calc_axis_slope( slope_azimuth, slope_tilt, axis_azimuth) - assert np.isclose(axis_tilt, expected_axis_tilt, rtol=1e-3, atol=1e-3) - cross_axis_tilt = tracking.calc_cross_axis_tilt( - slope_azimuth, slope_tilt, axis_azimuth, axis_tilt) + assert np.isclose(axis_slope, expected_axis_slope, rtol=1e-3, atol=1e-3) + cross_axis_slope = tracking.calc_cross_axis_slope( + slope_azimuth, slope_tilt, axis_azimuth, axis_slope) assert np.isclose( - cross_axis_tilt, expected_slope_angle, rtol=1e-3, atol=1e-3) + cross_axis_slope, expected_slope_angle, rtol=1e-3, atol=1e-3) sat = tracking.singleaxis( 90.0-expected_data['ApparentElevation'], expected_data['SolarAzimuth'], - axis_tilt, axis_azimuth, max_angle=90.0, backtrack=True, gcr=0.5, - cross_axis_tilt=cross_axis_tilt) + axis_slope, axis_azimuth, max_angle=90.0, backtrack=True, gcr=0.5, + cross_axis_slope=cross_axis_slope) assert_series_equal(sat['tracker_theta'], expected_data['Backtracking'].rename('tracker_theta'), check_less_precise=True) truetracking = tracking.singleaxis( 90.0-expected_data['ApparentElevation'], expected_data['SolarAzimuth'], - axis_tilt, axis_azimuth, max_angle=90.0, backtrack=False, gcr=0.5, - cross_axis_tilt=cross_axis_tilt) + axis_slope, axis_azimuth, max_angle=90.0, backtrack=False, gcr=0.5, + cross_axis_slope=cross_axis_slope) assert_series_equal(truetracking['tracker_theta'], expected_data['TrueTracking'].rename('tracker_theta'), check_less_precise=True) @@ -397,7 +398,7 @@ def test_singleaxis_aoi_gh1221(): tz='Etc/GMT+6') sp = loc.get_solarposition(dr) tr = pvlib.tracking.singleaxis( - sp['apparent_zenith'], sp['azimuth'], axis_tilt=90, axis_azimuth=180, + sp['apparent_zenith'], sp['azimuth'], axis_slope=90, axis_azimuth=180, max_angle=0.001, backtrack=False) fixed = pvlib.irradiance.aoi(90, 180, sp['apparent_zenith'], sp['azimuth']) fixed[np.isnan(tr['aoi'])] = np.nan @@ -435,7 +436,7 @@ def test_calc_surface_orientation_kwargs(): expected_tilts = np.array([22.2687445, 20.0, 22.2687445]) expected_azimuths = np.array([152.72683041, 180.0, 207.27316959]) out = tracking.calc_surface_orientation(rotations, - axis_tilt=20, + axis_slope=20, axis_azimuth=180) np.testing.assert_allclose(out['surface_tilt'], expected_tilts) np.testing.assert_allclose(out['surface_azimuth'], expected_azimuths) @@ -450,11 +451,11 @@ def test_calc_surface_orientation_special(): np.testing.assert_allclose(out['surface_tilt'], expected_tilts) np.testing.assert_allclose(out['surface_azimuth'], expected_azimuths) - # special case for axis_tilt + # special case for axis_slope rotations = np.array([-10, 0, 10]) expected_tilts = np.array([90, 90, 90], dtype=float) expected_azimuths = np.array([350, 0, 10], dtype=float) - out = tracking.calc_surface_orientation(rotations, axis_tilt=90) + out = tracking.calc_surface_orientation(rotations, axis_slope=90) np.testing.assert_allclose(out['surface_tilt'], expected_tilts) np.testing.assert_allclose(out['surface_azimuth'], expected_azimuths) @@ -471,3 +472,43 @@ def test_calc_surface_orientation_special(): # in a modulo-360 sense. np.testing.assert_allclose(np.round(out['surface_azimuth'], 4) % 360, expected_azimuths, rtol=1e-5, atol=1e-5) + + +def test_singleaxis_renamed_tilt_to_slope(): + with (pytest.warns(pvlibDeprecationWarning, match="axis_slope"), + pytest.warns(pvlibDeprecationWarning, match="cross_axis_slope")): + tracking.singleaxis( + 10, 10, axis_tilt=10, axis_azimuth=10, cross_axis_tilt=20 + ) + + +def test_calc_surface_orientation_tilt_to_slope(): + with pytest.warns(pvlibDeprecationWarning, match="axis_slope"): + tracking.calc_surface_orientation(10, axis_tilt=10, axis_azimuth=10) + + +def test_deprecated_calc_axis_tilt(): + # renamed to calc_axis_slope + with pytest.warns(pvlibDeprecationWarning, match="calc_axis_slope"): + tracking.calc_axis_tilt(slope_azimuth=1, slope_tilt=1, axis_azimuth=1) + + +def test_deprecated_calc_cross_axis_tilt_and_param_tilt_to_slope(): + # 1. calc_cross_axis_tilt renamed to calc_cross_axis_slope + # 2. parameter axis_tilt renamed to axis_slope + + # just 1. + with pytest.warns(pvlibDeprecationWarning, match="calc_cross_axis_slope"): + tracking.calc_cross_axis_tilt(slope_azimuth=1, slope_tilt=1, + axis_azimuth=1, axis_slope=1) + # just 2. + with pytest.warns(pvlibDeprecationWarning, match="axis_slope"): + tracking.calc_cross_axis_slope(slope_azimuth=1, slope_tilt=1, + axis_azimuth=1, axis_tilt=1) + # both 1. and 2. + with ( + pytest.warns(pvlibDeprecationWarning, match="axis_slope"), + pytest.warns(pvlibDeprecationWarning, match="calc_cross_axis_slope"), + ): # somehow this order is important, the other way around fails + tracking.calc_cross_axis_tilt(slope_azimuth=1, slope_tilt=1, + axis_azimuth=1, axis_tilt=1) From 27b24e743aa047fa2b5a64f919771e5eff8bc0d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Echedey=20Luis=20=C3=81lvarez?= <80125792+echedey-ls@users.noreply.github.com> Date: Wed, 24 Sep 2025 11:42:04 +0200 Subject: [PATCH 11/15] Typo in v0.13.1 whatnew --- docs/sphinx/source/whatsnew/v0.13.1.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sphinx/source/whatsnew/v0.13.1.rst b/docs/sphinx/source/whatsnew/v0.13.1.rst index ff5089aa15..ad3f855ada 100644 --- a/docs/sphinx/source/whatsnew/v0.13.1.rst +++ b/docs/sphinx/source/whatsnew/v0.13.1.rst @@ -11,7 +11,7 @@ Breaking Changes Deprecations ~~~~~~~~~~~~ * Deprecate :py:func:`~pvlib.modelchain.get_orientation`. (:pull:`2495`) -* Rename parameter name ``aparent_azimuth`` to ``solar_azimuth`` in :py:func:`~pvlib.tracking.singleaxis`. +* Rename parameter name ``apparent_azimuth`` to ``solar_azimuth`` in :py:func:`~pvlib.tracking.singleaxis`. (:issue:`2479`, :pull:`2480`) * Rename ``axis_tilt`` and ``cross_axis_tilt`` terms to ``axis_slope`` and ``cross_axis_slope`` all through the project. Affects: From 3625a5a5aa545922404c87d313237318437ef668 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Echedey=20Luis=20=C3=81lvarez?= <80125792+echedey-ls@users.noreply.github.com> Date: Wed, 24 Sep 2025 11:44:46 +0200 Subject: [PATCH 12/15] flake8 may forgive me --- .../plot_single_axis_tracking_on_sloped_terrain.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/examples/solar-tracking/plot_single_axis_tracking_on_sloped_terrain.py b/docs/examples/solar-tracking/plot_single_axis_tracking_on_sloped_terrain.py index 7131513a71..64a03ce549 100644 --- a/docs/examples/solar-tracking/plot_single_axis_tracking_on_sloped_terrain.py +++ b/docs/examples/solar-tracking/plot_single_axis_tracking_on_sloped_terrain.py @@ -145,7 +145,7 @@ # calculate the cross-axis tilt: cross_axis_slope = tracking.calc_cross_axis_slope(slope_azimuth, slope_tilt, - axis_azimuth, axis_slope) + axis_azimuth, axis_slope) print('Axis tilt:', '{:0.01f}°'.format(axis_slope)) print('Cross-axis tilt:', '{:0.01f}°'.format(cross_axis_slope)) From de940218bfb9c3a62b219e9d0c50ac16fa8fd49b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Echedey=20Luis=20=C3=81lvarez?= <80125792+echedey-ls@users.noreply.github.com> Date: Wed, 24 Sep 2025 11:55:24 +0200 Subject: [PATCH 13/15] whatsnew improvements --- docs/sphinx/source/whatsnew/v0.13.1.rst | 6 +++--- docs/sphinx/source/whatsnew/v0.8.0.rst | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/sphinx/source/whatsnew/v0.13.1.rst b/docs/sphinx/source/whatsnew/v0.13.1.rst index 535c01805d..52375aaff8 100644 --- a/docs/sphinx/source/whatsnew/v0.13.1.rst +++ b/docs/sphinx/source/whatsnew/v0.13.1.rst @@ -13,14 +13,14 @@ Deprecations * Deprecate :py:func:`~pvlib.modelchain.get_orientation`. (:pull:`2495`) * Rename parameter name ``apparent_azimuth`` to ``solar_azimuth`` in :py:func:`~pvlib.tracking.singleaxis`. (:issue:`2479`, :pull:`2480`) -* Rename ``axis_tilt`` and ``cross_axis_tilt`` terms to ``axis_slope`` and ``cross_axis_slope`` all through the project. Affects: +* Rename ``axis_tilt`` and ``cross_axis_tilt`` to ``axis_slope`` and ``cross_axis_slope`` all through the project. Affects: - :py:func:`pvlib.shading.shaded_fraction1d` parameters ``cross_axis_tilt`` and ``axis_tilt`` - :py:func:`pvlib.shading.projected_solar_zenith_angle` parameter ``axis_tilt`` - :py:func:`pvlib.tracking.singleaxis` parameters ``cross_axis_tilt`` and ``axis_tilt`` - :py:func:`pvlib.tracking.calc_surface_orientation` parameter ``axis_tilt`` - - :py:func:`!pvlib.tracking.calc_axis_tilt` function signature is renamed to :py:func:`pvlib.tracking.calc_axis_slope` - - :py:func:`!pvlib.tracking.calc_cross_axis_tilt` function signature is renamed to :py:func:`pvlib.tracking.calc_cross_axis_slope` and parameter ``axis_tilt`` + - :py:func:`!pvlib.tracking.calc_axis_tilt` function is renamed to :py:func:`~pvlib.tracking.calc_axis_slope` + - :py:func:`!pvlib.tracking.calc_cross_axis_tilt` function is renamed to :py:func:`~pvlib.tracking.calc_cross_axis_slope`, and its parameter ``axis_tilt`` - :py:class:`pvlib.pvsystem.SingleAxisTrackerMount` dataclass fields ``axis_tilt`` and ``cross_axis_tilt`` (:issue:`2334`, :pull:`2543`) diff --git a/docs/sphinx/source/whatsnew/v0.8.0.rst b/docs/sphinx/source/whatsnew/v0.8.0.rst index 04e2d16765..f3e9ae89be 100644 --- a/docs/sphinx/source/whatsnew/v0.8.0.rst +++ b/docs/sphinx/source/whatsnew/v0.8.0.rst @@ -110,14 +110,14 @@ Enhancements * Added ``racking_model``, ``module_type``, and ``temperature_model_parameters`` to :py:class:`~pvlib.pvsystem.PVSystem` and :py:class:`~pvlib.tracking.SingleAxisTracker` repr methods. (:issue:`1027`) -* Added :py:func:`!~pvlib.tracking.calc_axis_tilt` to calculate the - tracker axes tilt and :py:func:`!~pvlib.tracking.calc_cross_axis_tilt` to +* Added :py:func:`!pvlib.tracking.calc_axis_tilt` to calculate the + tracker axes tilt and :py:func:`!pvlib.tracking.calc_cross_axis_tilt` to calculate the cross-axis tilt, which is the angle, relative to horizontal, of the line formed by the intersection between the slope containing the tracker axes and a plane perpendicular to the tracker axes. (:pull:`823`) * Added ``cross_axis_tilt`` argument to :py:func:`~pvlib.tracking.singleaxis` and :py:func:`~pvlib.tracking.SingleAxisTracker` which defaults to zero. Use - :py:func:`!~pvlib.tracking.calc_cross_axis_tilt` to calculate the cross-axis + :py:func:`!pvlib.tracking.calc_cross_axis_tilt` to calculate the cross-axis tilt angle if necessary. (:pull:`823`) * Added ability for :py:func:`pvlib.soiling.hsu` to accept arbitrary time intervals. (:pull:`980`) * Added :py:func:`pvlib.temperature.fuentes` for cell temperature modeling. (:pull:`1037`) From 7e5223435a952bbf27dd1ed93011728b4837014d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Echedey=20Luis=20=C3=81lvarez?= <80125792+echedey-ls@users.noreply.github.com> Date: Wed, 1 Oct 2025 18:20:32 +0200 Subject: [PATCH 14/15] move whatsnew entries --- docs/sphinx/source/whatsnew/v0.13.1.rst | 11 ----------- docs/sphinx/source/whatsnew/v0.13.2.rst | 11 +++++++++++ 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/docs/sphinx/source/whatsnew/v0.13.1.rst b/docs/sphinx/source/whatsnew/v0.13.1.rst index 2e5d8fdd56..b056803b2b 100644 --- a/docs/sphinx/source/whatsnew/v0.13.1.rst +++ b/docs/sphinx/source/whatsnew/v0.13.1.rst @@ -10,17 +10,6 @@ Deprecations * Deprecate :py:func:`pvlib.modelchain.get_orientation`. (:pull:`2495`) * Rename parameter name ``apparent_azimuth`` to ``solar_azimuth`` in :py:func:`~pvlib.tracking.singleaxis`. (:issue:`2479`, :pull:`2480`) -* Rename ``axis_tilt`` and ``cross_axis_tilt`` to ``axis_slope`` and ``cross_axis_slope`` all through the project. Affects: - - - :py:func:`pvlib.shading.shaded_fraction1d` parameters ``cross_axis_tilt`` and ``axis_tilt`` - - :py:func:`pvlib.shading.projected_solar_zenith_angle` parameter ``axis_tilt`` - - :py:func:`pvlib.tracking.singleaxis` parameters ``cross_axis_tilt`` and ``axis_tilt`` - - :py:func:`pvlib.tracking.calc_surface_orientation` parameter ``axis_tilt`` - - :py:func:`!pvlib.tracking.calc_axis_tilt` function is renamed to :py:func:`~pvlib.tracking.calc_axis_slope` - - :py:func:`!pvlib.tracking.calc_cross_axis_tilt` function is renamed to :py:func:`~pvlib.tracking.calc_cross_axis_slope`, and its parameter ``axis_tilt`` - - :py:class:`pvlib.pvsystem.SingleAxisTrackerMount` dataclass fields ``axis_tilt`` and ``cross_axis_tilt`` - - (:issue:`2334`, :pull:`2543`) Bug fixes ~~~~~~~~~ diff --git a/docs/sphinx/source/whatsnew/v0.13.2.rst b/docs/sphinx/source/whatsnew/v0.13.2.rst index f649a93fd0..d4009ccd4e 100644 --- a/docs/sphinx/source/whatsnew/v0.13.2.rst +++ b/docs/sphinx/source/whatsnew/v0.13.2.rst @@ -6,6 +6,17 @@ v0.13.2 (Anticipated December, 2025) Breaking Changes ~~~~~~~~~~~~~~~~ +* Rename ``axis_tilt`` and ``cross_axis_tilt`` to ``axis_slope`` and ``cross_axis_slope`` all through the project. Affects: + + - :py:func:`pvlib.shading.shaded_fraction1d` parameters ``cross_axis_tilt`` and ``axis_tilt`` + - :py:func:`pvlib.shading.projected_solar_zenith_angle` parameter ``axis_tilt`` + - :py:func:`pvlib.tracking.singleaxis` parameters ``cross_axis_tilt`` and ``axis_tilt`` + - :py:func:`pvlib.tracking.calc_surface_orientation` parameter ``axis_tilt`` + - :py:func:`!pvlib.tracking.calc_axis_tilt` function is renamed to :py:func:`~pvlib.tracking.calc_axis_slope` + - :py:func:`!pvlib.tracking.calc_cross_axis_tilt` function is renamed to :py:func:`~pvlib.tracking.calc_cross_axis_slope`, and its parameter ``axis_tilt`` + - :py:class:`pvlib.pvsystem.SingleAxisTrackerMount` dataclass fields ``axis_tilt`` and ``cross_axis_tilt`` + + (:issue:`2334`, :pull:`2543`) Deprecations From d8467bc43d376d5a096ae2b47b38d828ca64060c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Echedey=20Luis=20=C3=81lvarez?= <80125792+echedey-ls@users.noreply.github.com> Date: Wed, 1 Oct 2025 18:22:01 +0200 Subject: [PATCH 15/15] rm whatnew duplicated from merge entry --- docs/sphinx/source/whatsnew/v0.13.1.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/sphinx/source/whatsnew/v0.13.1.rst b/docs/sphinx/source/whatsnew/v0.13.1.rst index b056803b2b..00e117b86b 100644 --- a/docs/sphinx/source/whatsnew/v0.13.1.rst +++ b/docs/sphinx/source/whatsnew/v0.13.1.rst @@ -6,7 +6,6 @@ v0.13.1 (September 24, 2025) Deprecations ~~~~~~~~~~~~ -* Deprecate :py:func:`~pvlib.modelchain.get_orientation`. (:pull:`2495`) * Deprecate :py:func:`pvlib.modelchain.get_orientation`. (:pull:`2495`) * Rename parameter name ``apparent_azimuth`` to ``solar_azimuth`` in :py:func:`~pvlib.tracking.singleaxis`. (:issue:`2479`, :pull:`2480`)