diff --git a/docs/examples/shading/plot_martinez_shade_loss.py b/docs/examples/shading/plot_martinez_shade_loss.py index 625be33bc8..10ce77f741 100644 --- a/docs/examples/shading/plot_martinez_shade_loss.py +++ b/docs/examples/shading/plot_martinez_shade_loss.py @@ -47,7 +47,7 @@ import pandas as pd import numpy as np import matplotlib.pyplot as plt -from matplotlib.dates import ConciseDateFormatter +from matplotlib.dates import DateFormatter pitch = 4 # meters width = 1.5 # meters @@ -235,10 +235,6 @@ ax1.plot(times, shade_losses, label=k, linestyle=linestyle) ax1.legend(loc="upper center") ax1.grid() -ax1.set_xlabel("Time") -ax1.xaxis.set_major_formatter( - ConciseDateFormatter("%H:%M", tz="Europe/Madrid") -) ax1.set_ylabel(r"$P_{out}$ losses") ax1.set_title("Per module") @@ -248,9 +244,7 @@ ax2.legend(loc="upper center") ax2.grid() ax2.set_xlabel("Time") -ax2.xaxis.set_major_formatter( - ConciseDateFormatter("%H:%M", tz="Europe/Madrid") -) +ax2.xaxis.set_major_formatter(DateFormatter("%H:%M", tz="Europe/Madrid")) ax2.set_ylabel(r"$P_{out}$ losses") ax2.set_title("Per row") fig.tight_layout() diff --git a/docs/sphinx/source/conf.py b/docs/sphinx/source/conf.py index 157e77074a..a08507c0b3 100644 --- a/docs/sphinx/source/conf.py +++ b/docs/sphinx/source/conf.py @@ -73,7 +73,9 @@ # Enable hover tooltips hoverxref_auto_ref = True -hoverxref_roles = ["class", "meth", "func", "ref", "term"] +hoverxref_roles = [ + "class", "meth", "func", "ref", "term", "obj", "mod", "data" +] hoverxref_role_types = dict.fromkeys(hoverxref_roles, "tooltip") hoverxref_domains = ["py"] hoverxref_intersphinx = list(intersphinx_mapping.keys()) diff --git a/docs/sphinx/source/reference/airmass_atmospheric.rst b/docs/sphinx/source/reference/airmass_atmospheric.rst index 384c345aec..2ece5cd5fb 100644 --- a/docs/sphinx/source/reference/airmass_atmospheric.rst +++ b/docs/sphinx/source/reference/airmass_atmospheric.rst @@ -11,6 +11,8 @@ Airmass and atmospheric models atmosphere.get_relative_airmass atmosphere.pres2alt atmosphere.alt2pres + atmosphere.tdew_from_rh + atmosphere.rh_from_tdew atmosphere.gueymard94_pw atmosphere.first_solar_spectral_correction atmosphere.bird_hulstrom80_aod_bb diff --git a/docs/sphinx/source/whatsnew.rst b/docs/sphinx/source/whatsnew.rst index 68b1a81dce..34ade38b4e 100644 --- a/docs/sphinx/source/whatsnew.rst +++ b/docs/sphinx/source/whatsnew.rst @@ -6,6 +6,7 @@ What's New These are new features and improvements of note in each release. +.. include:: whatsnew/v0.11.3.rst .. include:: whatsnew/v0.11.2.rst .. include:: whatsnew/v0.11.1.rst .. include:: whatsnew/v0.11.0.rst diff --git a/docs/sphinx/source/whatsnew/v0.11.2.rst b/docs/sphinx/source/whatsnew/v0.11.2.rst index beb5bafaff..8010b9389d 100644 --- a/docs/sphinx/source/whatsnew/v0.11.2.rst +++ b/docs/sphinx/source/whatsnew/v0.11.2.rst @@ -1,18 +1,19 @@ .. _whatsnew_01120: -v0.11.2 (Anticipated December, 2024) ------------------------------------- +v0.11.2 (December 16, 2024) +--------------------------- Deprecations ~~~~~~~~~~~~ -* Deprecated terms ``dni_clearsky`` and ``clearsky_dni``, replaced with ``dni_clear``. - Affected functions are :py:func:`~pvlib.irradiance.dirindex` and :py:func:`~pvlib.irradiance.dni`. - (:issue:`2272`, :pull:`2274`) - +* Deprecate terms ``dni_clearsky`` and ``clearsky_dni``, replace with ``dni_clear`` and ``ghi_clear``. + Affected functions are :py:func:`~pvlib.irradiance.dirindex`, :py:func:`~pvlib.irradiance.dni` + and :py:func:`~pvlib.irradiance.clearsky_index`. Enhancements ~~~~~~~~~~~~ +* Add :py:func:`~pvlib.atmosphere.rh_from_tdew` and :py:func:`~pvlib.atmosphere.tdew_from_rh`. + (:issue:`1744`, :pull:`2286`) * :py:func:`~pvlib.ivtools.sdm.fit_desoto` now allows input of initial parameter guesses. (:issue:`1014`, :pull:`2291`) @@ -28,49 +29,51 @@ Bug Fixes Bug fixes ~~~~~~~~~ +* Change ``dni_extra`` to a required parameter in :py:func:`pvlib.irradiance.ghi_from_poa_driesse_2023` + (:issue:`2279` :pull:`2331`) * :py:func:`~pvlib.spa.julian_day_dt` now accounts for the 10 day difference between Julian and Gregorian calendars prior to the year 1582. (:issue:`2077`, :pull:`2249`) +* Correct sign of temperature coefficient ``dEgdT`` in :py:func:`~pvlib.ivtools.sdm.fit_desoto_sandia`. + Results may differ slightly from previous versions. (:issue:`2311`, :pull:`2322`) Documentation ~~~~~~~~~~~~~ -* Edited docstrings for :py:func:`~pvlib.pvsystem.dc_ohms_from_percent` and +* Edit docstrings for :py:func:`~pvlib.pvsystem.dc_ohms_from_percent` and :py:func:`~pvlib.pvsystem.dc_ohmic_losses` for clarity. (:issue:`1601`, :pull:`2229`) -* Added 'freestanding' and 'insulated' `racking_model` options for cell - temperature calculation in :py:class:`~pvlib.pvsystem.PVSystem` +* Add 'freestanding' and 'insulated' `racking_model` options for cell + temperature calculation in :py:class:`~pvlib.pvsystem.PVSystem`, :py:class:`~pvlib.pvsystem.SingleAxisTrackerMount`, and :py:class:`~pvlib.pvsystem.FixedMount` docstrings. Various formatting edits for clarity. (:issue:`1942`, :pull:`2232`) -* Added a new citation style guide (:ref:`reference_style`) to the contributing - page. (:issue:`2202`, :pull:`2226`) -* Updated :py:func:`~pvlib.irradiance.reindl` to include definitions of terms +* Update :py:func:`~pvlib.irradiance.reindl` to include definitions of terms and a new "notes" section (:issue:`2183`, :pull:`2193`) -* Clarified the error message in :py:func:`~pvlib.clearsky.detect_clearsky` when +* Clarify the error message in :py:func:`~pvlib.clearsky.detect_clearsky` when windows contain fewer than three data points (:issue:`2005`, :pull:`2281`) -* Added a new :ref:`nomenclature` page, in place of the Variables and Symbols +* Clarify mounting cases for parameters for :py:func:`~pvlib.temperature.sapm_module`, + :py:func:`~pvlib.temperature.sapm_cell` and :py:func:`~pvlib.temperature.pvsyst_cell`. + (:issue:`1323`, :pull:`2293`) +* Add an example to :py:func:`~pvlib.pvsystem.retrieve_sam` docstring to + demonstrate how to retrieve a database from the SAM repo. (:pull:`2313`) +* Add a new citation style guide (:ref:`reference_style`) to the contributing + page. (:issue:`2202`, :pull:`2226`) +* Explain how to write docstrings for new functions in :ref:`example-docstring` + (:discuss:`2081`, :pull:`2254`) +* Add a section in the style guide for parameter naming and units best practices. + See :ref:`documentation-units`. (:issue:`2205`, :pull:`2248`) +* Add a new :ref:`nomenclature` page, in place of the Variables and Symbols page, using the sphinx glossary directive. (:issue:`1421`, :pull:`2234`) -* Explained how to write docstrings for new functions in :ref:`example-docstring` - (:discussion:`2081`, :pull:`2254`) -* Added the following variables to the :ref:`nomenclature` page: +* Add the following variables to the :ref:`nomenclature` page: - `spectra` and `spectra_components` (:issue:`2150`, :pull:`2264`) -* Added a section in the style guide for parameter naming and units best practices. - See :ref:`documentation-units`. (:issue:`2205`, :pull:`2248`) -* Added a example to :py:func:`~pvlib.pvsystem.retrieve_sam` docstring to - demonstrate how to retrieve a database from the SAM repo. (:pull:`2313`) Testing ~~~~~~~ -* Updated test files to track new PVGIS 5.3 data. (:pull:`2305`) - - -Requirements -~~~~~~~~~~~~ - +* Update test files to track new PVGIS 5.3 data. (:pull:`2305`) Maintenance ~~~~~~~~~~~ -* Added a decorator to deprecate renamed keyword arguments in functions, +* Add a decorator to deprecate renamed keyword arguments in functions, :py:func:`pvlib._deprecation.renamed_kwarg_warning`. (:pull:`2237`) @@ -81,7 +84,22 @@ Contributors * Dave Pitts (:ghuser:`dgapitts`) * Kurt Rhee (:ghuser:`kurt-rhee`) * Mark Mikofski (:ghuser:`mikofski`) -* matsuobasho (:ghuser:`matsuobasho`) +* Roma Koulikov (:ghuser:`matsuobasho`) * Echedey Luis (:ghuser:`echedey-ls`) * Kevin Anderson (:ghuser:`kandersolar`) * Scott Nelson (:ghuser:`scttnlsn`) +* Ioannis Sifnaios (:ghuser:`IoannisSifnaios`) +* Adam R. Jensen (:ghuser:`AdamRJensen`) +* Anton Driesse (:ghuser:`adriesse`) +* Will Holmgren (:ghuser:`wholmgren`) +* Umay Akkoseoglu (:ghuser:`uakkoseo`) +* Mark Campanelli (:ghuser:`markcampanelli`) +* :ghuser:`iblasi` +* Hiromasa Ihara (:ghuser:`miettal`) +* Yunho Kee (:ghuser:`yhkee0404`) +* Hamilton Kibbe (:ghuser:`hamiltonkibbe`) +* Felix Korbelius (:ghuser:`FelixKoTU`) +* Michael Deceglie (:ghuser:`mdeceglie`) +* Will Hobbs (:ghuser:`williamhobbs`) +* Todd Karin (:ghuser:`toddkarin`) +* Lucas Schneeberger (:ghuser:`lucasschn`) diff --git a/docs/sphinx/source/whatsnew/v0.11.3.rst b/docs/sphinx/source/whatsnew/v0.11.3.rst new file mode 100644 index 0000000000..238ec1e9a6 --- /dev/null +++ b/docs/sphinx/source/whatsnew/v0.11.3.rst @@ -0,0 +1,30 @@ +.. _whatsnew_01130: + + +v0.11.3 (Anticipated March, 2025) +--------------------------------- + +Deprecations +~~~~~~~~~~~~ + + +Enhancements +~~~~~~~~~~~~ + + +Documentation +~~~~~~~~~~~~~ + + +Testing +~~~~~~~ + + +Requirements +~~~~~~~~~~~~ + + +Contributors +~~~~~~~~~~~~ + + diff --git a/pvlib/atmosphere.py b/pvlib/atmosphere.py index 5b7ffff4ee..4603fb7e11 100644 --- a/pvlib/atmosphere.py +++ b/pvlib/atmosphere.py @@ -337,6 +337,85 @@ def gueymard94_pw(temp_air, relative_humidity): return pw +def rh_from_tdew(temp_air, temp_dew, coeff=(6.112, 17.62, 243.12)): + """ + Calculate relative humidity from dewpoint temperature using the Magnus + equation. + + Parameters + ---------- + temp_air : numeric + Air temperature (dry-bulb temperature). [°C] + temp_dew : numeric + Dew-point temperature. [°C] + coeff : tuple, default (6.112, 17.62, 243.12) + Magnus equation coefficients (A, B, C). The default values are those + recommended by the WMO [1]_. + + Returns + ------- + numeric + Relative humidity (0.0-100.0). [%] + + References + ---------- + .. [1] "Guide to Instruments and Methods of Observation", + World Meteorological Organization, WMO-No. 8, 2023. + https://library.wmo.int/idurl/4/68695 + """ + + # Calculate vapor pressure (e) and saturation vapor pressure (es) + e = coeff[0] * np.exp((coeff[1] * temp_air) / (coeff[2] + temp_air)) + es = coeff[0] * np.exp((coeff[1] * temp_dew) / (coeff[2] + temp_dew)) + + # Calculate relative humidity as percentage + relative_humidity = 100 * (es / e) + + return relative_humidity + + +def tdew_from_rh(temp_air, relative_humidity, coeff=(6.112, 17.62, 243.12)): + """ + Calculate dewpoint temperature using the Magnus equation. + This is a reversal of the calculation in :py:func:`rh_from_tdew`. + + Parameters + ---------- + temp_air : numeric + Air temperature (dry-bulb temperature). [°C] + relative_humidity : numeric + Relative humidity (0-100). [%] + coeff: tuple, default (6.112, 17.62, 243.12) + Magnus equation coefficients (A, B, C). The default values are those + recommended by the WMO [1]_. + + Returns + ------- + numeric + Dewpoint temperature. [°C] + + References + ---------- + .. [1] "Guide to Instruments and Methods of Observation", + World Meteorological Organization, WMO-No. 8, 2023. + https://library.wmo.int/idurl/4/68695 + """ + # Calculate the term inside the log + # From RH = 100 * (es/e), we get es = (RH/100) * e + # Substituting the Magnus equation and solving for dewpoint + + # First calculate ln(es/A) + ln_term = ( + (coeff[1] * temp_air) / (coeff[2] + temp_air) + + np.log(relative_humidity/100) + ) + + # Then solve for dewpoint + dewpoint = coeff[2] * ln_term / (coeff[1] - ln_term) + + return dewpoint + + first_solar_spectral_correction = deprecated( since='0.10.0', alternative='pvlib.spectrum.spectral_factor_firstsolar' diff --git a/pvlib/clearsky.py b/pvlib/clearsky.py index eb97691ba3..be75ecd47a 100644 --- a/pvlib/clearsky.py +++ b/pvlib/clearsky.py @@ -327,13 +327,13 @@ def haurwitz(apparent_zenith): ''' cos_zenith = tools.cosd(apparent_zenith.values) - clearsky_ghi = np.zeros_like(apparent_zenith.values) + ghi_clear = np.zeros_like(apparent_zenith.values) cos_zen_gte_0 = cos_zenith > 0 - clearsky_ghi[cos_zen_gte_0] = (1098.0 * cos_zenith[cos_zen_gte_0] * - np.exp(-0.059/cos_zenith[cos_zen_gte_0])) + ghi_clear[cos_zen_gte_0] = (1098.0 * cos_zenith[cos_zen_gte_0] * + np.exp(-0.059/cos_zenith[cos_zen_gte_0])) df_out = pd.DataFrame(index=apparent_zenith.index, - data=clearsky_ghi, + data=ghi_clear, columns=['ghi']) return df_out diff --git a/pvlib/irradiance.py b/pvlib/irradiance.py index 1c735eb454..7fbb1ea985 100644 --- a/pvlib/irradiance.py +++ b/pvlib/irradiance.py @@ -1528,7 +1528,7 @@ def poa_error(ghi): def ghi_from_poa_driesse_2023(surface_tilt, surface_azimuth, solar_zenith, solar_azimuth, poa_global, - dni_extra=None, airmass=None, albedo=0.25, + dni_extra, airmass=None, albedo=0.25, xtol=0.01, full_output=False): ''' @@ -1549,7 +1549,7 @@ def ghi_from_poa_driesse_2023(surface_tilt, surface_azimuth, Solar azimuth angle. [degree] poa_global : numeric Plane-of-array global irradiance, aka global tilted irradiance. [Wm⁻²] - dni_extra : numeric, optional + dni_extra : numeric Extraterrestrial direct normal irradiance. [Wm⁻²] airmass : numeric, optional Relative airmass (not adjusted for pressure). [unitless] @@ -1614,7 +1614,12 @@ def ghi_from_poa_driesse_2023(surface_tilt, surface_azimuth, return ghi -def clearsky_index(ghi, clearsky_ghi, max_clearsky_index=2.0): +@renamed_kwarg_warning( + since='0.11.2', + old_param_name='clearsky_ghi', + new_param_name='ghi_clear', + removal="0.13.0") +def clearsky_index(ghi, ghi_clear, max_clearsky_index=2.0): """ Calculate the clearsky index. @@ -1626,9 +1631,12 @@ def clearsky_index(ghi, clearsky_ghi, max_clearsky_index=2.0): ghi : numeric Global horizontal irradiance. [Wm⁻²] - clearsky_ghi : numeric + ghi_clear : numeric Modeled clearsky GHI + .. versionchanged:: 0.11.2 + Renamed from ``ghi_clearsky`` to ``ghi_clear``. + max_clearsky_index : numeric, default 2.0 Maximum value of the clearsky index. The default, 2.0, allows for over-irradiance events typically seen in sub-hourly data. @@ -1638,12 +1646,12 @@ def clearsky_index(ghi, clearsky_ghi, max_clearsky_index=2.0): clearsky_index : numeric Clearsky index """ - clearsky_index = ghi / clearsky_ghi + clearsky_index = ghi / ghi_clear # set +inf, -inf, and nans to zero clearsky_index = np.where(~np.isfinite(clearsky_index), 0, clearsky_index) # but preserve nans in the input arrays - input_is_nan = ~np.isfinite(ghi) | ~np.isfinite(clearsky_ghi) + input_is_nan = ~np.isfinite(ghi) | ~np.isfinite(ghi_clear) clearsky_index = np.where(input_is_nan, np.nan, clearsky_index) clearsky_index = np.maximum(clearsky_index, 0) @@ -2151,12 +2159,17 @@ def _dirint_bins(times, kt_prime, zenith, w, delta_kt_prime): return kt_prime_bin, zenith_bin, w_bin, delta_kt_prime_bin +@renamed_kwarg_warning( + since='0.11.2', + old_param_name='ghi_clearsky', + new_param_name='ghi_clear', + removal="0.13.0") @renamed_kwarg_warning( since='0.11.2', old_param_name='dni_clearsky', new_param_name='dni_clear', removal="0.13.0") -def dirindex(ghi, ghi_clearsky, dni_clear, zenith, times, pressure=101325., +def dirindex(ghi, ghi_clear, dni_clear, zenith, times, pressure=101325., use_delta_kt_prime=True, temp_dew=None, min_cos_zenith=0.065, max_zenith=87): """ @@ -2164,7 +2177,7 @@ def dirindex(ghi, ghi_clearsky, dni_clear, zenith, times, pressure=101325., The DIRINDEX model [1]_ modifies the DIRINT model implemented in :py:func:`pvlib.irradiance.dirint` by taking into account information - from a clear sky model. It is recommended that ``ghi_clearsky`` be + from a clear sky model. It is recommended that ``ghi_clear`` be calculated using the Ineichen clear sky model :py:func:`pvlib.clearsky.ineichen` with ``perez_enhancement=True``. @@ -2175,9 +2188,12 @@ def dirindex(ghi, ghi_clearsky, dni_clear, zenith, times, pressure=101325., ghi : array-like Global horizontal irradiance. [Wm⁻²] - ghi_clearsky : array-like + ghi_clear : array-like Global horizontal irradiance from clear sky model. [Wm⁻²] + .. versionchanged:: 0.11.2 + Renamed from ``ghi_clearsky`` to ``ghi_clear``. + dni_clear : array-like Direct normal irradiance from clear sky model. [Wm⁻²] @@ -2240,7 +2256,7 @@ def dirindex(ghi, ghi_clearsky, dni_clear, zenith, times, pressure=101325., temp_dew=temp_dew, min_cos_zenith=min_cos_zenith, max_zenith=max_zenith) - dni_dirint_clearsky = dirint(ghi_clearsky, zenith, times, + dni_dirint_clearsky = dirint(ghi_clear, zenith, times, pressure=pressure, use_delta_kt_prime=use_delta_kt_prime, temp_dew=temp_dew, diff --git a/pvlib/ivtools/sdm.py b/pvlib/ivtools/sdm.py index 0c2576d519..9c72fbf7e5 100644 --- a/pvlib/ivtools/sdm.py +++ b/pvlib/ivtools/sdm.py @@ -878,7 +878,7 @@ def fun_rsh(x, rshexp, ee, e0, rsh): params['R_sh_exp'] = R_sh_exp elif model == 'desoto': - dEgdT = 0.0002677 + dEgdT = -0.0002677 x_for_io = const['q'] / const['k'] * ( 1. / tok - 1. / tck[u] + dEgdT * (tc[u] - const['T0']) / tck[u]) diff --git a/pvlib/pvsystem.py b/pvlib/pvsystem.py index 03cf782391..f99f0275af 100644 --- a/pvlib/pvsystem.py +++ b/pvlib/pvsystem.py @@ -168,9 +168,12 @@ class PVSystem: Inverter parameters as defined by the SAPM, CEC, or other. racking_model : string, optional - Valid strings are 'open_rack', 'close_mount', 'freestanding', - 'insulated', or 'insulated_back'. - Used to identify a parameter set for the cell temperature model. + Valid strings are ``'open_rack'``, ``'close_mount'``, + ``'insulated_back'``, ``'freestanding'`` and ``'insulated'``. + Used to identify a parameter set for the SAPM or PVsyst cell + temperature model. + See :py:func:`~pvlib.temperature.sapm_module` and + :py:func:`~pvlib.temperature.pvsyst_cell` for definitions. losses_parameters : dict or Series, optional Losses parameters as defined by PVWatts or other. @@ -1393,9 +1396,12 @@ class FixedMount(AbstractMount): West=270. [degrees] racking_model : str, optional - Valid strings are 'open_rack', 'close_mount', 'freestanding', - 'insulated', or 'insulated_back'. - Used to identify a parameter set for the cell temperature model. + Valid strings are ``'open_rack'``, ``'close_mount'``, + ``'insulated_back'``, ``'freestanding'`` and ``'insulated'``. + Used to identify a parameter set for the SAPM or PVsyst cell + temperature model. + See :py:func:`~pvlib.temperature.sapm_module` and + :py:func:`~pvlib.temperature.pvsyst_cell` for definitions. module_height : float, optional The height above ground of the center of the module [m]. Used for @@ -1471,9 +1477,13 @@ class SingleAxisTrackerMount(AbstractMount): `cross_axis_tilt`. [degrees] racking_model : str, optional - Valid strings are 'open_rack', 'close_mount', 'freestanding', - 'insulated', or 'insulated_back'. - Used to identify a parameter set for the cell temperature model. + Valid strings are ``'open_rack'``, ``'close_mount'``, + ``'insulated_back'``, ``'freestanding'`` and ``'insulated'``. + Used to identify a parameter set for the SAPM or PVsyst cell + temperature model. ``'open_rack'`` or ``'freestanding'`` should + be used for systems with single-axis trackers. + See :py:func:`~pvlib.temperature.sapm_module` and + :py:func:`~pvlib.temperature.pvsyst_cell` for definitions. module_height : float, optional The height above ground of the center of the module [m]. Used for diff --git a/pvlib/temperature.py b/pvlib/temperature.py index 6269014969..ab31ffaf56 100644 --- a/pvlib/temperature.py +++ b/pvlib/temperature.py @@ -118,13 +118,25 @@ def sapm_cell(poa_global, temp_air, wind_speed, a, b, deltaT, +===============+================+=======+=========+=====================+ | glass/glass | open rack | -3.47 | -0.0594 | 3 | +---------------+----------------+-------+---------+---------------------+ - | glass/glass | close roof | -2.98 | -0.0471 | 1 | + | glass/glass | close mount | -2.98 | -0.0471 | 1 | +---------------+----------------+-------+---------+---------------------+ | glass/polymer | open rack | -3.56 | -0.075 | 3 | +---------------+----------------+-------+---------+---------------------+ | glass/polymer | insulated back | -2.81 | -0.0455 | 0 | +---------------+----------------+-------+---------+---------------------+ + Mounting cases can be described in terms of air flow across and around the + rear-facing surface of the module: + + * "open rack" refers to mounting that allows relatively free air flow. + This case is typical of ground-mounted systems on fixed racking or + single axis trackers. + * "close mount" refers to limited or restricted air flow. This case is + typical of roof-mounted systems with some gap behind the module. + * "insulated back" refers to systems with no air flow contacting the rear + surface of the module. This case is typical of building-integrated PV + systems, or systems laid flat on a ground surface. + References ---------- .. [1] King, D. et al, 2004, "Sandia Photovoltaic Array Performance @@ -199,13 +211,25 @@ def sapm_module(poa_global, temp_air, wind_speed, a, b): +===============+================+=======+=========+=====================+ | glass/glass | open rack | -3.47 | -0.0594 | 3 | +---------------+----------------+-------+---------+---------------------+ - | glass/glass | close roof | -2.98 | -0.0471 | 1 | + | glass/glass | close mount | -2.98 | -0.0471 | 1 | +---------------+----------------+-------+---------+---------------------+ | glass/polymer | open rack | -3.56 | -0.075 | 3 | +---------------+----------------+-------+---------+---------------------+ | glass/polymer | insulated back | -2.81 | -0.0455 | 0 | +---------------+----------------+-------+---------+---------------------+ + Mounting cases can be described in terms of air flow across and around the + rear-facing surface of the module: + + * "open rack" refers to mounting that allows relatively free air flow. + This case is typical of ground-mounted systems on fixed racking or + single axis trackers. + * "close mount" refers to limited or restricted air flow. This case is + typical of roof-mounted systems with some gap behind the module. + * "insulated back" refers to systems with no air flow contacting the rear + surface of the module. This case is typical of building-integrated PV + systems, or systems laid flat on a ground surface. + References ---------- .. [1] King, D. et al, 2004, "Sandia Photovoltaic Array Performance @@ -269,13 +293,25 @@ def sapm_cell_from_module(module_temperature, poa_global, deltaT, +===============+================+=======+=========+=====================+ | glass/glass | open rack | -3.47 | -0.0594 | 3 | +---------------+----------------+-------+---------+---------------------+ - | glass/glass | close roof | -2.98 | -0.0471 | 1 | + | glass/glass | close mount | -2.98 | -0.0471 | 1 | +---------------+----------------+-------+---------+---------------------+ | glass/polymer | open rack | -3.56 | -0.075 | 3 | +---------------+----------------+-------+---------+---------------------+ | glass/polymer | insulated back | -2.81 | -0.0455 | 0 | +---------------+----------------+-------+---------+---------------------+ + Mounting cases can be described in terms of air flow across and around the + rear-facing surface of the module: + + * "open rack" refers to mounting that allows relatively free air flow. + This case is typical of ground-mounted systems on fixed racking or + single axis trackers. + * "close mount" refers to limited or restricted air flow. This case is + typical of roof-mounted systems with some gap behind the module. + * "insulated back" refers to systems with no air flow contacting the rear + surface of the module. This case is typical of building-integrated PV + systems, or systems laid flat on a ground surface. + References ---------- .. [1] King, D. et al, 2004, "Sandia Photovoltaic Array Performance @@ -360,6 +396,16 @@ def pvsyst_cell(poa_global, temp_air, wind_speed=1.0, u_c=29.0, u_v=0.0, | insulated | 15.0 | 0.0 | +--------------+---------------+---------------+ + Mounting cases can be described in terms of air flow across and around the + rear-facing surface of the module: + + * "freestanding" refers to mounting that allows relatively free air + circulation around the modules. This case is typical of ground-mounted + systems on tilted, fixed racking or single axis trackers. + * "insulated" refers to mounting with air flow across only the front + surface. This case is typical of roof-mounted systems with no gap + behind the module. + References ---------- .. [1] "PVsyst 7 Help", [Online]. Available: diff --git a/pvlib/tests/ivtools/test_sdm.py b/pvlib/tests/ivtools/test_sdm.py index 508591f9e9..e0def04621 100644 --- a/pvlib/tests/ivtools/test_sdm.py +++ b/pvlib/tests/ivtools/test_sdm.py @@ -140,6 +140,9 @@ def test_fit_desoto_sandia(cec_params_cansol_cs5p_220p): expected = pd.Series(params) assert np.allclose(modeled[params.keys()].values, expected[params.keys()].values, rtol=5e-2) + assert_allclose(result['dEgdT'], -0.0002677) + assert_allclose(result['EgRef'], 1.3112547292120638) + assert_allclose(result['cells_in_series'], specs['cells_in_series']) def _read_iv_curves_for_test(datafile, npts): diff --git a/pvlib/tests/test_atmosphere.py b/pvlib/tests/test_atmosphere.py index e12a41dc6d..2f0b5cadc2 100644 --- a/pvlib/tests/test_atmosphere.py +++ b/pvlib/tests/test_atmosphere.py @@ -88,6 +88,153 @@ def test_gueymard94_pw(): assert_allclose(pws, expected, atol=0.01) +def test_tdew_to_rh_to_tdew(): + + # dewpoint temp calculated with wmo and aekr coefficients + dewpoint_original = pd.Series([ + 15.0, 20.0, 25.0, 12.0, 8.0 + ]) + + temperature_ambient = pd.Series([20.0, 25.0, 30.0, 15.0, 10.0]) + + # Calculate relative humidity using pandas series as input + relative_humidity = atmosphere.rh_from_tdew( + temp_air=temperature_ambient, + temp_dew=dewpoint_original + ) + + dewpoint_calculated = atmosphere.tdew_from_rh( + temp_air=temperature_ambient, + relative_humidity=relative_humidity + ) + + # test + pd.testing.assert_series_equal( + dewpoint_original, + dewpoint_calculated, + check_names=False + ) + + +def test_rh_from_tdew(): + + dewpoint = pd.Series([ + 15.0, 20.0, 25.0, 12.0, 8.0 + ]) + + # relative humidity calculated with wmo and aekr coefficients + relative_humidity_wmo = pd.Series([ + 72.95185312581116, 73.81500029087906, 74.6401272083123, + 82.27063889868842, 87.39018119185337 + ]) + relative_humidity_aekr = pd.Series([ + 72.93876680928582, 73.8025121880607, 74.62820502423823, + 82.26135295757305, 87.38323744820416 + ]) + + temperature_ambient = pd.Series([20.0, 25.0, 30.0, 15.0, 10.0]) + + # Calculate relative humidity using pandas series as input + rh_series = atmosphere.rh_from_tdew( + temp_air=temperature_ambient, + temp_dew=dewpoint + ) + + pd.testing.assert_series_equal( + rh_series, + relative_humidity_wmo, + check_names=False + ) + + # Calulate relative humidity using pandas series as input + # with AEKR coefficients + rh_series_aekr = atmosphere.rh_from_tdew( + temp_air=temperature_ambient, + temp_dew=dewpoint, + coeff=(6.1094, 17.625, 243.04) + ) + + pd.testing.assert_series_equal( + rh_series_aekr, + relative_humidity_aekr, + check_names=False + ) + + # Calculate relative humidity using array as input + rh_array = atmosphere.rh_from_tdew( + temp_air=temperature_ambient.to_numpy(), + temp_dew=dewpoint.to_numpy() + ) + + np.testing.assert_allclose(rh_array, relative_humidity_wmo.to_numpy()) + + # Calculate relative humidity using float as input + rh_float = atmosphere.rh_from_tdew( + temp_air=temperature_ambient.iloc[0], + temp_dew=dewpoint.iloc[0] + ) + + assert np.isclose(rh_float, relative_humidity_wmo.iloc[0]) + + +# Unit tests +def test_tdew_from_rh(): + + dewpoint = pd.Series([ + 15.0, 20.0, 25.0, 12.0, 8.0 + ]) + + # relative humidity calculated with wmo and aekr coefficients + relative_humidity_wmo = pd.Series([ + 72.95185312581116, 73.81500029087906, 74.6401272083123, + 82.27063889868842, 87.39018119185337 + ]) + relative_humidity_aekr = pd.Series([ + 72.93876680928582, 73.8025121880607, 74.62820502423823, + 82.26135295757305, 87.38323744820416 + ]) + + temperature_ambient = pd.Series([20.0, 25.0, 30.0, 15.0, 10.0]) + + # test as series + dewpoint_series = atmosphere.tdew_from_rh( + temp_air=temperature_ambient, + relative_humidity=relative_humidity_wmo + ) + + pd.testing.assert_series_equal( + dewpoint_series, dewpoint, check_names=False + ) + + # test as series with AEKR coefficients + dewpoint_series_aekr = atmosphere.tdew_from_rh( + temp_air=temperature_ambient, + relative_humidity=relative_humidity_aekr, + coeff=(6.1094, 17.625, 243.04) + ) + + pd.testing.assert_series_equal( + dewpoint_series_aekr, dewpoint, + check_names=False + ) + + # test as numpy array + dewpoint_array = atmosphere.tdew_from_rh( + temp_air=temperature_ambient.to_numpy(), + relative_humidity=relative_humidity_wmo.to_numpy() + ) + + np.testing.assert_allclose(dewpoint_array, dewpoint.to_numpy()) + + # test as float + dewpoint_float = atmosphere.tdew_from_rh( + temp_air=temperature_ambient.iloc[0], + relative_humidity=relative_humidity_wmo.iloc[0] + ) + + assert np.isclose(dewpoint_float, dewpoint.iloc[0]) + + def test_first_solar_spectral_correction_deprecated(): with pytest.warns(pvlibDeprecationWarning, match='Use pvlib.spectrum.spectral_factor_firstsolar'): diff --git a/pvlib/tests/test_irradiance.py b/pvlib/tests/test_irradiance.py index c39191ba18..9352e978e6 100644 --- a/pvlib/tests/test_irradiance.py +++ b/pvlib/tests/test_irradiance.py @@ -1095,6 +1095,20 @@ def test_dirindex(times): equal_nan=True) +@fail_on_pvlib_version("0.13") +def test_dirindex_ghi_clearsky_deprecation(): + times = pd.DatetimeIndex(['2014-06-24T18-1200']) + ghi = pd.Series([1038.62], index=times) + ghi_clearsky = pd.Series([1042.48031487], index=times) + dni_clearsky = pd.Series([939.95469881], index=times) + zenith = pd.Series([10.56413562], index=times) + pressure, tdew = 93193, 10 + with pytest.warns(pvlibDeprecationWarning, match='ghi_clear'): + irradiance.dirindex( + ghi=ghi, ghi_clearsky=ghi_clearsky, dni_clear=dni_clearsky, + zenith=zenith, times=times, pressure=pressure, temp_dew=tdew) + + def test_dirindex_min_cos_zenith_max_zenith(): # map out behavior under difficult conditions with various # limiting kwargs settings @@ -1260,6 +1274,13 @@ def test_clearsky_index(): assert_series_equal(out, expected) +@fail_on_pvlib_version("0.13") +def test_clearsky_index_clearsky_ghi_deprecation(): + with pytest.warns(pvlibDeprecationWarning, match='ghi_clear'): + ghi, clearsky_ghi = 200, 300 + irradiance.clearsky_index(ghi, clearsky_ghi=clearsky_ghi) + + def test_clearness_index(): ghi = np.array([-1, 0, 1, 1000]) solar_zenith = np.array([180, 90, 89.999, 0])