From 662846e16b5b211daea0a6d46d6a5dc41c2c38ab Mon Sep 17 00:00:00 2001 From: Anton Driesse Date: Sun, 12 Nov 2023 21:44:32 +0100 Subject: [PATCH 01/26] Add reverse transposition function and two helpers. --- pvlib/irradiance.py | 155 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 155 insertions(+) diff --git a/pvlib/irradiance.py b/pvlib/irradiance.py index efae7f6236..4c8080b493 100644 --- a/pvlib/irradiance.py +++ b/pvlib/irradiance.py @@ -1444,6 +1444,161 @@ def perez_driesse(surface_tilt, surface_azimuth, dhi, dni, dni_extra, return sky_diffuse +def _transpose(surface_tilt, surface_azimuth, + solar_zenith, solar_azimuth, + ghi, + dni_extra, airmass, albedo): + ''' + Transposition function that includes decomposition if GHI using the + continuous Erbs-Driesse model. + + Helper function for rtranspose_driesse_2023. + ''' + # Contributed by Anton Driesse (@adriesse), PV Performance Labs. Nov., 2023 + + erbsout = erbs_driesse(ghi, solar_zenith, dni_extra=dni_extra) + + dni = erbsout['dni'] + dhi = erbsout['dhi'] + + irrads = get_total_irradiance(surface_tilt, surface_azimuth, + solar_zenith, solar_azimuth, + dni, ghi, dhi, + dni_extra, airmass, albedo, + model='perez-driesse') + + return irrads['poa_global'] + + +def _rtranspose(surface_tilt, surface_azimuth, + solar_zenith, solar_azimuth, + poa_global, + dni_extra, airmass, albedo, + xtol=0.01): + ''' + Reverse transposition function that uses the scalar bisection from scipy. + + Helper function for rtranspose_driesse_2023. + ''' + # Contributed by Anton Driesse (@adriesse), PV Performance Labs. Nov., 2023 + + # propagate nans and zeros quickly + if np.isnan(poa_global): + return np.nan, False, 0 + if poa_global <= 0: + return 0.0, True, 0 + + # function whose root needs to be found + def poa_error(ghi): + poa_hat = _transpose(surface_tilt, surface_azimuth, + solar_zenith, solar_azimuth, + ghi, + dni_extra, airmass, albedo) + return poa_hat - poa_global + + # calculate an upper bound for ghi using clearness index 1.25 + ghi_clear = dni_extra * tools.cosd(solar_zenith) + ghi_high = np.maximum(10, 1.25 * ghi_clear) + + try: + result = bisect(poa_error, + a=0, + b=ghi_high, + xtol=xtol, + maxiter=25, + full_output=True, + disp=False, + ) + except ValueError: + # this occurs when poa_error has the same sign at both end points + ghi = np.nan + conv = False + niter = -1 + else: + ghi = result[0] + conv = result[1].converged + niter = result[1].iterations + + return ghi, conv, niter + + +def rtranspose_driesse_2023(surface_tilt, surface_azimuth, + solar_zenith, solar_azimuth, + poa_global, + dni_extra=None, airmass=None, albedo=0.25, + xtol=0.01): + ''' + Estimate global horizontal irradiance (GHI) from global plane-of-array + (POA) irradiance. This reverse transposition algorithm uses a bisection + search together with the continuous Perez-Driesse transposition and + continuous Erbs-Driesse decomposition models, as described in _[1]. + + Parameters + ---------- + surface_tilt : numeric + Panel tilt from horizontal. [degree] + surface_azimuth : numeric + Panel azimuth from north. [degree] + solar_zenith : numeric + Solar zenith angle. [degree] + solar_azimuth : numeric + Solar azimuth angle. [degree] + poa_global : numeric + Plane-of-array global irradiance, aka global tilted irradiance. [W/m^2] + dni_extra : None or numeric, default None + Extraterrestrial direct normal irradiance. [W/m^2] + airmass : None or numeric, default None + Relative airmass (not adjusted for pressure). [unitless] + albedo : numeric, default 0.25 + Ground surface albedo. [unitless] + xtol : numeric, default 0.01 + Convergence criterion. The estimated GHI will be within xtol of the + true value. [W/m^2] + + Returns + ------- + ghi : numeric + Estimated GHI. [W/m^2] + conv : boolean + Indicates which elements converged successfully. + niter : integer + Indicates how many bisection steps were done. + + Notes + ----- + Since :py:func:`bisect` is not vectorized, high-resolution time series + can be quite slow to process. + + References + ---------- + .. [1] A. Driesse, A. Jensen, R. Perez, A Continuous Form of the Perez + Diffuse Sky Model for Forward and Reverse Transposition, accepted + for publication in the Solar Energy Journal. + + See also + -------- + perez-driesse + erbs-driesse + gti-dirint + ''' + # Contributed by Anton Driesse (@adriesse), PV Performance Labs. Nov., 2023 + + rtranspose_array = np.vectorize(_rtranspose) + + ghi, conv, niter = rtranspose_array(surface_tilt, surface_azimuth, + solar_zenith, solar_azimuth, + poa_global, + dni_extra, airmass, albedo, + xtol=0.01) + + if isinstance(poa_global, pd.Series): + ghi = pd.Series(ghi, poa_global.index) + conv = pd.Series(conv, poa_global.index) + niter = pd.Series(niter, poa_global.index) + + return ghi, conv, niter + + def clearsky_index(ghi, clearsky_ghi, max_clearsky_index=2.0): """ Calculate the clearsky index. From 24772a96e5b7599bf04b77634369b59a8c36bb68 Mon Sep 17 00:00:00 2001 From: Anton Driesse Date: Sun, 12 Nov 2023 21:51:59 +0100 Subject: [PATCH 02/26] Add missing import. --- pvlib/irradiance.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/pvlib/irradiance.py b/pvlib/irradiance.py index 4c8080b493..a465db8b24 100644 --- a/pvlib/irradiance.py +++ b/pvlib/irradiance.py @@ -11,6 +11,7 @@ import numpy as np import pandas as pd from scipy.interpolate import splev +from scipy.optimize import bisect from pvlib import atmosphere, solarposition, tools import pvlib # used to avoid dni name collision in complete_irradiance @@ -1585,11 +1586,11 @@ def rtranspose_driesse_2023(surface_tilt, surface_azimuth, rtranspose_array = np.vectorize(_rtranspose) - ghi, conv, niter = rtranspose_array(surface_tilt, surface_azimuth, - solar_zenith, solar_azimuth, - poa_global, - dni_extra, airmass, albedo, - xtol=0.01) + ghi, conv, niter = rtranspose_array(surface_tilt, surface_azimuth, + solar_zenith, solar_azimuth, + poa_global, + dni_extra, airmass, albedo, + xtol=0.01) if isinstance(poa_global, pd.Series): ghi = pd.Series(ghi, poa_global.index) From 60b103a7d54fd7aaa5bd36ff72cb20e35b4fcd8f Mon Sep 17 00:00:00 2001 From: Anton Driesse Date: Sun, 12 Nov 2023 21:58:07 +0100 Subject: [PATCH 03/26] Add to docs under transposition until a better place is found/made. --- docs/sphinx/source/reference/irradiance/transposition.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/sphinx/source/reference/irradiance/transposition.rst b/docs/sphinx/source/reference/irradiance/transposition.rst index 7b3624e692..b7e3295c5f 100644 --- a/docs/sphinx/source/reference/irradiance/transposition.rst +++ b/docs/sphinx/source/reference/irradiance/transposition.rst @@ -15,3 +15,4 @@ Transposition models irradiance.klucher irradiance.reindl irradiance.king + irradiance.rtranspose_driesse_2023 From 2c4e4b9d1cc6e3ae221196d4456c8d7d1bd40845 Mon Sep 17 00:00:00 2001 From: Anton Driesse Date: Sun, 12 Nov 2023 22:10:26 +0100 Subject: [PATCH 04/26] Minor doc string fixes. --- pvlib/irradiance.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pvlib/irradiance.py b/pvlib/irradiance.py index a465db8b24..ea7a4488b3 100644 --- a/pvlib/irradiance.py +++ b/pvlib/irradiance.py @@ -1532,7 +1532,7 @@ def rtranspose_driesse_2023(surface_tilt, surface_azimuth, Estimate global horizontal irradiance (GHI) from global plane-of-array (POA) irradiance. This reverse transposition algorithm uses a bisection search together with the continuous Perez-Driesse transposition and - continuous Erbs-Driesse decomposition models, as described in _[1]. + continuous Erbs-Driesse decomposition models, as described in [1]_. Parameters ---------- @@ -1567,8 +1567,8 @@ def rtranspose_driesse_2023(surface_tilt, surface_azimuth, Notes ----- - Since :py:func:`bisect` is not vectorized, high-resolution time series - can be quite slow to process. + Since :py:func:`scipy.optimize.bisect` is not vectorized, high-resolution + time series can be quite slow to process. References ---------- @@ -1578,9 +1578,9 @@ def rtranspose_driesse_2023(surface_tilt, surface_azimuth, See also -------- - perez-driesse - erbs-driesse - gti-dirint + perez_driesse + erbs_driesse + gti_dirint ''' # Contributed by Anton Driesse (@adriesse), PV Performance Labs. Nov., 2023 From 9bf906460701324e91fb97530ef58570b2b5ba21 Mon Sep 17 00:00:00 2001 From: Anton Driesse Date: Mon, 13 Nov 2023 21:57:24 +0100 Subject: [PATCH 05/26] Add full_output option similar to newton(). --- pvlib/irradiance.py | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/pvlib/irradiance.py b/pvlib/irradiance.py index ea7a4488b3..6e5ac92428 100644 --- a/pvlib/irradiance.py +++ b/pvlib/irradiance.py @@ -1527,7 +1527,8 @@ def rtranspose_driesse_2023(surface_tilt, surface_azimuth, solar_zenith, solar_azimuth, poa_global, dni_extra=None, airmass=None, albedo=0.25, - xtol=0.01): + xtol=0.01, + full_output=False): ''' Estimate global horizontal irradiance (GHI) from global plane-of-array (POA) irradiance. This reverse transposition algorithm uses a bisection @@ -1555,15 +1556,20 @@ def rtranspose_driesse_2023(surface_tilt, surface_azimuth, xtol : numeric, default 0.01 Convergence criterion. The estimated GHI will be within xtol of the true value. [W/m^2] + full_output : boolean, default False + If full_output is False, only ghi is returned, otherwise the return + value is (ghi, converged, niter). (see Returns section for details). Returns ------- ghi : numeric Estimated GHI. [W/m^2] - conv : boolean - Indicates which elements converged successfully. - niter : integer - Indicates how many bisection steps were done. + converged : boolean, optional + Present if full_output=True. Indicates which elements converged + successfully. + niter : integer, optional + Present if full_output=True. Indicates how many bisection iterations + were done. Notes ----- @@ -1597,7 +1603,10 @@ def rtranspose_driesse_2023(surface_tilt, surface_azimuth, conv = pd.Series(conv, poa_global.index) niter = pd.Series(niter, poa_global.index) - return ghi, conv, niter + if full_output: + return ghi, conv, niter + else: + return ghi def clearsky_index(ghi, clearsky_ghi, max_clearsky_index=2.0): From 1196851fb2e030048e1aa8a47a5430b82b1c12ae Mon Sep 17 00:00:00 2001 From: Anton Driesse Date: Mon, 13 Nov 2023 23:21:35 +0100 Subject: [PATCH 06/26] First example for reverse transposition. --- .../plot_rtranpose_year.py | 128 ++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 docs/examples/irradiance-transposition/plot_rtranpose_year.py diff --git a/docs/examples/irradiance-transposition/plot_rtranpose_year.py b/docs/examples/irradiance-transposition/plot_rtranpose_year.py new file mode 100644 index 0000000000..9a67a600be --- /dev/null +++ b/docs/examples/irradiance-transposition/plot_rtranpose_year.py @@ -0,0 +1,128 @@ +""" +Reverse transposition using one year of hourly data +=================================================== + +With a brief look at accuracy and speed. + +Author: Anton Driesse +""" + +# %% +# +# In this example we start with a TMY file and calculate POA global irradiance. +# Then we use :py:func:`pvlib.irradiance.rtranspose_driesse_2023` to estimate +# the original GHI from POA global. Details of the method are more fully +# described in [1]_. +# +# References +# ---------- +# .. [1] A. Driesse, A. Jensen, R. Perez, A Continuous Form of the Perez +# Diffuse Sky Model for Forward and Reverse Transposition, accepted +# for publication in the Solar Energy Journal. +# + +import os +import pandas as pd +import matplotlib.pyplot as plt + +import pvlib +from pvlib import iotools, location, irradiance +from pvlib.irradiance import (get_extra_radiation, + get_total_irradiance, + rtranspose_driesse_2023, + aoi, + ) + +from timeit import timeit + +# %% +# +# Read a TMY3 file containing weather data and select needed columns +# + +PVLIB_DIR = pvlib.__path__[0] +DATA_FILE = os.path.join(PVLIB_DIR, 'data', '723170TYA.CSV') + +tmy, metadata = iotools.read_tmy3(DATA_FILE, coerce_year=1990, + map_variables=True) + +df = pd.DataFrame({'ghi': tmy['ghi'], 'dhi': tmy['dhi'], 'dni': tmy['dni'], + 'temp_air': tmy['temp_air'], + 'wind_speed': tmy['wind_speed'], + }) + +# %% +# +# Shift timestamps to middle of hour and then calculate sun positions +# + +df.index = df.index - pd.Timedelta(minutes=30) + +loc = location.Location.from_tmy(metadata) +solpos = loc.get_solarposition(df.index) + +# %% +# +# Estimate global irradiance on a fixed-tilt array (forward transposition) +# The array is tilted 30 degrees an oriented 30 degrees east of south. +# The Perez-Driesse model is used for this to match the reverse transposition +# later, but the match is not perfect because the diffuse fraction of the data +# does not match the Erbs model. +# + +TILT = 30 +ORIENT = 150 + +df['dni_extra'] = get_extra_radiation(df.index) + +total_irrad = get_total_irradiance(TILT, ORIENT, + solpos.apparent_zenith, solpos.azimuth, + df.dni, df.ghi, df.dhi, + dni_extra=df.dni_extra, + model='perez-driesse') + +df['poa_global'] = total_irrad.poa_global +df['aoi'] = aoi(TILT, ORIENT, solpos.apparent_zenith, solpos.azimuth) + +# %% +# +# Now estimate ghi from poa_global using reverse transposition +# This step uses a +# + +df['ghi_rev'] = rtranspose_driesse_2023(TILT, ORIENT, + solpos.apparent_zenith, solpos.azimuth, + df.poa_global, + dni_extra=df.dni_extra) + +# %% +# +# Let's see how long this takes +# + +def run_rtranspose(): + rtranspose_driesse_2023(TILT, ORIENT, + solpos.apparent_zenith, solpos.azimuth, + df.poa_global, + dni_extra=df.dni_extra) + +elapsed = timeit('run_rtranspose()', number=1, globals=globals()) + +print('Elapsed time for reverse transposition: %.1f s' % elapsed) + +# %% +# +# Errors occur mostly at high incidence angles. +# +df = df.sort_values('aoi') + +plt.figure() +pc = plt.scatter(df['ghi'], df['ghi_rev'], c=df['aoi'], s=15, + cmap='jet', vmin=60, vmax=120) +plt.colorbar(label='AOI [°]', ax=plt.gca()) +pc.set_alpha(0.5) +plt.grid(alpha=0.5) +plt.xlabel('GHI original [W/m²]') +plt.ylabel('GHI from POA [W/m²]') +plt.show() + From bf9105aa757554be1fe0ef075257ad3cbe43473f Mon Sep 17 00:00:00 2001 From: Anton Driesse Date: Mon, 13 Nov 2023 23:50:25 +0100 Subject: [PATCH 07/26] Second example for reverse transposition. --- .../plot_rtranpose_limitations.py | 135 ++++++++++++++++++ .../plot_rtranpose_year.py | 5 +- 2 files changed, 138 insertions(+), 2 deletions(-) create mode 100644 docs/examples/irradiance-transposition/plot_rtranpose_limitations.py diff --git a/docs/examples/irradiance-transposition/plot_rtranpose_limitations.py b/docs/examples/irradiance-transposition/plot_rtranpose_limitations.py new file mode 100644 index 0000000000..84f9064a03 --- /dev/null +++ b/docs/examples/irradiance-transposition/plot_rtranpose_limitations.py @@ -0,0 +1,135 @@ +""" +Explore the limitations of reverse transposition +================================================ + +Unfortunately, sometimes there is more than one solution. + +Author: Anton Driesse + +""" + +# %% +# +# In this example we look at a single point in time and consider a full range +# of possible GHI and POA global values as shown in figures 3 and 4 of [1]_. +# Then we use :py:func:`pvlib.irradiance.rtranspose_driesse_2023` to estimate +# the original GHI from POA global. +# +# References +# ---------- +# .. [1] A. Driesse, A. Jensen, R. Perez, A Continuous Form of the Perez +# Diffuse Sky Model for Forward and Reverse Transposition, accepted +# for publication in the Solar Energy Journal. +# + +import numpy as np +import pandas as pd + +import matplotlib.pyplot as plt +import matplotlib +matplotlib.rcParams['axes.grid'] = True + +import pvlib +from pvlib import iotools, location +from pvlib.irradiance import (erbs_driesse, + get_total_irradiance, + rtranspose_driesse_2023, + ) + +# %% define conditions for figure 3 in [1]_ + +dni_extra = 1366.1 +albedo = 0.25 +surface_tilt = 40 +surface_azimuth = 180 + +solar_azimuth = 82 +solar_zenith = 75 + +ghi = np.linspace(0, 500, 100+1) + +# %% transpose forward + +erbsout = erbs_driesse(ghi, solar_zenith, dni_extra=dni_extra) + +dni = erbsout['dni'] +dhi = erbsout['dhi'] + +irrads = get_total_irradiance(surface_tilt, surface_azimuth, + solar_zenith, solar_azimuth, + dni, ghi, dhi, + dni_extra, #airmass, albedo, + model='perez-driesse') + +gti = irrads['poa_global'] + +# %% reverse transpose a single GTI value of 200 W/m² + +gti_test = 200 + +ghi_hat = rtranspose_driesse_2023(surface_tilt, surface_azimuth, + solar_zenith, solar_azimuth, + gti_test, + dni_extra, + full_output=False, + ) + +plt.figure() +plt.plot(ghi, gti, 'g-') +plt.axvline(ghi_hat, color='b') +plt.axhline(gti_test, color='b') +plt.plot(ghi_hat, gti_test, 'bs') +plt.annotate('(%.2f, %.0f)' % (ghi_hat, gti_test), + xy=(ghi_hat-2, 200+2), + xytext=(ghi_hat-20, 200+20), + ha='right', + arrowprops={'arrowstyle':'simple'}) +plt.xlim(0, 500) +plt.ylim(0, 250) +plt.xlabel('GHI [W/m²]') +plt.ylabel('GTI or POA [W/m²]') + +# %% change to the conditions for figure 4 in [1]_ + +solar_azimuth = 76 + +# %% transpose forward + +erbsout = erbs_driesse(ghi, solar_zenith, dni_extra=dni_extra) + +dni = erbsout['dni'] +dhi = erbsout['dhi'] + +irrads = get_total_irradiance(surface_tilt, surface_azimuth, + solar_zenith, solar_azimuth, + dni, ghi, dhi, + dni_extra, #airmass, albedo, + model='perez-driesse') + +gti = irrads['poa_global'] + +# %% reverse transpose the full range of possible POA values + +result = rtranspose_driesse_2023(surface_tilt, surface_azimuth, + solar_zenith, solar_azimuth, + gti, + dni_extra, + full_output=True, + ) + +ghi_hat, conv, niter = result +correct = np.isclose(ghi, ghi_hat, atol=0.01) + +plt.figure() + +plt.plot(np.where(correct, ghi, np.nan), + np.where(correct, gti, np.nan), 'g-', label='GHI correct') + +plt.plot(ghi[~correct], gti[~correct], 'r.', label='GHI incorrect') +plt.plot(ghi[~conv], gti[~conv], 'm.', label='out of range (kt > 1.25)') +plt.xlim(0, 500) +plt.ylim(0, 250) +plt.xlabel('GHI [W/m²]') +plt.ylabel('GTI or POA [W/m²]') +plt.legend() +plt.show() diff --git a/docs/examples/irradiance-transposition/plot_rtranpose_year.py b/docs/examples/irradiance-transposition/plot_rtranpose_year.py index 9a67a600be..9105e98a1f 100644 --- a/docs/examples/irradiance-transposition/plot_rtranpose_year.py +++ b/docs/examples/irradiance-transposition/plot_rtranpose_year.py @@ -5,6 +5,7 @@ With a brief look at accuracy and speed. Author: Anton Driesse + """ # %% @@ -64,7 +65,7 @@ # %% # # Estimate global irradiance on a fixed-tilt array (forward transposition) -# The array is tilted 30 degrees an oriented 30 degrees east of south. +# The array is tilted 30 degrees and oriented 30 degrees east of south. # The Perez-Driesse model is used for this to match the reverse transposition # later, but the match is not perfect because the diffuse fraction of the data # does not match the Erbs model. @@ -87,7 +88,7 @@ # %% # # Now estimate ghi from poa_global using reverse transposition -# This step uses a +# The algorithm uses a simple bisection search. # df['ghi_rev'] = rtranspose_driesse_2023(TILT, ORIENT, From 97d4559622690e0ef56b0694c658ef641921eb91 Mon Sep 17 00:00:00 2001 From: Anton Driesse Date: Tue, 14 Nov 2023 00:09:41 +0100 Subject: [PATCH 08/26] Some improvements to the two examples. --- .../plot_rtranpose_limitations.py | 51 ++++++++++++------- .../plot_rtranpose_year.py | 9 +++- 2 files changed, 41 insertions(+), 19 deletions(-) diff --git a/docs/examples/irradiance-transposition/plot_rtranpose_limitations.py b/docs/examples/irradiance-transposition/plot_rtranpose_limitations.py index 84f9064a03..e82afdc2cd 100644 --- a/docs/examples/irradiance-transposition/plot_rtranpose_limitations.py +++ b/docs/examples/irradiance-transposition/plot_rtranpose_limitations.py @@ -23,20 +23,21 @@ # import numpy as np -import pandas as pd -import matplotlib.pyplot as plt import matplotlib -matplotlib.rcParams['axes.grid'] = True +import matplotlib.pyplot as plt -import pvlib -from pvlib import iotools, location from pvlib.irradiance import (erbs_driesse, get_total_irradiance, rtranspose_driesse_2023, ) -# %% define conditions for figure 3 in [1]_ +matplotlib.rcParams['axes.grid'] = True + +# %% +# +# define conditions for figure 3 in [1]_ +# dni_extra = 1366.1 albedo = 0.25 @@ -48,7 +49,10 @@ ghi = np.linspace(0, 500, 100+1) -# %% transpose forward +# %% +# +# transpose forward +# erbsout = erbs_driesse(ghi, solar_zenith, dni_extra=dni_extra) @@ -63,7 +67,10 @@ gti = irrads['poa_global'] -# %% reverse transpose a single GTI value of 200 W/m² +# %% +# +# reverse transpose a single GTI value of 200 W/m² +# gti_test = 200 @@ -83,17 +90,24 @@ xy=(ghi_hat-2, 200+2), xytext=(ghi_hat-20, 200+20), ha='right', - arrowprops={'arrowstyle':'simple'}) + arrowprops={'arrowstyle': 'simple'}) plt.xlim(0, 500) plt.ylim(0, 250) plt.xlabel('GHI [W/m²]') plt.ylabel('GTI or POA [W/m²]') +plt.show() -# %% change to the conditions for figure 4 in [1]_ +# %% +# +# change to the conditions for figure 4 in [1]_ +# solar_azimuth = 76 -# %% transpose forward +# %% +# +# transpose forward +# erbsout = erbs_driesse(ghi, solar_zenith, dni_extra=dni_extra) @@ -108,14 +122,17 @@ gti = irrads['poa_global'] -# %% reverse transpose the full range of possible POA values +# %% +# +# reverse transpose the full range of possible POA values +# result = rtranspose_driesse_2023(surface_tilt, surface_azimuth, - solar_zenith, solar_azimuth, - gti, - dni_extra, - full_output=True, - ) + solar_zenith, solar_azimuth, + gti, + dni_extra, + full_output=True, + ) ghi_hat, conv, niter = result correct = np.isclose(ghi, ghi_hat, atol=0.01) diff --git a/docs/examples/irradiance-transposition/plot_rtranpose_year.py b/docs/examples/irradiance-transposition/plot_rtranpose_year.py index 9105e98a1f..3af9a81a3c 100644 --- a/docs/examples/irradiance-transposition/plot_rtranpose_year.py +++ b/docs/examples/irradiance-transposition/plot_rtranpose_year.py @@ -24,10 +24,12 @@ import os import pandas as pd + +import matplotlib import matplotlib.pyplot as plt import pvlib -from pvlib import iotools, location, irradiance +from pvlib import iotools, location from pvlib.irradiance import (get_extra_radiation, get_total_irradiance, rtranspose_driesse_2023, @@ -36,6 +38,8 @@ from timeit import timeit +matplotlib.rcParams['axes.grid'] = True + # %% # # Read a TMY3 file containing weather data and select needed columns @@ -101,12 +105,14 @@ # Let's see how long this takes # + def run_rtranspose(): rtranspose_driesse_2023(TILT, ORIENT, solpos.apparent_zenith, solpos.azimuth, df.poa_global, dni_extra=df.dni_extra) + elapsed = timeit('run_rtranspose()', number=1, globals=globals()) print('Elapsed time for reverse transposition: %.1f s' % elapsed) @@ -126,4 +132,3 @@ def run_rtranspose(): plt.xlabel('GHI original [W/m²]') plt.ylabel('GHI from POA [W/m²]') plt.show() - From 17b3f3d62f7998db14ea1948cdb4410e23ee3a77 Mon Sep 17 00:00:00 2001 From: Anton Driesse Date: Tue, 14 Nov 2023 00:13:18 +0100 Subject: [PATCH 09/26] Placate flake8. --- .../irradiance-transposition/plot_rtranpose_limitations.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/examples/irradiance-transposition/plot_rtranpose_limitations.py b/docs/examples/irradiance-transposition/plot_rtranpose_limitations.py index e82afdc2cd..e391f9a51c 100644 --- a/docs/examples/irradiance-transposition/plot_rtranpose_limitations.py +++ b/docs/examples/irradiance-transposition/plot_rtranpose_limitations.py @@ -62,7 +62,7 @@ irrads = get_total_irradiance(surface_tilt, surface_azimuth, solar_zenith, solar_azimuth, dni, ghi, dhi, - dni_extra, #airmass, albedo, + dni_extra, model='perez-driesse') gti = irrads['poa_global'] @@ -117,7 +117,7 @@ irrads = get_total_irradiance(surface_tilt, surface_azimuth, solar_zenith, solar_azimuth, dni, ghi, dhi, - dni_extra, #airmass, albedo, + dni_extra, model='perez-driesse') gti = irrads['poa_global'] From 613e70b6cc7714f7e2dcb8579b0b298f0425edd3 Mon Sep 17 00:00:00 2001 From: Anton Driesse Date: Tue, 14 Nov 2023 12:39:29 +0100 Subject: [PATCH 10/26] Add tests for reverse transpostion. --- pvlib/tests/test_irradiance.py | 45 ++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/pvlib/tests/test_irradiance.py b/pvlib/tests/test_irradiance.py index ba66f4dc36..2bfca315ed 100644 --- a/pvlib/tests/test_irradiance.py +++ b/pvlib/tests/test_irradiance.py @@ -782,6 +782,51 @@ def test_dirint_min_cos_zenith_max_zenith(): assert_series_equal(out, expected, check_less_precise=True) +def test_rtranspose_driesse(): + # inputs copied from test_gti_dirint + times = pd.DatetimeIndex( + ['2014-06-24T06-0700', '2014-06-24T09-0700', '2014-06-24T12-0700']) + poa_global = np.array([20, 300, 1000]) + aoi = np.array([100, 70, 10]) + zenith = np.array([80, 45, 20]) + azimuth = np.array([90, 135, 180]) + surface_tilt = 30 + surface_azimuth = 180 + + # test core function + output = irradiance.rtranspose_driesse_2023( + surface_tilt, surface_azimuth, zenith, azimuth, + poa_global, dni_extra=1366.1) + + expected = [22.089, 304.088, 931.143] + assert_allclose(expected, output, atol=0.001) + + # test series output + poa_global = pd.Series([20, 300, 1000], index=times) + + output = irradiance.rtranspose_driesse_2023( + surface_tilt, surface_azimuth, zenith, azimuth, + poa_global, dni_extra=1366.1) + + assert isinstance(output, pd.Series) + + # test full_output option and special cases + poa_global = np.array([0, 1500, np.nan]) + + ghi, conv, niter = irradiance.rtranspose_driesse_2023( + surface_tilt, surface_azimuth, zenith, azimuth, + poa_global, dni_extra=1366.1, full_output=True) + + expected = [0, np.nan, np.nan] + assert_allclose(expected, ghi, atol=0.001) + + expected = [True, False, False] + assert_allclose(expected, conv) + + expected = [0, -1, 0] + assert_allclose(expected, niter) + + def test_gti_dirint(): times = pd.DatetimeIndex( ['2014-06-24T06-0700', '2014-06-24T09-0700', '2014-06-24T12-0700']) From e5025b47e0b71d80a0e170c3dc82843defcdea2d Mon Sep 17 00:00:00 2001 From: Anton Driesse Date: Tue, 14 Nov 2023 14:28:13 +0100 Subject: [PATCH 11/26] Refine examples and fix test. --- .../plot_rtranpose_limitations.py | 65 +++++++++++-------- .../plot_rtranpose_year.py | 11 +++- pvlib/tests/test_irradiance.py | 1 - 3 files changed, 47 insertions(+), 30 deletions(-) diff --git a/docs/examples/irradiance-transposition/plot_rtranpose_limitations.py b/docs/examples/irradiance-transposition/plot_rtranpose_limitations.py index e391f9a51c..4f13adc0f5 100644 --- a/docs/examples/irradiance-transposition/plot_rtranpose_limitations.py +++ b/docs/examples/irradiance-transposition/plot_rtranpose_limitations.py @@ -10,6 +10,8 @@ # %% # +# Introduction +# ------------ # In this example we look at a single point in time and consider a full range # of possible GHI and POA global values as shown in figures 3 and 4 of [1]_. # Then we use :py:func:`pvlib.irradiance.rtranspose_driesse_2023` to estimate @@ -36,7 +38,7 @@ # %% # -# define conditions for figure 3 in [1]_ +# Define the conditions that were used for figure 3 in [1]_. # dni_extra = 1366.1 @@ -47,13 +49,15 @@ solar_azimuth = 82 solar_zenith = 75 -ghi = np.linspace(0, 500, 100+1) - # %% # -# transpose forward +# Define a range of possible GHI values and calculate the corresponding +# POA global. First estimate DNI and DHI using the Erbs-Driesse model, then +# transpose using the Perez-Driesse model. # +ghi = np.linspace(0, 500, 100+1) + erbsout = erbs_driesse(ghi, solar_zenith, dni_extra=dni_extra) dni = erbsout['dni'] @@ -65,28 +69,34 @@ dni_extra, model='perez-driesse') -gti = irrads['poa_global'] +poa_global = irrads['poa_global'] # %% # -# reverse transpose a single GTI value of 200 W/m² +# Suppose you measure that POA global is 200 W/m2. What would GHI be? # -gti_test = 200 +poa_test = 200 ghi_hat = rtranspose_driesse_2023(surface_tilt, surface_azimuth, solar_zenith, solar_azimuth, - gti_test, + poa_test, dni_extra, - full_output=False, - ) + full_output=False) + +print('Estimated GHI: %.2f W/m².' % ghi_hat) + +# %% +# +# Show this result on the graph of all possible combinations of GHI and POA. +# plt.figure() -plt.plot(ghi, gti, 'g-') -plt.axvline(ghi_hat, color='b') -plt.axhline(gti_test, color='b') -plt.plot(ghi_hat, gti_test, 'bs') -plt.annotate('(%.2f, %.0f)' % (ghi_hat, gti_test), +plt.plot(ghi, poa_global, 'k-') +plt.axvline(ghi_hat, color='g', lw=1) +plt.axhline(poa_test, color='g', lw=1) +plt.plot(ghi_hat, poa_test, 'gs') +plt.annotate('GHI=%.2f' % (ghi_hat), xy=(ghi_hat-2, 200+2), xytext=(ghi_hat-20, 200+20), ha='right', @@ -94,19 +104,20 @@ plt.xlim(0, 500) plt.ylim(0, 250) plt.xlabel('GHI [W/m²]') -plt.ylabel('GTI or POA [W/m²]') +plt.ylabel('POA [W/m²]') plt.show() # %% # -# change to the conditions for figure 4 in [1]_ +# Now change the solar azimuth to match the conditions for figure 4 in [1]_. # solar_azimuth = 76 # %% # -# transpose forward +# Again, estimate DNI and DHI using the Erbs-Driesse model, then +# transpose using the Perez-Driesse model. # erbsout = erbs_driesse(ghi, solar_zenith, dni_extra=dni_extra) @@ -120,16 +131,18 @@ dni_extra, model='perez-driesse') -gti = irrads['poa_global'] +poa_global = irrads['poa_global'] # %% # -# reverse transpose the full range of possible POA values +# Now reverse transpose all the POA values and observe that the original +# GHI cannot always be found. There is a range of POA values that +# maps to three possible GHI values. # result = rtranspose_driesse_2023(surface_tilt, surface_azimuth, solar_zenith, solar_azimuth, - gti, + poa_global, dni_extra, full_output=True, ) @@ -138,15 +151,15 @@ correct = np.isclose(ghi, ghi_hat, atol=0.01) plt.figure() - plt.plot(np.where(correct, ghi, np.nan), - np.where(correct, gti, np.nan), 'g-', label='GHI correct') + np.where(correct, poa_global, np.nan), 'g.', label='correct GHI found') +plt.plot(ghi[~correct], poa_global[~correct], 'r.', label='unreachable GHI') +plt.plot(ghi[~conv], poa_global[~conv], 'm.', label='out of range (kt > 1.25)') +plt.axhspan(88, 103, color='y', alpha=0.25, label='problem region') -plt.plot(ghi[~correct], gti[~correct], 'r.', label='GHI incorrect') -plt.plot(ghi[~conv], gti[~conv], 'm.', label='out of range (kt > 1.25)') plt.xlim(0, 500) plt.ylim(0, 250) plt.xlabel('GHI [W/m²]') -plt.ylabel('GTI or POA [W/m²]') +plt.ylabel('POA [W/m²]') plt.legend() plt.show() diff --git a/docs/examples/irradiance-transposition/plot_rtranpose_year.py b/docs/examples/irradiance-transposition/plot_rtranpose_year.py index 3af9a81a3c..9b92574479 100644 --- a/docs/examples/irradiance-transposition/plot_rtranpose_year.py +++ b/docs/examples/irradiance-transposition/plot_rtranpose_year.py @@ -10,6 +10,8 @@ # %% # +# Introduction +# ------------ # In this example we start with a TMY file and calculate POA global irradiance. # Then we use :py:func:`pvlib.irradiance.rtranspose_driesse_2023` to estimate # the original GHI from POA global. Details of the method are more fully @@ -58,7 +60,7 @@ # %% # -# Shift timestamps to middle of hour and then calculate sun positions +# Shift timestamps to the middle of the hour and then calculate sun positions. # df.index = df.index - pd.Timedelta(minutes=30) @@ -102,7 +104,7 @@ # %% # -# Let's see how long this takes +# Let's see how long this takes... # @@ -119,8 +121,11 @@ def run_rtranspose(): # %% # -# Errors occur mostly at high incidence angles. +# This graph shows the reverse transposed values vs. the orinal values. +# The markers are color-coded by angle-of-incidence to highlight that +# errors occur mostly at high incidence angles. # + df = df.sort_values('aoi') plt.figure() diff --git a/pvlib/tests/test_irradiance.py b/pvlib/tests/test_irradiance.py index 2bfca315ed..72a8e374e1 100644 --- a/pvlib/tests/test_irradiance.py +++ b/pvlib/tests/test_irradiance.py @@ -787,7 +787,6 @@ def test_rtranspose_driesse(): times = pd.DatetimeIndex( ['2014-06-24T06-0700', '2014-06-24T09-0700', '2014-06-24T12-0700']) poa_global = np.array([20, 300, 1000]) - aoi = np.array([100, 70, 10]) zenith = np.array([80, 45, 20]) azimuth = np.array([90, 135, 180]) surface_tilt = 30 From aaf34ba99378a603a59269eeb7ddebaafa6269f5 Mon Sep 17 00:00:00 2001 From: Anton Driesse Date: Tue, 14 Nov 2023 14:35:41 +0100 Subject: [PATCH 12/26] Update whatsnew. --- docs/sphinx/source/whatsnew/v0.10.3.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/sphinx/source/whatsnew/v0.10.3.rst b/docs/sphinx/source/whatsnew/v0.10.3.rst index b7eece4108..a82911d0dd 100644 --- a/docs/sphinx/source/whatsnew/v0.10.3.rst +++ b/docs/sphinx/source/whatsnew/v0.10.3.rst @@ -9,6 +9,9 @@ Enhancements ~~~~~~~~~~~~ * Added the continuous Perez-Driesse transposition model. :py:func:`pvlib.irradiance.perez_driesse` (:issue:`1841`, :pull:`1876`) +* Added a reverse transposition algorithm using the Perez-Driesse model. + :py:func:`pvlib.irradiance.rtranspose_driesse_2023` + (:issue:`1901`, :pull:`1907`) * :py:func:`pvlib.bifacial.infinite_sheds.get_irradiance` and :py:func:`pvlib.bifacial.infinite_sheds.get_irradiance_poa` now include shaded fraction in returned variables. (:pull:`1871`) @@ -25,6 +28,7 @@ Testing Documentation ~~~~~~~~~~~~~ * Fixed a plotting issue in the IV curve gallery example (:pull:`1895`) +* Added two examples to demonstrate reverse transposition (:pull:`1907`) Contributors ~~~~~~~~~~~~ From 74f5c06b0607fa1dc6e36ee092ec604a9a8153d9 Mon Sep 17 00:00:00 2001 From: Anton Driesse Date: Tue, 14 Nov 2023 15:05:14 +0100 Subject: [PATCH 13/26] Refine examples. --- .../plot_rtranpose_limitations.py | 12 ++++++---- .../plot_rtranpose_year.py | 22 +++++++++---------- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/docs/examples/irradiance-transposition/plot_rtranpose_limitations.py b/docs/examples/irradiance-transposition/plot_rtranpose_limitations.py index 4f13adc0f5..22c445bdc2 100644 --- a/docs/examples/irradiance-transposition/plot_rtranpose_limitations.py +++ b/docs/examples/irradiance-transposition/plot_rtranpose_limitations.py @@ -2,7 +2,7 @@ Explore the limitations of reverse transposition ================================================ -Unfortunately, sometimes there is more than one solution. +Unfortunately, sometimes there is not a unique solution. Author: Anton Driesse @@ -137,7 +137,9 @@ # # Now reverse transpose all the POA values and observe that the original # GHI cannot always be found. There is a range of POA values that -# maps to three possible GHI values. +# maps to three possible GHI values, and there is not enough information +# to choose one of them. Sometimes we get lucky and the right one comes +# out, other times not. # result = rtranspose_driesse_2023(surface_tilt, surface_azimuth, @@ -151,8 +153,8 @@ correct = np.isclose(ghi, ghi_hat, atol=0.01) plt.figure() -plt.plot(np.where(correct, ghi, np.nan), - np.where(correct, poa_global, np.nan), 'g.', label='correct GHI found') +plt.plot(np.where(correct, ghi, np.nan), np.where(correct, poa_global, np.nan), + 'g.', label='correct GHI found') plt.plot(ghi[~correct], poa_global[~correct], 'r.', label='unreachable GHI') plt.plot(ghi[~conv], poa_global[~conv], 'm.', label='out of range (kt > 1.25)') plt.axhspan(88, 103, color='y', alpha=0.25, label='problem region') @@ -163,3 +165,5 @@ plt.ylabel('POA [W/m²]') plt.legend() plt.show() + +# %% diff --git a/docs/examples/irradiance-transposition/plot_rtranpose_year.py b/docs/examples/irradiance-transposition/plot_rtranpose_year.py index 9b92574479..1ad6a1758c 100644 --- a/docs/examples/irradiance-transposition/plot_rtranpose_year.py +++ b/docs/examples/irradiance-transposition/plot_rtranpose_year.py @@ -13,9 +13,8 @@ # Introduction # ------------ # In this example we start with a TMY file and calculate POA global irradiance. -# Then we use :py:func:`pvlib.irradiance.rtranspose_driesse_2023` to estimate -# the original GHI from POA global. Details of the method are more fully -# described in [1]_. +# Then, we use :py:func:`pvlib.irradiance.rtranspose_driesse_2023` to estimate +# the original GHI from POA global. Details of the method found in [1]_. # # References # ---------- @@ -44,7 +43,7 @@ # %% # -# Read a TMY3 file containing weather data and select needed columns +# Read a TMY3 file containing weather data and select needed columns. # PVLIB_DIR = pvlib.__path__[0] @@ -60,7 +59,7 @@ # %% # -# Shift timestamps to the middle of the hour and then calculate sun positions. +# Shift the timestamps to the middle of the hour and calculate sun positions. # df.index = df.index - pd.Timedelta(minutes=30) @@ -70,11 +69,8 @@ # %% # -# Estimate global irradiance on a fixed-tilt array (forward transposition) +# Estimate global irradiance on a fixed-tilt array (forward transposition). # The array is tilted 30 degrees and oriented 30 degrees east of south. -# The Perez-Driesse model is used for this to match the reverse transposition -# later, but the match is not perfect because the diffuse fraction of the data -# does not match the Erbs model. # TILT = 30 @@ -121,9 +117,9 @@ def run_rtranspose(): # %% # -# This graph shows the reverse transposed values vs. the orinal values. -# The markers are color-coded by angle-of-incidence to highlight that -# errors occur mostly at high incidence angles. +# This graph shows the reverse transposed values vs. the original values. +# The markers are color-coded by angle-of-incidence to show that +# errors occur primarily with incidence angle approaching 90° and beyond. # df = df.sort_values('aoi') @@ -137,3 +133,5 @@ def run_rtranspose(): plt.xlabel('GHI original [W/m²]') plt.ylabel('GHI from POA [W/m²]') plt.show() + +# %% From 58e8dedbff9749b1f4d22862c68386746efc32ac Mon Sep 17 00:00:00 2001 From: Anton Driesse Date: Tue, 14 Nov 2023 15:37:39 +0100 Subject: [PATCH 14/26] Try to get rid of matplotlib warning in example. --- .../plot_rtranpose_limitations.py | 2 -- .../irradiance-transposition/plot_rtranpose_year.py | 10 ++++------ 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/docs/examples/irradiance-transposition/plot_rtranpose_limitations.py b/docs/examples/irradiance-transposition/plot_rtranpose_limitations.py index 22c445bdc2..37e6b696aa 100644 --- a/docs/examples/irradiance-transposition/plot_rtranpose_limitations.py +++ b/docs/examples/irradiance-transposition/plot_rtranpose_limitations.py @@ -165,5 +165,3 @@ plt.ylabel('POA [W/m²]') plt.legend() plt.show() - -# %% diff --git a/docs/examples/irradiance-transposition/plot_rtranpose_year.py b/docs/examples/irradiance-transposition/plot_rtranpose_year.py index 1ad6a1758c..8c6cf664f1 100644 --- a/docs/examples/irradiance-transposition/plot_rtranpose_year.py +++ b/docs/examples/irradiance-transposition/plot_rtranpose_year.py @@ -39,8 +39,6 @@ from timeit import timeit -matplotlib.rcParams['axes.grid'] = True - # %% # # Read a TMY3 file containing weather data and select needed columns. @@ -125,13 +123,13 @@ def run_rtranspose(): df = df.sort_values('aoi') plt.figure() +plt.gca().grid(True, alpha=.5) pc = plt.scatter(df['ghi'], df['ghi_rev'], c=df['aoi'], s=15, cmap='jet', vmin=60, vmax=120) -plt.colorbar(label='AOI [°]', ax=plt.gca()) +plt.colorbar(label='AOI [°]') pc.set_alpha(0.5) -plt.grid(alpha=0.5) + plt.xlabel('GHI original [W/m²]') plt.ylabel('GHI from POA [W/m²]') -plt.show() -# %% +plt.show() From 0f35860afc06781e02b793948ebae939b783b6ac Mon Sep 17 00:00:00 2001 From: Anton Driesse Date: Tue, 14 Nov 2023 15:48:07 +0100 Subject: [PATCH 15/26] Remove unused import. --- docs/examples/irradiance-transposition/plot_rtranpose_year.py | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/examples/irradiance-transposition/plot_rtranpose_year.py b/docs/examples/irradiance-transposition/plot_rtranpose_year.py index 8c6cf664f1..e1f2c7d5d3 100644 --- a/docs/examples/irradiance-transposition/plot_rtranpose_year.py +++ b/docs/examples/irradiance-transposition/plot_rtranpose_year.py @@ -26,7 +26,6 @@ import os import pandas as pd -import matplotlib import matplotlib.pyplot as plt import pvlib From 6e7ec7b81eefc2cd2ae62e4470a0a4c4c4080fc8 Mon Sep 17 00:00:00 2001 From: Anton Driesse Date: Mon, 27 Nov 2023 16:30:08 +0100 Subject: [PATCH 16/26] Update pvlib/irradiance.py Co-authored-by: Cliff Hansen --- pvlib/irradiance.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pvlib/irradiance.py b/pvlib/irradiance.py index 6e5ac92428..ab5fca3967 100644 --- a/pvlib/irradiance.py +++ b/pvlib/irradiance.py @@ -1450,7 +1450,7 @@ def _transpose(surface_tilt, surface_azimuth, ghi, dni_extra, airmass, albedo): ''' - Transposition function that includes decomposition if GHI using the + Transposition function that includes decomposition of GHI using the continuous Erbs-Driesse model. Helper function for rtranspose_driesse_2023. From 8106fcbc1067351cd3f68e8a6a8c5146460b13c0 Mon Sep 17 00:00:00 2001 From: Anton Driesse Date: Mon, 27 Nov 2023 17:28:32 +0100 Subject: [PATCH 17/26] Improve examples based on reviews. --- .../plot_rtranpose_limitations.py | 18 +++++++- .../plot_rtranpose_year.py | 43 ++++++++++--------- 2 files changed, 38 insertions(+), 23 deletions(-) diff --git a/docs/examples/irradiance-transposition/plot_rtranpose_limitations.py b/docs/examples/irradiance-transposition/plot_rtranpose_limitations.py index 37e6b696aa..df4cb4d94c 100644 --- a/docs/examples/irradiance-transposition/plot_rtranpose_limitations.py +++ b/docs/examples/irradiance-transposition/plot_rtranpose_limitations.py @@ -1,6 +1,6 @@ """ -Explore the limitations of reverse transposition -================================================ +Limitations of reverse transposition +==================================== Unfortunately, sometimes there is not a unique solution. @@ -12,6 +12,20 @@ # # Introduction # ------------ +# When irradiance is measured on a tilted plane, it is useful to be able to +# estimate the GHI that produces the POA irradiance. +# The estimation requires inverting a GHI-to-POA irradiance model, +# which involves two parts: +# a decomposition of GHI into direct and diffuse components, +# and a transposition model that calculates the direct and diffuse irradiance +# on the tilted plane. +# Recovering GHI from POA irradiance is termed "reverse transposition." +# +# Unfortunately, for a given POA irradiance value, sometimes there is not a +# unique solution for GHI. +# Different GHI values can produce different combinations of direct and +# diffuse irradiance that sum to the same POA irradiance value. +# # In this example we look at a single point in time and consider a full range # of possible GHI and POA global values as shown in figures 3 and 4 of [1]_. # Then we use :py:func:`pvlib.irradiance.rtranspose_driesse_2023` to estimate diff --git a/docs/examples/irradiance-transposition/plot_rtranpose_year.py b/docs/examples/irradiance-transposition/plot_rtranpose_year.py index e1f2c7d5d3..666010d0ee 100644 --- a/docs/examples/irradiance-transposition/plot_rtranpose_year.py +++ b/docs/examples/irradiance-transposition/plot_rtranpose_year.py @@ -7,11 +7,19 @@ Author: Anton Driesse """ - # %% # # Introduction # ------------ +# When irradiance is measured on a tilted plane, it is useful to be able to +# estimate the GHI that produces the POA irradiance. +# The estimation requires inverting a GHI-to-POA irradiance model, +# which involves two parts: +# a decomposition of GHI into direct and diffuse components, +# and a transposition model that calculates the direct and diffuse +# irradiance on the tilted plane. +# Recovering GHI from POA irradiance is termed "reverse transposition." +# # In this example we start with a TMY file and calculate POA global irradiance. # Then, we use :py:func:`pvlib.irradiance.rtranspose_driesse_2023` to estimate # the original GHI from POA global. Details of the method found in [1]_. @@ -24,6 +32,7 @@ # import os +import time import pandas as pd import matplotlib.pyplot as plt @@ -36,8 +45,6 @@ aoi, ) -from timeit import timeit - # %% # # Read a TMY3 file containing weather data and select needed columns. @@ -86,31 +93,25 @@ # %% # -# Now estimate ghi from poa_global using reverse transposition -# The algorithm uses a simple bisection search. +# Now estimate ghi from poa_global using reverse transposition. +# The algorithm uses a simple bisection search, which is quite slow +# because scipy doesn't offer a vectorized version (yet). +# For this reason we'll process a random sample of 1000 timestamps +# rather than the whole year. # +df = df[df.ghi > 0].sample(n=1000) +solpos = solpos.reindex(df.index) + +start = time.process_time() + df['ghi_rev'] = rtranspose_driesse_2023(TILT, ORIENT, solpos.apparent_zenith, solpos.azimuth, df.poa_global, dni_extra=df.dni_extra) +finish = time.process_time() -# %% -# -# Let's see how long this takes... -# - - -def run_rtranspose(): - rtranspose_driesse_2023(TILT, ORIENT, - solpos.apparent_zenith, solpos.azimuth, - df.poa_global, - dni_extra=df.dni_extra) - - -elapsed = timeit('run_rtranspose()', number=1, globals=globals()) - -print('Elapsed time for reverse transposition: %.1f s' % elapsed) +print ('Elapsed time for reverse transposition: %.1f s' % (finish - start)) # %% # From 1c4da536766a9afe1ee69a1743022c845f5446fd Mon Sep 17 00:00:00 2001 From: Anton Driesse Date: Mon, 27 Nov 2023 17:34:05 +0100 Subject: [PATCH 18/26] Settle conflict. --- docs/sphinx/source/whatsnew/v0.10.3.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/sphinx/source/whatsnew/v0.10.3.rst b/docs/sphinx/source/whatsnew/v0.10.3.rst index a82911d0dd..1bf5368203 100644 --- a/docs/sphinx/source/whatsnew/v0.10.3.rst +++ b/docs/sphinx/source/whatsnew/v0.10.3.rst @@ -29,6 +29,7 @@ Documentation ~~~~~~~~~~~~~ * Fixed a plotting issue in the IV curve gallery example (:pull:`1895`) * Added two examples to demonstrate reverse transposition (:pull:`1907`) +* Fixed `detect_clearsky` example in `clearsky.rst` (:issue:`1914`) Contributors ~~~~~~~~~~~~ From 1c16709477e30486bd9605c3279f3bd343db457b Mon Sep 17 00:00:00 2001 From: Anton Driesse Date: Mon, 27 Nov 2023 17:37:59 +0100 Subject: [PATCH 19/26] Try again. --- docs/sphinx/source/whatsnew/v0.10.3.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/sphinx/source/whatsnew/v0.10.3.rst b/docs/sphinx/source/whatsnew/v0.10.3.rst index 1bf5368203..8fd897ab3d 100644 --- a/docs/sphinx/source/whatsnew/v0.10.3.rst +++ b/docs/sphinx/source/whatsnew/v0.10.3.rst @@ -31,6 +31,7 @@ Documentation * Added two examples to demonstrate reverse transposition (:pull:`1907`) * Fixed `detect_clearsky` example in `clearsky.rst` (:issue:`1914`) + Contributors ~~~~~~~~~~~~ * Arjan Keeman (:ghuser:`akeeman`) From 8d7afa97fc6893dfcedb27a750fa6bb1049b732e Mon Sep 17 00:00:00 2001 From: Anton Driesse Date: Mon, 27 Nov 2023 18:23:40 +0100 Subject: [PATCH 20/26] Remove one space. --- docs/examples/irradiance-transposition/plot_rtranpose_year.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/examples/irradiance-transposition/plot_rtranpose_year.py b/docs/examples/irradiance-transposition/plot_rtranpose_year.py index 666010d0ee..131fe8c75c 100644 --- a/docs/examples/irradiance-transposition/plot_rtranpose_year.py +++ b/docs/examples/irradiance-transposition/plot_rtranpose_year.py @@ -111,7 +111,7 @@ dni_extra=df.dni_extra) finish = time.process_time() -print ('Elapsed time for reverse transposition: %.1f s' % (finish - start)) +print('Elapsed time for reverse transposition: %.1f s' % (finish - start)) # %% # From ae3cdb61e1dc1b83daf5fda6a3304187136adb52 Mon Sep 17 00:00:00 2001 From: Anton Driesse Date: Sat, 2 Dec 2023 22:31:24 +0100 Subject: [PATCH 21/26] Final(?) changes. --- .../plot_rtranpose_limitations.py | 32 ++++----- .../plot_rtranpose_year.py | 22 ++++--- pvlib/irradiance.py | 66 +++++++++---------- pvlib/tests/test_irradiance.py | 8 +-- 4 files changed, 65 insertions(+), 63 deletions(-) diff --git a/docs/examples/irradiance-transposition/plot_rtranpose_limitations.py b/docs/examples/irradiance-transposition/plot_rtranpose_limitations.py index df4cb4d94c..6e6b3e4168 100644 --- a/docs/examples/irradiance-transposition/plot_rtranpose_limitations.py +++ b/docs/examples/irradiance-transposition/plot_rtranpose_limitations.py @@ -28,14 +28,14 @@ # # In this example we look at a single point in time and consider a full range # of possible GHI and POA global values as shown in figures 3 and 4 of [1]_. -# Then we use :py:func:`pvlib.irradiance.rtranspose_driesse_2023` to estimate +# Then we use :py:func:`pvlib.irradiance.ghi_from_poa_driesse_2023` to estimate # the original GHI from POA global. # # References # ---------- -# .. [1] A. Driesse, A. Jensen, R. Perez, A Continuous Form of the Perez -# Diffuse Sky Model for Forward and Reverse Transposition, accepted -# for publication in the Solar Energy Journal. +# .. [1] Driesse, A., Jensen, A., Perez, R., 2024. A Continuous form of the +# Perez diffuse sky model for forward and reverse transposition. +# Solar Energy vol. 267. :doi:`10.1016/j.solener.2023.112093` # import numpy as np @@ -45,7 +45,7 @@ from pvlib.irradiance import (erbs_driesse, get_total_irradiance, - rtranspose_driesse_2023, + ghi_from_poa_driesse_2023, ) matplotlib.rcParams['axes.grid'] = True @@ -92,11 +92,11 @@ poa_test = 200 -ghi_hat = rtranspose_driesse_2023(surface_tilt, surface_azimuth, - solar_zenith, solar_azimuth, - poa_test, - dni_extra, - full_output=False) +ghi_hat = ghi_from_poa_driesse_2023(surface_tilt, surface_azimuth, + solar_zenith, solar_azimuth, + poa_test, + dni_extra, + full_output=False) print('Estimated GHI: %.2f W/m².' % ghi_hat) @@ -156,12 +156,12 @@ # out, other times not. # -result = rtranspose_driesse_2023(surface_tilt, surface_azimuth, - solar_zenith, solar_azimuth, - poa_global, - dni_extra, - full_output=True, - ) +result = ghi_from_poa_driesse_2023(surface_tilt, surface_azimuth, + solar_zenith, solar_azimuth, + poa_global, + dni_extra, + full_output=True, + ) ghi_hat, conv, niter = result correct = np.isclose(ghi, ghi_hat, atol=0.01) diff --git a/docs/examples/irradiance-transposition/plot_rtranpose_year.py b/docs/examples/irradiance-transposition/plot_rtranpose_year.py index 131fe8c75c..1920d91adf 100644 --- a/docs/examples/irradiance-transposition/plot_rtranpose_year.py +++ b/docs/examples/irradiance-transposition/plot_rtranpose_year.py @@ -21,14 +21,14 @@ # Recovering GHI from POA irradiance is termed "reverse transposition." # # In this example we start with a TMY file and calculate POA global irradiance. -# Then, we use :py:func:`pvlib.irradiance.rtranspose_driesse_2023` to estimate +# Then we use :py:func:`pvlib.irradiance.ghi_from_poa_driesse_2023` to estimate # the original GHI from POA global. Details of the method found in [1]_. # # References # ---------- -# .. [1] A. Driesse, A. Jensen, R. Perez, A Continuous Form of the Perez -# Diffuse Sky Model for Forward and Reverse Transposition, accepted -# for publication in the Solar Energy Journal. +# .. [1] Driesse, A., Jensen, A., Perez, R., 2024. A Continuous form of the +# Perez diffuse sky model for forward and reverse transposition. +# Solar Energy vol. 267. :doi:`10.1016/j.solener.2023.112093` # import os @@ -41,7 +41,7 @@ from pvlib import iotools, location from pvlib.irradiance import (get_extra_radiation, get_total_irradiance, - rtranspose_driesse_2023, + ghi_from_poa_driesse_2023, aoi, ) @@ -83,7 +83,8 @@ df['dni_extra'] = get_extra_radiation(df.index) total_irrad = get_total_irradiance(TILT, ORIENT, - solpos.apparent_zenith, solpos.azimuth, + solpos.apparent_zenith, + solpos.azimuth, df.dni, df.ghi, df.dhi, dni_extra=df.dni_extra, model='perez-driesse') @@ -105,10 +106,11 @@ start = time.process_time() -df['ghi_rev'] = rtranspose_driesse_2023(TILT, ORIENT, - solpos.apparent_zenith, solpos.azimuth, - df.poa_global, - dni_extra=df.dni_extra) +df['ghi_rev'] = ghi_from_poa_driesse_2023(TILT, ORIENT, + solpos.apparent_zenith, + solpos.azimuth, + df.poa_global, + dni_extra=df.dni_extra) finish = time.process_time() print('Elapsed time for reverse transposition: %.1f s' % (finish - start)) diff --git a/pvlib/irradiance.py b/pvlib/irradiance.py index ab5fca3967..39e7514dd8 100644 --- a/pvlib/irradiance.py +++ b/pvlib/irradiance.py @@ -1381,9 +1381,9 @@ def perez_driesse(surface_tilt, surface_azimuth, dhi, dni, dni_extra, References ---------- - .. [1] A. Driesse, A. Jensen, R. Perez, A Continuous Form of the Perez - Diffuse Sky Model for Forward and Reverse Transposition, accepted - for publication in the Solar Energy Journal. + .. [1] Driesse, A., Jensen, A., Perez, R., 2024. A Continuous form of the + Perez diffuse sky model for forward and reverse transposition. + Solar Energy vol. 267. :doi:`10.1016/j.solener.2023.112093` .. [2] Perez, R., Ineichen, P., Seals, R., Michalsky, J., Stewart, R., 1990. Modeling daylight availability and irradiance components from @@ -1445,15 +1445,15 @@ def perez_driesse(surface_tilt, surface_azimuth, dhi, dni, dni_extra, return sky_diffuse -def _transpose(surface_tilt, surface_azimuth, - solar_zenith, solar_azimuth, - ghi, - dni_extra, airmass, albedo): +def _poa_from_ghi(surface_tilt, surface_azimuth, + solar_zenith, solar_azimuth, + ghi, + dni_extra, airmass, albedo): ''' Transposition function that includes decomposition of GHI using the continuous Erbs-Driesse model. - Helper function for rtranspose_driesse_2023. + Helper function for ghi_from_poa_driesse_2023. ''' # Contributed by Anton Driesse (@adriesse), PV Performance Labs. Nov., 2023 @@ -1471,15 +1471,15 @@ def _transpose(surface_tilt, surface_azimuth, return irrads['poa_global'] -def _rtranspose(surface_tilt, surface_azimuth, - solar_zenith, solar_azimuth, - poa_global, - dni_extra, airmass, albedo, - xtol=0.01): +def _ghi_from_poa(surface_tilt, surface_azimuth, + solar_zenith, solar_azimuth, + poa_global, + dni_extra, airmass, albedo, + xtol=0.01): ''' Reverse transposition function that uses the scalar bisection from scipy. - Helper function for rtranspose_driesse_2023. + Helper function for ghi_from_poa_driesse_2023. ''' # Contributed by Anton Driesse (@adriesse), PV Performance Labs. Nov., 2023 @@ -1491,10 +1491,10 @@ def _rtranspose(surface_tilt, surface_azimuth, # function whose root needs to be found def poa_error(ghi): - poa_hat = _transpose(surface_tilt, surface_azimuth, - solar_zenith, solar_azimuth, - ghi, - dni_extra, airmass, albedo) + poa_hat = _poa_from_ghi(surface_tilt, surface_azimuth, + solar_zenith, solar_azimuth, + ghi, + dni_extra, airmass, albedo) return poa_hat - poa_global # calculate an upper bound for ghi using clearness index 1.25 @@ -1523,12 +1523,12 @@ def poa_error(ghi): return ghi, conv, niter -def rtranspose_driesse_2023(surface_tilt, surface_azimuth, - solar_zenith, solar_azimuth, - poa_global, - dni_extra=None, airmass=None, albedo=0.25, - xtol=0.01, - full_output=False): +def ghi_from_poa_driesse_2023(surface_tilt, surface_azimuth, + solar_zenith, solar_azimuth, + poa_global, + dni_extra=None, airmass=None, albedo=0.25, + xtol=0.01, + full_output=False): ''' Estimate global horizontal irradiance (GHI) from global plane-of-array (POA) irradiance. This reverse transposition algorithm uses a bisection @@ -1578,9 +1578,9 @@ def rtranspose_driesse_2023(surface_tilt, surface_azimuth, References ---------- - .. [1] A. Driesse, A. Jensen, R. Perez, A Continuous Form of the Perez - Diffuse Sky Model for Forward and Reverse Transposition, accepted - for publication in the Solar Energy Journal. + .. [1] Driesse, A., Jensen, A., Perez, R., 2024. A Continuous form of the + Perez diffuse sky model for forward and reverse transposition. + Solar Energy vol. 267. :doi:`10.1016/j.solener.2023.112093` See also -------- @@ -1590,13 +1590,13 @@ def rtranspose_driesse_2023(surface_tilt, surface_azimuth, ''' # Contributed by Anton Driesse (@adriesse), PV Performance Labs. Nov., 2023 - rtranspose_array = np.vectorize(_rtranspose) + ghi_from_poa_array = np.vectorize(_ghi_from_poa) - ghi, conv, niter = rtranspose_array(surface_tilt, surface_azimuth, - solar_zenith, solar_azimuth, - poa_global, - dni_extra, airmass, albedo, - xtol=0.01) + ghi, conv, niter = ghi_from_poa_array(surface_tilt, surface_azimuth, + solar_zenith, solar_azimuth, + poa_global, + dni_extra, airmass, albedo, + xtol=0.01) if isinstance(poa_global, pd.Series): ghi = pd.Series(ghi, poa_global.index) diff --git a/pvlib/tests/test_irradiance.py b/pvlib/tests/test_irradiance.py index 72a8e374e1..55fef490ba 100644 --- a/pvlib/tests/test_irradiance.py +++ b/pvlib/tests/test_irradiance.py @@ -782,7 +782,7 @@ def test_dirint_min_cos_zenith_max_zenith(): assert_series_equal(out, expected, check_less_precise=True) -def test_rtranspose_driesse(): +def test_ghi_from_poa_driesse(): # inputs copied from test_gti_dirint times = pd.DatetimeIndex( ['2014-06-24T06-0700', '2014-06-24T09-0700', '2014-06-24T12-0700']) @@ -793,7 +793,7 @@ def test_rtranspose_driesse(): surface_azimuth = 180 # test core function - output = irradiance.rtranspose_driesse_2023( + output = irradiance.ghi_from_poa_driesse_2023( surface_tilt, surface_azimuth, zenith, azimuth, poa_global, dni_extra=1366.1) @@ -803,7 +803,7 @@ def test_rtranspose_driesse(): # test series output poa_global = pd.Series([20, 300, 1000], index=times) - output = irradiance.rtranspose_driesse_2023( + output = irradiance.ghi_from_poa_driesse_2023( surface_tilt, surface_azimuth, zenith, azimuth, poa_global, dni_extra=1366.1) @@ -812,7 +812,7 @@ def test_rtranspose_driesse(): # test full_output option and special cases poa_global = np.array([0, 1500, np.nan]) - ghi, conv, niter = irradiance.rtranspose_driesse_2023( + ghi, conv, niter = irradiance.ghi_from_poa_driesse_2023( surface_tilt, surface_azimuth, zenith, azimuth, poa_global, dni_extra=1366.1, full_output=True) From d7fb73dba8616a62c3909d1899e4b380db83791d Mon Sep 17 00:00:00 2001 From: Anton Driesse Date: Sat, 2 Dec 2023 22:37:33 +0100 Subject: [PATCH 22/26] Update reference in erbs_driesse(). --- pvlib/irradiance.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pvlib/irradiance.py b/pvlib/irradiance.py index 39e7514dd8..f5e9a44b7a 100644 --- a/pvlib/irradiance.py +++ b/pvlib/irradiance.py @@ -2732,8 +2732,9 @@ def erbs_driesse(ghi, zenith, datetime_or_doy=None, dni_extra=None, References ---------- - .. [1] A. Driesse, A. Jensen, R. Perez, A Continuous Form of the Perez - Diffuse Sky Model for Forward and Reverse Transposition, forthcoming. + .. [1] Driesse, A., Jensen, A., Perez, R., 2024. A Continuous form of the + Perez diffuse sky model for forward and reverse transposition. + Solar Energy vol. 267. :doi:`10.1016/j.solener.2023.112093` .. [2] D. G. Erbs, S. A. Klein and J. A. Duffie, Estimation of the diffuse radiation fraction for hourly, daily and monthly-average From 8021769eafe105a14341e8e1cea8c9611da2f16d Mon Sep 17 00:00:00 2001 From: Anton Driesse Date: Sat, 2 Dec 2023 23:05:22 +0100 Subject: [PATCH 23/26] Fix links in examples. --- .../irradiance-transposition/plot_rtranpose_limitations.py | 2 +- docs/examples/irradiance-transposition/plot_rtranpose_year.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/examples/irradiance-transposition/plot_rtranpose_limitations.py b/docs/examples/irradiance-transposition/plot_rtranpose_limitations.py index 6e6b3e4168..d64116acca 100644 --- a/docs/examples/irradiance-transposition/plot_rtranpose_limitations.py +++ b/docs/examples/irradiance-transposition/plot_rtranpose_limitations.py @@ -28,7 +28,7 @@ # # In this example we look at a single point in time and consider a full range # of possible GHI and POA global values as shown in figures 3 and 4 of [1]_. -# Then we use :py:func:`pvlib.irradiance.ghi_from_poa_driesse_2023` to estimate +# Then we use :py:meth:`pvlib.irradiance.ghi_from_poa_driesse_2023` to estimate # the original GHI from POA global. # # References diff --git a/docs/examples/irradiance-transposition/plot_rtranpose_year.py b/docs/examples/irradiance-transposition/plot_rtranpose_year.py index 1920d91adf..89849d84e2 100644 --- a/docs/examples/irradiance-transposition/plot_rtranpose_year.py +++ b/docs/examples/irradiance-transposition/plot_rtranpose_year.py @@ -21,7 +21,7 @@ # Recovering GHI from POA irradiance is termed "reverse transposition." # # In this example we start with a TMY file and calculate POA global irradiance. -# Then we use :py:func:`pvlib.irradiance.ghi_from_poa_driesse_2023` to estimate +# Then we use :py:meth:`pvlib.irradiance.ghi_from_poa_driesse_2023` to estimate # the original GHI from POA global. Details of the method found in [1]_. # # References From cbf971bbe285ef8d9218a29d52b759994c015d0d Mon Sep 17 00:00:00 2001 From: Anton Driesse Date: Thu, 7 Dec 2023 22:13:06 +0100 Subject: [PATCH 24/26] Update docs/examples/irradiance-transposition/plot_rtranpose_limitations.py Co-authored-by: Adam R. Jensen <39184289+AdamRJensen@users.noreply.github.com> --- .../irradiance-transposition/plot_rtranpose_limitations.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/examples/irradiance-transposition/plot_rtranpose_limitations.py b/docs/examples/irradiance-transposition/plot_rtranpose_limitations.py index d64116acca..8df8339ad4 100644 --- a/docs/examples/irradiance-transposition/plot_rtranpose_limitations.py +++ b/docs/examples/irradiance-transposition/plot_rtranpose_limitations.py @@ -1,5 +1,5 @@ """ -Limitations of reverse transposition +Reverse transposition limitations ==================================== Unfortunately, sometimes there is not a unique solution. From afcf178ce6c22ecc14e5b3862b594fd7b95c5439 Mon Sep 17 00:00:00 2001 From: Anton Driesse Date: Thu, 7 Dec 2023 23:11:09 +0100 Subject: [PATCH 25/26] Address review comments. --- .../plot_rtranpose_year.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/docs/examples/irradiance-transposition/plot_rtranpose_year.py b/docs/examples/irradiance-transposition/plot_rtranpose_year.py index 89849d84e2..c0b9860bba 100644 --- a/docs/examples/irradiance-transposition/plot_rtranpose_year.py +++ b/docs/examples/irradiance-transposition/plot_rtranpose_year.py @@ -24,12 +24,20 @@ # Then we use :py:meth:`pvlib.irradiance.ghi_from_poa_driesse_2023` to estimate # the original GHI from POA global. Details of the method found in [1]_. # +# Another method for reverse tranposition called GTI-DIRINT is also +# available in pvlib python (:py:meth:`pvlib.irradiance.gti_dirint`). +# More information is available in [2]_. +# # References # ---------- # .. [1] Driesse, A., Jensen, A., Perez, R., 2024. A Continuous form of the # Perez diffuse sky model for forward and reverse transposition. # Solar Energy vol. 267. :doi:`10.1016/j.solener.2023.112093` # +# .. [2] B. Marion, A model for deriving the direct normal and +# diffuse horizontal irradiance from the global tilted +# irradiance, Solar Energy 122, 1037-1046. +# :doi:`10.1016/j.solener.2015.10.024` import os import time @@ -121,6 +129,13 @@ # The markers are color-coded by angle-of-incidence to show that # errors occur primarily with incidence angle approaching 90° and beyond. # +# Note that the results look particularly good because the POA values +# were calculated using the same models as used in reverse transposition. +# This isn't cheating though. It's a way of ensuring that the errors +# we see are really due to the reverse transposition algorithm. +# Expect to see larger errors with real-word POA measurements +# because errors from forward and reverse transposition will both be present. +# df = df.sort_values('aoi') From 71db4c4717f121f6d126a881c34817451c42dad8 Mon Sep 17 00:00:00 2001 From: Anton Driesse Date: Tue, 12 Dec 2023 13:45:00 +0100 Subject: [PATCH 26/26] Final renames. --- docs/sphinx/source/reference/irradiance/transposition.rst | 2 +- docs/sphinx/source/whatsnew/v0.10.3.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/sphinx/source/reference/irradiance/transposition.rst b/docs/sphinx/source/reference/irradiance/transposition.rst index b7e3295c5f..22136f0c58 100644 --- a/docs/sphinx/source/reference/irradiance/transposition.rst +++ b/docs/sphinx/source/reference/irradiance/transposition.rst @@ -15,4 +15,4 @@ Transposition models irradiance.klucher irradiance.reindl irradiance.king - irradiance.rtranspose_driesse_2023 + irradiance.ghi_from_poa_driesse_2023 diff --git a/docs/sphinx/source/whatsnew/v0.10.3.rst b/docs/sphinx/source/whatsnew/v0.10.3.rst index d9e469fcba..63f7b90b84 100644 --- a/docs/sphinx/source/whatsnew/v0.10.3.rst +++ b/docs/sphinx/source/whatsnew/v0.10.3.rst @@ -10,7 +10,7 @@ Enhancements * Added the continuous Perez-Driesse transposition model. :py:func:`pvlib.irradiance.perez_driesse` (:issue:`1841`, :pull:`1876`) * Added a reverse transposition algorithm using the Perez-Driesse model. - :py:func:`pvlib.irradiance.rtranspose_driesse_2023` + :py:func:`pvlib.irradiance.ghi_from_poa_driesse_2023` (:issue:`1901`, :pull:`1907`) * :py:func:`pvlib.bifacial.infinite_sheds.get_irradiance` and :py:func:`pvlib.bifacial.infinite_sheds.get_irradiance_poa` now include