From 6ff02e5295a2c57f7225a4be54a34cbcd10cca91 Mon Sep 17 00:00:00 2001 From: "Adam R. Jensen" <39184289+AdamRJensen@users.noreply.github.com> Date: Tue, 12 Dec 2023 16:37:04 +0100 Subject: [PATCH 1/6] Add atand function (#1927) * Add atand function * Fix * Fix me again * Last fix promise * Update pvlib/tools.py Co-authored-by: Kevin Anderson <kevin.anderso@gmail.com> * Apply suggestions from code review Thanks for these improvements @adriesse Co-authored-by: Anton Driesse <anton.driesse@pvperformancelabs.com> --------- Co-authored-by: Kevin Anderson <kevin.anderso@gmail.com> Co-authored-by: Anton Driesse <anton.driesse@pvperformancelabs.com> --- pvlib/tools.py | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/pvlib/tools.py b/pvlib/tools.py index fe1b79a5f1..adf502a79d 100644 --- a/pvlib/tools.py +++ b/pvlib/tools.py @@ -11,7 +11,7 @@ def cosd(angle): """ - Cosine with angle input in degrees + Trigonometric cosine with angle input in degrees. Parameters ---------- @@ -23,14 +23,13 @@ def cosd(angle): result : float or array-like Cosine of the angle """ - res = np.cos(np.radians(angle)) return res def sind(angle): """ - Sine with angle input in degrees + Trigonometric sine with angle input in degrees. Parameters ---------- @@ -42,14 +41,13 @@ def sind(angle): result : float Sin of the angle """ - res = np.sin(np.radians(angle)) return res def tand(angle): """ - Tan with angle input in degrees + Trigonometric tangent with angle input in degrees. Parameters ---------- @@ -61,14 +59,13 @@ def tand(angle): result : float Tan of the angle """ - res = np.tan(np.radians(angle)) return res def asind(number): """ - Inverse Sine returning an angle in degrees + Trigonometric inverse sine returning an angle in degrees. Parameters ---------- @@ -80,14 +77,13 @@ def asind(number): result : float arcsin result """ - res = np.degrees(np.arcsin(number)) return res def acosd(number): """ - Inverse Cosine returning an angle in degrees + Trigonometric inverse cosine returning an angle in degrees. Parameters ---------- @@ -99,11 +95,28 @@ def acosd(number): result : float arccos result """ - res = np.degrees(np.arccos(number)) return res +def atand(number): + """ + Trigonometric inverse tangent returning an angle in degrees. + + Parameters + ---------- + number : float + Input number + + Returns + ------- + result : float + arctan result + """ + res = np.degrees(np.arctan(number)) + return res + + def localize_to_utc(time, location): """ Converts or localizes a time series to UTC. From 300aedc27f3a78ab9dc850880d1def92055bc079 Mon Sep 17 00:00:00 2001 From: Echedey Luis <80125792+echedey-ls@users.noreply.github.com> Date: Tue, 12 Dec 2023 16:53:05 +0100 Subject: [PATCH 2/6] Docstring cleanup: "default None", doi (#1828) * Align reference * Update irradiance.py Exclude .*=| from negative lookahead ^(\s{8}|\s{4})(?!try|else|doi|DOI|Warning|Access|Requests|Note)(\w*): (?!.*:)(.+)$ Reason: just this line :full_moon_with_face: Remaining matches are type annotations in modelchain.py and pvsystem.py * Remove parenthesis in types F: (?<spaces>\s{8}|\s{4})(?<name>\w*)\s?: (?<type>.*) \(optional, default=(?<default>.*)\)$ R: * Remove none in type definition #1574 1st-F: (?<spaces> {8}| {4})(?<name>\w*) : (?<types>.*), default None$ 2nd-F: (?<spaces> {8}| {4})(?<name>\w*) : [Nn}one or (?<type>.*), optional$ 3rd-F: (?<spaces> {8}| {4})(?<name>\w*) : (?<type>.*) or [nN]one, optional$ R: $1$2 : $3, optional * Change if none ocurrences (I) F: If None, R: If not specified, * Change if none ocurrences (II) F: If None R: If not specified, * Too long line * Too long line * fix doi external links Regex find: (?:doi|DOI):(?!`)\s?(.*?)(\.\n|\n) Replace: :doi:`$1`$2 * Apply cwhanse suggestion Co-authored-by: Cliff Hansen <cwhanse@sandia.gov> * Apply cwhanse suggestion Co-authored-by: Cliff Hansen <cwhanse@sandia.gov> * Apply cwhanse suggestion Co-authored-by: Cliff Hansen <cwhanse@sandia.gov> * Apply cwhanse suggestion Co-authored-by: Cliff Hansen <cwhanse@sandia.gov> * Modified cwhanse suggestion Co-Authored-By: Cliff Hansen <5393711+cwhanse@users.noreply.github.com> * Update modelchain.py Co-Authored-By: Cliff Hansen <5393711+cwhanse@users.noreply.github.com> * Some None's missed F: (?<spaces> {8}| {4})(?<name>\w*) : [Nn]one[ ,]*(?<type>.*), optional$ R: $1$2 : $3, optional Co-Authored-By: Cliff Hansen <5393711+cwhanse@users.noreply.github.com> * ``none``'s, some more ``'s to variables and some other nones Co-Authored-By: Cliff Hansen <5393711+cwhanse@users.noreply.github.com> * Special ``none`` cases (please review) Co-Authored-By: Cliff Hansen <5393711+cwhanse@users.noreply.github.com> * Linter Co-Authored-By: Cliff Hansen <5393711+cwhanse@users.noreply.github.com> * Run more regexes F1: (?<spaces> {8}| {4})(?<name>\w*) ?: (?<type>.*), default: None F2: (?<spaces> {8}| {4})(?<name>\w*) ?: (?<types>.*), default None$ F3: (?<spaces> {8}| {4})(?<name>\w*) ?: [Nn]one or (?<type>.*), optional$ zero results F4: (?<spaces> {8}| {4})(?<name>\w*) ?: (?<type>.*) or [nN]one, optional$ zero results R: $1$2 : $3, optional * More Nones F: (?<spaces> {8}| {4})(?<name>\w*) ?: (?<type>.*), default:? None\.? R: $1$2 : $3, optional F: (?<spaces> {8}| {4})(?<name>\w*) ?: [Nn]one, (?<type>.*), default:? (?<def>.*)\.? R: $1$2 : $3, default $4 F: (?<spaces> {8}| {4})(?<name>\w*) ?: [Nn]one or (?<types>.*) R: $1$2 : $3 F: (?<spaces> {8}| {4})(?<name>\w*) ?: (?<types>.*) or [Nn]one(?<trailing>.*) R: $1$2 : $3$4 F: (?<spaces> {8}| {4})(?<name>\w*) ?: [Nn]one(?:,|or) (?<types>.*) R: $1$2 : $3 * Update irradiance.py * Revert "Special ``none`` cases (please review)" This reverts commit b8f942bb4396829986158d3995277c42f418e478. * Apply suggestions from code review Co-authored-by: Cliff Hansen <cwhanse@sandia.gov> * Update irradiance.py * Update pvlib/irradiance.py Co-authored-by: Cliff Hansen <cwhanse@sandia.gov> * Update irradiance.py --------- Co-authored-by: Cliff Hansen <cwhanse@sandia.gov> Co-authored-by: Cliff Hansen <5393711+cwhanse@users.noreply.github.com> --- pvlib/bifacial/utils.py | 2 +- pvlib/clearsky.py | 8 ++-- pvlib/iam.py | 12 +++--- pvlib/iotools/epw.py | 4 +- pvlib/iotools/pvgis.py | 50 +++++++++++------------ pvlib/iotools/sodapro.py | 10 ++--- pvlib/iotools/tmy.py | 6 +-- pvlib/irradiance.py | 57 ++++++++++++++------------- pvlib/ivtools/sde.py | 6 +-- pvlib/ivtools/sdm.py | 2 +- pvlib/ivtools/utils.py | 6 +-- pvlib/location.py | 22 +++++------ pvlib/modelchain.py | 50 +++++++++++------------ pvlib/pvsystem.py | 81 +++++++++++++++++++------------------- pvlib/scaling.py | 4 +- pvlib/shading.py | 6 +-- pvlib/soiling.py | 4 +- pvlib/solarposition.py | 14 +++---- pvlib/spectrum/mismatch.py | 2 +- pvlib/spectrum/spectrl2.py | 2 +- pvlib/temperature.py | 6 +-- 21 files changed, 177 insertions(+), 177 deletions(-) diff --git a/pvlib/bifacial/utils.py b/pvlib/bifacial/utils.py index 8d7f5d5a71..9e6e0bcd60 100644 --- a/pvlib/bifacial/utils.py +++ b/pvlib/bifacial/utils.py @@ -80,7 +80,7 @@ def _unshaded_ground_fraction(surface_tilt, surface_azimuth, solar_zenith, .. [1] Mikofski, M., Darawali, R., Hamer, M., Neubert, A., and Newmiller, J. "Bifacial Performance Modeling in Large Arrays". 2019 IEEE 46th Photovoltaic Specialists Conference (PVSC), 2019, pp. 1282-1287. - doi: 10.1109/PVSC40753.2019.8980572. + :doi:`10.1109/PVSC40753.2019.8980572`. """ tan_phi = _solar_projection_tangent(solar_zenith, solar_azimuth, surface_azimuth) diff --git a/pvlib/clearsky.py b/pvlib/clearsky.py index 62318942da..ad779182eb 100644 --- a/pvlib/clearsky.py +++ b/pvlib/clearsky.py @@ -159,7 +159,7 @@ def lookup_linke_turbidity(time, latitude, longitude, filepath=None, longitude : float or int - filepath : None or string, default None + filepath : string, optional The path to the ``.h5`` file. interp_turbidity : bool, default True @@ -703,9 +703,9 @@ def detect_clearsky(measured, clearsky, times=None, infer_limits=False, Time series of measured GHI. [W/m2] clearsky : array or Series Time series of the expected clearsky GHI. [W/m2] - times : DatetimeIndex or None, default None. - Times of measured and clearsky values. If None the index of measured - will be used. + times : DatetimeIndex, optional + Times of measured and clearsky values. If not specified, the index of + ``measured`` will be used. infer_limits : bool, default False If True, does not use passed in kwargs (or defaults), but instead interpolates these values from Table 1 in [2]_. diff --git a/pvlib/iam.py b/pvlib/iam.py index b0b2202ad9..4f32d352ee 100644 --- a/pvlib/iam.py +++ b/pvlib/iam.py @@ -124,7 +124,7 @@ def physical(aoi, n=1.526, K=4.0, L=0.002, *, n_ar=None): n_ar : numeric, optional The effective index of refraction of the anti-reflective (AR) coating - (unitless). If n_ar is None (default), no AR coating is applied. + (unitless). If ``n_ar`` is not supplied, no AR coating is applied. A typical value for the effective index of an AR coating is 1.29. Returns @@ -338,7 +338,7 @@ def martin_ruiz_diffuse(surface_tilt, a_r=0.16, c1=0.4244, c2=None): c2 : float Second fitting parameter for the expressions that approximate the integral of diffuse irradiance coming from different directions. - If c2 is None, it will be calculated according to the linear + If c2 is not specified, it will be calculated according to the linear relationship given in [3]_. Returns @@ -514,7 +514,7 @@ def sapm(aoi, module, upper=None): A dict or Series with the SAPM IAM model parameters. See the :py:func:`sapm` notes section for more details. - upper : None or float, default None + upper : float, optional Upper limit on the results. Returns @@ -541,7 +541,7 @@ def sapm(aoi, module, upper=None): .. [3] B.H. King et al, "Recent Advancements in Outdoor Measurement Techniques for Angle of Incidence Effects," 42nd IEEE PVSC (2015). - DOI: 10.1109/PVSC.2015.7355849 + :doi:`10.1109/PVSC.2015.7355849` See Also -------- @@ -607,7 +607,7 @@ def marion_diffuse(model, surface_tilt, **kwargs): .. [1] B. Marion "Numerical method for angle-of-incidence correction factors for diffuse radiation incident photovoltaic modules", Solar Energy, Volume 147, Pages 344-348. 2017. - DOI: 10.1016/j.solener.2017.03.027 + :doi:`10.1016/j.solener.2017.03.027` Examples -------- @@ -694,7 +694,7 @@ def marion_integrate(function, surface_tilt, region, num=None): .. [1] B. Marion "Numerical method for angle-of-incidence correction factors for diffuse radiation incident photovoltaic modules", Solar Energy, Volume 147, Pages 344-348. 2017. - DOI: 10.1016/j.solener.2017.03.027 + :doi:`10.1016/j.solener.2017.03.027` Examples -------- diff --git a/pvlib/iotools/epw.py b/pvlib/iotools/epw.py index 249dd76056..a777b69911 100644 --- a/pvlib/iotools/epw.py +++ b/pvlib/iotools/epw.py @@ -25,7 +25,7 @@ def read_epw(filename, coerce_year=None): filename : String Can be a relative file path, absolute file path, or url. - coerce_year : None or int, default None + coerce_year : int, optional If supplied, the year of the data will be set to this value. This can be a useful feature because EPW data is composed of data from different years. @@ -247,7 +247,7 @@ def parse_epw(csvdata, coerce_year=None): csvdata : file-like buffer a file-like buffer containing data in the EPW format - coerce_year : None or int, default None + coerce_year : int, optional If supplied, the year of the data will be set to this value. This can be a useful feature because EPW data is composed of data from different years. diff --git a/pvlib/iotools/pvgis.py b/pvlib/iotools/pvgis.py index 3f1ba01e97..06986bf2e5 100644 --- a/pvlib/iotools/pvgis.py +++ b/pvlib/iotools/pvgis.py @@ -63,13 +63,13 @@ def get_pvgis_hourly(latitude, longitude, start=None, end=None, In decimal degrees, between -90 and 90, north is positive (ISO 19115) longitude: float In decimal degrees, between -180 and 180, east is positive (ISO 19115) - start: int or datetime like, default: None + start : int or datetime like, optional First year of the radiation time series. Defaults to first year available. - end: int or datetime like, default: None + end : int or datetime like, optional Last year of the radiation time series. Defaults to last year available. - raddatabase: str, default: None + raddatabase : str, optional Name of radiation database. Options depend on location, see [3]_. components: bool, default: True Output solar radiation components (beam, diffuse, and reflected). @@ -87,14 +87,14 @@ def get_pvgis_hourly(latitude, longitude, start=None, end=None, and pvlib<=0.9.5 is offset by 180 degrees. usehorizon: bool, default: True Include effects of horizon - userhorizon: list of float, default: None + userhorizon : list of float, optional Optional user specified elevation of horizon in degrees, at equally spaced azimuth clockwise from north, only valid if ``usehorizon`` is - true, if ``usehorizon`` is true but ``userhorizon`` is ``None`` then + true, if ``usehorizon`` is true but ``userhorizon`` is not specified then PVGIS will calculate the horizon [4]_ pvcalculation: bool, default: False Return estimate of hourly PV production. - peakpower: float, default: None + peakpower : float, optional Nominal power of PV system in kW. Required if pvcalculation=True. pvtechchoice: {'crystSi', 'CIS', 'CdTe', 'Unknown'}, default: 'crystSi' PV technology. @@ -309,12 +309,12 @@ def read_pvgis_hourly(filename, pvgis_format=None, map_variables=True): ---------- filename : str, pathlib.Path, or file-like buffer Name, path, or buffer of hourly data file downloaded from PVGIS. - pvgis_format : str, default None + pvgis_format : str, optional Format of PVGIS file or buffer. Equivalent to the ``outputformat`` parameter in the PVGIS API. If ``filename`` is a file and - ``pvgis_format`` is ``None`` then the file extension will be used to - determine the PVGIS format to parse. If ``filename`` is a buffer, then - ``pvgis_format`` is required and must be in ``['csv', 'json']``. + ``pvgis_format`` is not specified then the file extension will be used + to determine the PVGIS format to parse. If ``filename`` is a buffer, + then ``pvgis_format`` is required and must be in ``['csv', 'json']``. map_variables: bool, default True When true, renames columns of the DataFrame to pvlib variable names where applicable. See variable :const:`VARIABLE_MAP`. @@ -336,11 +336,11 @@ def read_pvgis_hourly(filename, pvgis_format=None, map_variables=True): Raises ------ ValueError - if ``pvgis_format`` is ``None`` and the file extension is neither + if ``pvgis_format`` is not specified and the file extension is neither ``.csv`` nor ``.json`` or if ``pvgis_format`` is provided as input but isn't in ``['csv', 'json']`` TypeError - if ``pvgis_format`` is ``None`` and ``filename`` is a buffer + if ``pvgis_format`` is not specified and ``filename`` is a buffer See Also -------- @@ -409,14 +409,13 @@ def get_pvgis_tmy(latitude, longitude, outputformat='json', usehorizon=True, documentation [2]_ for more info. usehorizon : bool, default True include effects of horizon - userhorizon : list of float, default None - optional user specified elevation of horizon in degrees, at equally - spaced azimuth clockwise from north, only valid if ``usehorizon`` is - true, if ``usehorizon`` is true but ``userhorizon`` is ``None`` then - PVGIS will calculate the horizon [3]_ - startyear : int, default None + userhorizon : list of float, optional + Optional user-specified elevation of horizon in degrees, at equally + spaced azimuth clockwise from north. If not specified, PVGIS will + calculate the horizon [3]_. If specified, requires ``usehorizon=True``. + startyear : int, optional first year to calculate TMY - endyear : int, default None + endyear : int, optional last year to calculate TMY, must be at least 10 years from first year map_variables: bool, default True When true, renames columns of the Dataframe to pvlib variable names @@ -573,12 +572,13 @@ def read_pvgis_tmy(filename, pvgis_format=None, map_variables=True): ---------- filename : str, pathlib.Path, or file-like buffer Name, path, or buffer of file downloaded from PVGIS. - pvgis_format : str, default None + pvgis_format : str, optional Format of PVGIS file or buffer. Equivalent to the ``outputformat`` parameter in the PVGIS TMY API. If ``filename`` is a file and - ``pvgis_format`` is ``None`` then the file extension will be used to - determine the PVGIS format to parse. For PVGIS files from the API with - ``outputformat='basic'``, please set ``pvgis_format`` to ``'basic'``. + ``pvgis_format`` is not specified then the file extension will be used + to determine the PVGIS format to parse. For PVGIS files from the API + with ``outputformat='basic'``, please set ``pvgis_format`` to + ``'basic'``. If ``filename`` is a buffer, then ``pvgis_format`` is required and must be in ``['csv', 'epw', 'json', 'basic']``. map_variables: bool, default True @@ -600,11 +600,11 @@ def read_pvgis_tmy(filename, pvgis_format=None, map_variables=True): Raises ------ ValueError - if ``pvgis_format`` is ``None`` and the file extension is neither + if ``pvgis_format`` is not specified and the file extension is neither ``.csv``, ``.json``, nor ``.epw``, or if ``pvgis_format`` is provided as input but isn't in ``['csv', 'epw', 'json', 'basic']`` TypeError - if ``pvgis_format`` is ``None`` and ``filename`` is a buffer + if ``pvgis_format`` is not specified and ``filename`` is a buffer See Also -------- diff --git a/pvlib/iotools/sodapro.py b/pvlib/iotools/sodapro.py index 95d6ffd866..a6c43ad341 100644 --- a/pvlib/iotools/sodapro.py +++ b/pvlib/iotools/sodapro.py @@ -75,8 +75,8 @@ def get_cams(latitude, longitude, start, end, email, identifier='mcclear', identifier: {'mcclear', 'cams_radiation'} Specify whether to retrieve CAMS Radiation or McClear parameters altitude: float, optional - Altitude in meters. If None, then the altitude is determined from the - NASA SRTM database + Altitude in meters. If not specified, then the altitude is determined + from the NASA SRTM database time_step: str, {'1min', '15min', '1h', '1d', '1M'}, default: '1h' Time step of the time series, either 1 minute, 15 minute, hourly, daily, or monthly. @@ -88,7 +88,7 @@ def get_cams(latitude, longitude, start, end, email, identifier='mcclear', integrated: boolean, default False Whether to return radiation parameters as integrated values (Wh/m^2) or as average irradiance values (W/m^2) (pvlib preferred units) - label: {'right', 'left'}, default: None + label : {'right', 'left'}, optional Which bin edge label to label time-step with. The default is 'left' for all time steps except for '1M' which has a default of 'right'. map_variables: bool, default: True @@ -241,7 +241,7 @@ def parse_cams(fbuf, integrated=False, label=None, map_variables=True): integrated: boolean, default False Whether to return radiation parameters as integrated values (Wh/m^2) or as average irradiance values (W/m^2) (pvlib preferred units) - label: {'right', 'left'}, default: None + label : {'right', 'left'}, optional Which bin edge label to label time-step with. The default is 'left' for all time steps except for '1M' which has a default of 'right'. map_variables: bool, default: True @@ -342,7 +342,7 @@ def read_cams(filename, integrated=False, label=None, map_variables=True): integrated: boolean, default False Whether to return radiation parameters as integrated values (Wh/m^2) or as average irradiance values (W/m^2) (pvlib preferred units) - label: {'right', 'left}, default: None + label : {'right', 'left}, optional Which bin edge label to label time-step with. The default is 'left' for all time steps except for '1M' which has a default of 'right'. map_variables: bool, default: True diff --git a/pvlib/iotools/tmy.py b/pvlib/iotools/tmy.py index 90358ed105..fde96ee679 100644 --- a/pvlib/iotools/tmy.py +++ b/pvlib/iotools/tmy.py @@ -40,11 +40,11 @@ def read_tmy3(filename, coerce_year=None, map_variables=None, recolumn=None, ---------- filename : str A relative file path or absolute file path. - coerce_year : None or int, default None - If supplied, the year of the index will be set to `coerce_year`, except + coerce_year : int, optional + If supplied, the year of the index will be set to ``coerce_year``, except for the last index value which will be set to the *next* year so that the index increases monotonically. - map_variables : bool, default None + map_variables : bool, optional When True, renames columns of the DataFrame to pvlib variable names where applicable. See variable :const:`VARIABLE_MAP`. recolumn : bool (deprecated, use map_variables instead) diff --git a/pvlib/irradiance.py b/pvlib/irradiance.py index efae7f6236..acccf97ba5 100644 --- a/pvlib/irradiance.py +++ b/pvlib/irradiance.py @@ -342,13 +342,13 @@ def get_total_irradiance(surface_tilt, surface_azimuth, Global horizontal irradiance. [W/m2] dhi : numeric Diffuse horizontal irradiance. [W/m2] - dni_extra : None or numeric, default None + dni_extra : numeric, optional Extraterrestrial direct normal irradiance. [W/m2] - airmass : None or numeric, default None + airmass : numeric, optional Relative airmass (not adjusted for pressure). [unitless] albedo : numeric, default 0.25 Ground surface albedo. [unitless] - surface_type : None or str, default None + surface_type : str, optional Surface type. See :py:func:`~pvlib.irradiance.get_ground_diffuse` for the list of accepted values. model : str, default 'isotropic' @@ -421,9 +421,9 @@ def get_sky_diffuse(surface_tilt, surface_azimuth, Global horizontal irradiance. [W/m2] dhi : numeric Diffuse horizontal irradiance. [W/m2] - dni_extra : None or numeric, default None + dni_extra : numeric, optional Extraterrestrial direct normal irradiance. [W/m2] - airmass : None or numeric, default None + airmass : numeric, optional Relative airmass (not adjusted for pressure). [unitless] model : str, default 'isotropic' Irradiance model. Can be one of ``'isotropic'``, ``'klucher'``, @@ -441,7 +441,7 @@ def get_sky_diffuse(surface_tilt, surface_azimuth, ------ ValueError If model is one of ``'haydavies'``, ``'reindl'``, or ``'perez'`` and - ``dni_extra`` is ``None``. + ``dni_extra`` is not specified. Notes ----- @@ -574,10 +574,11 @@ def get_ground_diffuse(surface_tilt, ghi, albedo=.25, surface_type=None): the reflection coefficient. Must be >=0 and <=1. Will be overridden if surface_type is supplied. - surface_type: None or string, default None - If not None, overrides albedo. String can be one of 'urban', - 'grass', 'fresh grass', 'snow', 'fresh snow', 'asphalt', 'concrete', - 'aluminum', 'copper', 'fresh steel', 'dirty steel', 'sea'. + surface_type : string, optional + If supplied, overrides ``albedo``. ``surface_type`` can be one of + 'urban', 'grass', 'fresh grass', 'snow', 'fresh snow', 'asphalt', + 'concrete', 'aluminum', 'copper', 'fresh steel', 'dirty steel', + 'sea'. Returns ------- @@ -790,17 +791,17 @@ def haydavies(surface_tilt, surface_azimuth, dhi, dni, dni_extra, dni_extra : numeric Extraterrestrial normal irradiance in W/m^2. - solar_zenith : None or numeric, default None + solar_zenith : numeric, optional Solar apparent (refraction-corrected) zenith angles in decimal degrees. Must supply ``solar_zenith`` and ``solar_azimuth`` or supply ``projection_ratio``. - solar_azimuth : None or numeric, default None + solar_azimuth : numeric, optional Solar azimuth angles in decimal degrees. Must supply ``solar_zenith`` and ``solar_azimuth`` or supply ``projection_ratio``. - projection_ratio : None or numeric, default None + projection_ratio : numeric, optional Ratio of angle of incidence projection to solar zenith angle projection. Must supply ``solar_zenith`` and ``solar_azimuth`` or supply ``projection_ratio``. @@ -1081,7 +1082,7 @@ def perez(surface_tilt, surface_azimuth, dhi, dni, dni_extra, inputs. AM must be >=0 (careful using the 1/sec(z) model of AM generation) - model : string (optional, default='allsitescomposite1990') + model : string, default 'allsitescomposite1990' A string which selects the desired set of Perez coefficients. If model is not provided as an input, the default, '1990' will be used. All possible model selections are: @@ -1099,7 +1100,7 @@ def perez(surface_tilt, surface_azimuth, dhi, dni, dni_extra, * 'capecanaveral1988' * 'albany1988' - return_components: bool (optional, default=False) + return_components : bool, default False Flag used to decide whether to return the calculated diffuse components or not. @@ -1341,8 +1342,8 @@ def perez_driesse(surface_tilt, surface_azimuth, dhi, dni, dni_extra, and <=360. The azimuth convention is defined as degrees east of north (e.g. North = 0, East = 90, West = 270). - airmass : numeric (optional, default None) - Relative (not pressure-corrected) airmass values. If airmass is a + airmass : numeric, optional + Relative (not pressure-corrected) airmass values. If ``airmass`` is a DataFrame it must be of the same size as all other DataFrame inputs. The kastenyoung1989 airmass calculation is used internally and is also recommended when pre-calculating airmass because @@ -1620,9 +1621,9 @@ def disc(ghi, solar_zenith, datetime_or_doy, pressure=101325, Day of year or array of days of year e.g. pd.DatetimeIndex.dayofyear, or pd.DatetimeIndex. - pressure : None or numeric, default 101325 - Site pressure in Pascal. If None, relative airmass is used - instead of absolute (pressure-corrected) airmass. + pressure : numeric or None, default 101325 + Site pressure in Pascal. Uses absolute (pressure-corrected) airmass + by default. Set to ``None`` to use relative airmass. min_cos_zenith : numeric, default 0.065 Minimum value of cos(zenith) to allow when calculating global @@ -1776,7 +1777,7 @@ def dirint(ghi, solar_zenith, times, pressure=101325., use_delta_kt_prime=True, GHI points is 1.5 hours or greater. If use_delta_kt_prime=True, input data must be Series. - temp_dew : None, float, or array-like, default None + temp_dew : float, or array-like, optional Surface dew point temperatures, in degrees C. Values of temp_dew may be numeric or NaN. Any single time period point with a temp_dew=NaN does not have dew point improvements applied. If @@ -2025,7 +2026,7 @@ def dirindex(ghi, ghi_clearsky, dni_clearsky, zenith, times, pressure=101325., GHI points is 1.5 hours or greater. If use_delta_kt_prime=True, input data must be Series. - temp_dew : None, float, or array-like, default None + temp_dew : float, or array-like, optional Surface dew point temperatures, in degrees C. Values of temp_dew may be numeric or NaN. Any single time period point with a temp_dew=NaN does not have dew point improvements applied. If @@ -2133,7 +2134,7 @@ def gti_dirint(poa_global, aoi, solar_zenith, solar_azimuth, times, GHI points is 1.5 hours or greater. If use_delta_kt_prime=True, input data must be Series. - temp_dew : None, float, or array-like, default None + temp_dew : float, or array-like, optional Surface dew point temperatures, in degrees C. Values of temp_dew may be numeric or NaN. Any single time period point with a temp_dew=NaN does not have dew point improvements applied. If @@ -2529,11 +2530,11 @@ def erbs_driesse(ghi, zenith, datetime_or_doy=None, dni_extra=None, Global horizontal irradiance in W/m^2. zenith: numeric True (not refraction-corrected) zenith angles in decimal degrees. - datetime_or_doy : int, float, array, pd.DatetimeIndex, default None + datetime_or_doy : int, float, array or pd.DatetimeIndex, optional Day of year or array of days of year e.g. pd.DatetimeIndex.dayofyear, or pd.DatetimeIndex. Either datetime_or_doy or dni_extra must be provided. - dni_extra : numeric, default None + dni_extra : numeric, optional Extraterrestrial normal irradiance. dni_extra can be provided if available to avoid recalculating it inside this function. In this case datetime_or_doy is not required. @@ -2652,7 +2653,7 @@ def orgill_hollands(ghi, zenith, datetime_or_doy, dni_extra=None, datetime_or_doy : int, float, array, pd.DatetimeIndex Day of year or array of days of year e.g. pd.DatetimeIndex.dayofyear, or pd.DatetimeIndex. - dni_extra : None or numeric, default None + dni_extra : numeric, optional Extraterrestrial direct normal irradiance. [W/m2] min_cos_zenith : numeric, default 0.065 Minimum value of cos(zenith) to allow when calculating global @@ -2945,7 +2946,7 @@ def _get_perez_coefficients(perezmodel): Parameters ---------- - perezmodel : string (optional, default='allsitescomposite1990') + perezmodel : string, default 'allsitescomposite1990' a character string which selects the desired set of Perez coefficients. If model is not provided as an input, the default, @@ -3465,7 +3466,7 @@ def dni(ghi, dhi, zenith, clearsky_dni=None, clearsky_tolerance=1.1, True (not refraction-corrected) zenith angles in decimal degrees. Angles must be >=0 and <=180. - clearsky_dni : None or Series, default None + clearsky_dni : Series, optional Clearsky direct normal irradiance. clearsky_tolerance : float, default 1.1 diff --git a/pvlib/ivtools/sde.py b/pvlib/ivtools/sde.py index 786a777210..236e91e390 100644 --- a/pvlib/ivtools/sde.py +++ b/pvlib/ivtools/sde.py @@ -25,15 +25,15 @@ def fit_sandia_simple(voltage, current, v_oc=None, i_sc=None, v_mp_i_mp=None, 1D array of `float` type containing current at each point on the IV curve, from ``i_sc`` to 0 inclusive. [A] - v_oc : float, default None + v_oc : float, optional Open circuit voltage. If not provided, ``v_oc`` is taken as the last point in the ``voltage`` array. [V] - i_sc : float, default None + i_sc : float, optional Short circuit current. If not provided, ``i_sc`` is taken as the first point in the ``current`` array. [A] - v_mp_i_mp : tuple of float, default None + v_mp_i_mp : tuple of float, optional Voltage, current at maximum power point. If not provided, the maximum power point is found at the maximum of ``voltage`` \times ``current``. [V], [A] diff --git a/pvlib/ivtools/sdm.py b/pvlib/ivtools/sdm.py index 1f36471a30..7d5a1cdd79 100644 --- a/pvlib/ivtools/sdm.py +++ b/pvlib/ivtools/sdm.py @@ -166,7 +166,7 @@ def fit_desoto(v_mp, i_mp, v_oc, i_sc, alpha_sc, beta_voc, cells_in_series, Reference temperature condition [C] irrad_ref: float, default 1000 Reference irradiance condition [W/m2] - root_kwargs: dictionary, default None + root_kwargs : dictionary, optional Dictionary of arguments to pass onto scipy.optimize.root() Returns diff --git a/pvlib/ivtools/utils.py b/pvlib/ivtools/utils.py index bdbc42da6c..cde50655dc 100644 --- a/pvlib/ivtools/utils.py +++ b/pvlib/ivtools/utils.py @@ -49,7 +49,7 @@ def _numdiff(x, f): ---------- .. [1] M. K. Bowen, R. Smith, "Derivative formulae and errors for non-uniformly spaced points", Proceedings of the Royal Society A, vol. - 461 pp 1975 - 1997, July 2005. DOI: 10.1098/rpsa.2004.1430 + 461 pp 1975 - 1997, July 2005. :doi:`10.1098/rpsa.2004.1430` .. [2] PVLib MATLAB https://github.com/sandialabs/MATLAB_PV_LIB """ @@ -136,9 +136,9 @@ def rectify_iv_curve(voltage, current, decimals=None): ---------- voltage : numeric [V] current : numeric [A] - decimals : int or None, default None + decimals : int, optional number of decimal places to which voltage is rounded to remove - duplicated points. If None, no rounding is done. + duplicated points. If not specified, no rounding is done. Returns ------- diff --git a/pvlib/location.py b/pvlib/location.py index 46d8477237..0a1f56c6ba 100644 --- a/pvlib/location.py +++ b/pvlib/location.py @@ -47,7 +47,7 @@ class Location: altitude : float, default 0. Altitude from sea level in meters. - name : None or string, default None. + name : string, optional Sets the name attribute of the Location object. See also @@ -94,7 +94,7 @@ def from_tmy(cls, tmy_metadata, tmy_data=None, **kwargs): ---------- tmy_metadata : dict Returned from tmy.readtmy2 or tmy.readtmy3 - tmy_data : None or DataFrame, default None + tmy_data : DataFrame, optional Optionally attach the TMY data to this object. Returns @@ -138,7 +138,7 @@ def from_epw(cls, metadata, data=None, **kwargs): ---------- metadata : dict Returned from epw.read_epw - data : None or DataFrame, default None + data : DataFrame, optional Optionally attach the epw data to this object. Returns @@ -173,10 +173,10 @@ def get_solarposition(self, times, pressure=None, temperature=12, ---------- times : pandas.DatetimeIndex Must be localized or UTC will be assumed. - pressure : None, float, or array-like, default None - If None, pressure will be calculated using + pressure : float, or array-like, optional + If not specified, ``pressure`` is calculated using :py:func:`pvlib.atmosphere.alt2pres` and ``self.altitude``. - temperature : None, float, or array-like, default 12 + temperature : float or array-like, default 12 kwargs passed to :py:func:`pvlib.solarposition.get_solarposition` @@ -209,11 +209,11 @@ def get_clearsky(self, times, model='ineichen', solar_position=None, model: str, default 'ineichen' The clear sky model to use. Must be one of 'ineichen', 'haurwitz', 'simplified_solis'. - solar_position : None or DataFrame, default None + solar_position : DataFrame, optional DataFrame with columns 'apparent_zenith', 'zenith', 'apparent_elevation'. - dni_extra: None or numeric, default None - If None, will be calculated from times. + dni_extra : numeric, optional + If not specified, will be calculated from times. kwargs Extra parameters passed to the relevant functions. Climatological @@ -279,9 +279,9 @@ def get_airmass(self, times=None, solar_position=None, Parameters ---------- - times : None or DatetimeIndex, default None + times : DatetimeIndex, optional Only used if solar_position is not provided. - solar_position : None or DataFrame, default None + solar_position : DataFrame, optional DataFrame with columns 'apparent_zenith', 'zenith'. model : str, default 'kastenyoung1989' Relative airmass model. See diff --git a/pvlib/modelchain.py b/pvlib/modelchain.py index c88f8a7641..cc42d59077 100644 --- a/pvlib/modelchain.py +++ b/pvlib/modelchain.py @@ -108,24 +108,24 @@ def basic_chain(times, latitude, longitude, as degrees east of north (North=0, South=180, East=90, West=270). - module_parameters : None, dict or Series + module_parameters : dict or Series Module parameters as defined by the SAPM. See pvsystem.sapm for details. - temperature_model_parameters : None, dict or Series. + temperature_model_parameters : dict or Series Temperature model parameters as defined by the SAPM. See temperature.sapm_cell for details. - inverter_parameters : None, dict or Series + inverter_parameters : dict or Series Inverter parameters as defined by the CEC. See :py:func:`inverter.sandia` for details. - irradiance : None or DataFrame, default None - If None, calculates clear sky data. + irradiance : DataFrame, optional + If not specified, calculates clear sky data. Columns must be 'dni', 'ghi', 'dhi'. - weather : None or DataFrame, default None - If None, assumes air temperature is 20 C and + weather : DataFrame, optional + If not specified, assumes air temperature is 20 C and wind speed is 0 m/s. Columns must be 'wind_speed', 'temp_air'. @@ -138,13 +138,13 @@ def basic_chain(times, latitude, longitude, airmass_model : str, default 'kastenyoung1989' Passed to atmosphere.relativeairmass. - altitude : None or float, default None - If None, computed from pressure. Assumed to be 0 m - if pressure is also None. + altitude : float, optional + If not specified, computed from ``pressure``. Assumed to be 0 m + if ``pressure`` is also unspecified. - pressure : None or float, default None - If None, computed from altitude. Assumed to be 101325 Pa - if altitude is also None. + pressure : float, optional + If not specified, computed from ``altitude``. Assumed to be 101325 Pa + if ``altitude`` is also unspecified. **kwargs Arbitrary keyword arguments. @@ -471,35 +471,35 @@ class ModelChain: airmass_model : str, default 'kastenyoung1989' Passed to location.get_airmass. - dc_model: None, str, or function, default None - If None, the model will be inferred from the parameters that + dc_model : str, or function, optional + If not specified, the model will be inferred from the parameters that are common to all of system.arrays[i].module_parameters. Valid strings are 'sapm', 'desoto', 'cec', 'pvsyst', 'pvwatts'. The ModelChain instance will be passed as the first argument to a user-defined function. - ac_model: None, str, or function, default None - If None, the model will be inferred from the parameters that + ac_model : str, or function, optional + If not specified, the model will be inferred from the parameters that are common to all of system.inverter_parameters. Valid strings are 'sandia', 'adr', 'pvwatts'. The ModelChain instance will be passed as the first argument to a user-defined function. - aoi_model: None, str, or function, default None - If None, the model will be inferred from the parameters that + aoi_model : str, or function, optional + If not specified, the model will be inferred from the parameters that are common to all of system.arrays[i].module_parameters. Valid strings are 'physical', 'ashrae', 'sapm', 'martin_ruiz', 'interp' and 'no_loss'. The ModelChain instance will be passed as the first argument to a user-defined function. - spectral_model: None, str, or function, default None - If None, the model will be inferred from the parameters that + spectral_model : str, or function, optional + If not specified, the model will be inferred from the parameters that are common to all of system.arrays[i].module_parameters. Valid strings are 'sapm', 'first_solar', 'no_loss'. The ModelChain instance will be passed as the first argument to a user-defined function. - temperature_model: None, str or function, default None + temperature_model : str or function, optional Valid strings are: 'sapm', 'pvsyst', 'faiman', 'fuentes', 'noct_sam'. The ModelChain instance will be passed as the first argument to a user-defined function. @@ -513,7 +513,7 @@ class ModelChain: Valid strings are 'pvwatts', 'no_loss'. The ModelChain instance will be passed as the first argument to a user-defined function. - name: None or str, default None + name : str, optional Name of ModelChain instance. """ @@ -574,7 +574,7 @@ def with_pvwatts(cls, system, location, airmass_model : str, default 'kastenyoung1989' Passed to location.get_airmass. - name: None or str, default None + name : str, optional Name of ModelChain instance. **kwargs @@ -672,7 +672,7 @@ def with_sapm(cls, system, location, airmass_model : str, default 'kastenyoung1989' Passed to location.get_airmass. - name: None or str, default None + name : str, optional Name of ModelChain instance. **kwargs diff --git a/pvlib/pvsystem.py b/pvlib/pvsystem.py index f0c1cd2cda..7b2f662b13 100644 --- a/pvlib/pvsystem.py +++ b/pvlib/pvsystem.py @@ -129,29 +129,29 @@ class PVSystem: Azimuth angle of the module surface. North=0, East=90, South=180, West=270. - albedo : None or float, default None - Ground surface albedo. If ``None``, then ``surface_type`` is used + albedo : float, optional + Ground surface albedo. If not supplied, then ``surface_type`` is used to look up a value in ``irradiance.SURFACE_ALBEDOS``. - If ``surface_type`` is also None then a ground surface albedo + If ``surface_type`` is also not supplied then a ground surface albedo of 0.25 is used. - surface_type : None or string, default None + surface_type : string, optional The ground surface type. See ``irradiance.SURFACE_ALBEDOS`` for valid values. - module : None or string, default None + module : string, optional The model name of the modules. May be used to look up the module_parameters dictionary via some other method. - module_type : None or string, default 'glass_polymer' + module_type : string, default 'glass_polymer' Describes the module's construction. Valid strings are 'glass_polymer' and 'glass_glass'. Used for cell and module temperature calculations. - module_parameters : None, dict or Series, default None + module_parameters : dict or Series, optional Module parameters as defined by the SAPM, CEC, or other. - temperature_model_parameters : None, dict or Series, default None. + temperature_model_parameters : dict or Series, optional Temperature model parameters as required by one of the models in pvlib.temperature (excluding poa_global, temp_air and wind_speed). @@ -161,22 +161,22 @@ class PVSystem: strings_per_inverter: int or float, default 1 See system topology discussion above. - inverter : None or string, default None + inverter : string, optional The model name of the inverters. May be used to look up the inverter_parameters dictionary via some other method. - inverter_parameters : None, dict or Series, default None + inverter_parameters : dict or Series, optional Inverter parameters as defined by the SAPM, CEC, or other. - racking_model : None or string, default 'open_rack' + racking_model : string, default 'open_rack' Valid strings are 'open_rack', 'close_mount', and 'insulated_back'. Used to identify a parameter set for the SAPM cell temperature model. - losses_parameters : None, dict or Series, default None + losses_parameters : dict or Series, optional Losses parameters as defined by PVWatts or other. - name : None or string, default None + name : string, optional **kwargs Arbitrary keyword arguments. @@ -328,12 +328,11 @@ def get_irradiance(self, solar_zenith, solar_azimuth, dni, ghi, dhi, Global horizontal irradiance. [W/m2] dhi : float or Series or tuple of float or Series Diffuse horizontal irradiance. [W/m2] - dni_extra : None, float, Series or tuple of float or Series,\ - default None + dni_extra : float, Series or tuple of float or Series, optional Extraterrestrial direct normal irradiance. [W/m2] - airmass : None, float or Series, default None + airmass : float or Series, optional Airmass. [unitless] - albedo : None, float or Series, default None + albedo : float or Series, optional Ground surface albedo. [unitless] model : String, default 'haydavies' Irradiance model. @@ -906,29 +905,29 @@ class Array: single axis tracker. Mounting is used to determine module orientation. If not provided, a FixedMount with zero tilt is used. - albedo : None or float, default None - Ground surface albedo. If ``None``, then ``surface_type`` is used + albedo : float, optional + Ground surface albedo. If not supplied, then ``surface_type`` is used to look up a value in ``irradiance.SURFACE_ALBEDOS``. - If ``surface_type`` is also None then a ground surface albedo + If ``surface_type`` is also not supplied then a ground surface albedo of 0.25 is used. - surface_type : None or string, default None + surface_type : string, optional The ground surface type. See ``irradiance.SURFACE_ALBEDOS`` for valid values. - module : None or string, default None + module : string, optional The model name of the modules. May be used to look up the module_parameters dictionary via some other method. - module_type : None or string, default None + module_type : string, optional Describes the module's construction. Valid strings are 'glass_polymer' and 'glass_glass'. Used for cell and module temperature calculations. - module_parameters : None, dict or Series, default None + module_parameters : dict or Series, optional Parameters for the module model, e.g., SAPM, CEC, or other. - temperature_model_parameters : None, dict or Series, default None. + temperature_model_parameters : dict or Series, optional Parameters for the module temperature model, e.g., SAPM, Pvsyst, or other. @@ -938,10 +937,10 @@ class Array: strings: int, default 1 Number of parallel strings in the array. - array_losses_parameters: None, dict or Series, default None. + array_losses_parameters : dict or Series, optional Supported keys are 'dc_ohmic_percent'. - name: None or str, default None + name : str, optional Name of Array instance. """ @@ -1095,11 +1094,11 @@ def get_irradiance(self, solar_zenith, solar_azimuth, dni, ghi, dhi, Global horizontal irradiance dhi : float or Series Diffuse horizontal irradiance. [W/m2] - dni_extra : None, float or Series, default None + dni_extra : float or Series, optional Extraterrestrial direct normal irradiance. [W/m2] - airmass : None, float or Series, default None + airmass : float or Series, optional Airmass. [unitless] - albedo : None, float or Series, default None + albedo : float or Series, optional Ground surface albedo. [unitless] model : String, default 'haydavies' Irradiance model. @@ -1536,10 +1535,10 @@ def calcparams_desoto(effective_irradiance, temp_cell, the SAM CEC module database, dEgdT=-0.0002677 is implicit for all cell types in the parameter estimation algorithm used by NREL. - irrad_ref : float (optional, default=1000) + irrad_ref : float, default 1000 Reference irradiance in W/m^2. - temp_ref : float (optional, default=25) + temp_ref : float, default 25 Reference cell temperature in C. Returns @@ -1752,10 +1751,10 @@ def calcparams_cec(effective_irradiance, temp_cell, the SAM CEC module database, dEgdT=-0.0002677 is implicit for all cell types in the parameter estimation algorithm used by NREL. - irrad_ref : float (optional, default=1000) + irrad_ref : float, default 1000 Reference irradiance in W/m^2. - temp_ref : float (optional, default=25) + temp_ref : float, default 25 Reference cell temperature in C. Returns @@ -1869,10 +1868,10 @@ def calcparams_pvsyst(effective_irradiance, temp_cell, The energy bandgap at reference temperature in units of eV. 1.121 eV for crystalline silicon. EgRef must be >0. - irrad_ref : float (optional, default=1000) + irrad_ref : float, default 1000 Reference irradiance in W/m^2. - temp_ref : float (optional, default=25) + temp_ref : float, default 25 Reference cell temperature in C. Returns @@ -1974,7 +1973,7 @@ def retrieve_sam(name=None, path=None): Parameters ---------- - name : None or string, default None + name : string, optional Name can be one of: * 'CECMod' - returns the CEC module database @@ -1985,7 +1984,7 @@ def retrieve_sam(name=None, path=None): * 'SandiaMod' - returns the Sandia Module database * 'ADRInverter' - returns the ADR Inverter database - path : None or string, default None + path : string, optional Path to the SAM file. May also be a URL. Returns @@ -2395,9 +2394,9 @@ def singlediode(photocurrent, saturation_current, resistance_series, junction in Kelvin, and :math:`q` is the charge of an electron (coulombs). ``0 < nNsVth``. [V] - ivcurve_pnts : None or int, default None - Number of points in the desired IV curve. If None or 0, no points on - the IV curves will be produced. + ivcurve_pnts : int, optional + Number of points in the desired IV curve. If not specified or 0, no + points on the IV curves will be produced. .. deprecated:: 0.10.0 Use :py:func:`pvlib.pvsystem.v_from_i` and diff --git a/pvlib/scaling.py b/pvlib/scaling.py index dca2ca4935..7fbc0a46d1 100644 --- a/pvlib/scaling.py +++ b/pvlib/scaling.py @@ -30,7 +30,7 @@ def wvm(clearsky_index, positions, cloud_speed, dt=None): cloud_speed : numeric Speed of cloud movement in meters per second [m/s]. - dt : float, default None + dt : float, optional The time series time delta. By default, is inferred from the clearsky_index. Must be specified for a time series that doesn't include an index. Units of seconds [s]. @@ -216,7 +216,7 @@ def _compute_wavelet(clearsky_index, dt=None): clearsky_index : numeric or pandas.Series Clear Sky Index time series that will be smoothed. - dt : float, default None + dt : float, optional The time series time delta. By default, is inferred from the clearsky_index. Must be specified for a time series that doesn't include an index. Units of seconds [s]. diff --git a/pvlib/shading.py b/pvlib/shading.py index 1533d2a013..c0a7a91f18 100644 --- a/pvlib/shading.py +++ b/pvlib/shading.py @@ -85,7 +85,7 @@ def masking_angle(surface_tilt, gcr, slant_height): ---------- .. [1] D. Passias and B. Källbäck, "Shading effects in rows of solar cell panels", Solar Cells, Volume 11, Pages 281-291. 1984. - DOI: 10.1016/0379-6787(84)90017-6 + :doi:`10.1016/0379-6787(84)90017-6` .. [2] Gilman, P. et al., (2018). "SAM Photovoltaic Model Technical Reference Update", NREL Technical Report NREL/TP-6A20-67399. Available at https://www.nrel.gov/docs/fy18osti/67399.pdf @@ -167,7 +167,7 @@ def masking_angle_passias(surface_tilt, gcr): ---------- .. [1] D. Passias and B. Källbäck, "Shading effects in rows of solar cell panels", Solar Cells, Volume 11, Pages 281-291. 1984. - DOI: 10.1016/0379-6787(84)90017-6 + :doi:`10.1016/0379-6787(84)90017-6` """ # wrap it in an array so that division by zero is handled well beta = np.radians(np.array(surface_tilt)) @@ -226,7 +226,7 @@ def sky_diffuse_passias(masking_angle): ---------- .. [1] D. Passias and B. Källbäck, "Shading effects in rows of solar cell panels", Solar Cells, Volume 11, Pages 281-291. 1984. - DOI: 10.1016/0379-6787(84)90017-6 + :doi:`10.1016/0379-6787(84)90017-6` .. [2] Gilman, P. et al., (2018). "SAM Photovoltaic Model Technical Reference Update", NREL Technical Report NREL/TP-6A20-67399. Available at https://www.nrel.gov/docs/fy18osti/67399.pdf diff --git a/pvlib/soiling.py b/pvlib/soiling.py index bbad4862f4..bcdaf97b58 100644 --- a/pvlib/soiling.py +++ b/pvlib/soiling.py @@ -58,7 +58,7 @@ def hsu(rainfall, cleaning_threshold, surface_tilt, pm2_5, pm10, ----------- .. [1] M. Coello and L. Boyle, "Simple Model For Predicting Time Series Soiling of Photovoltaic Panels," in IEEE Journal of Photovoltaics. - doi: 10.1109/JPHOTOV.2019.2919628 + :doi:`10.1109/JPHOTOV.2019.2919628` .. [2] Atmospheric Chemistry and Physics: From Air Pollution to Climate Change. J. Seinfeld and S. Pandis. Wiley and Sons 2001. @@ -128,7 +128,7 @@ def kimber(rainfall, cleaning_threshold=6, soiling_loss_rate=0.0015, max_soiling : float, default 0.3 Maximum fraction of energy lost due to soiling. Soiling will build up until this value. [unitless] - manual_wash_dates : sequence or None, default None + manual_wash_dates : sequence, optional List or tuple of dates as Python ``datetime.date`` when the panels were washed manually. Note there is no grace period after a manual wash, so soiling begins to build up immediately. diff --git a/pvlib/solarposition.py b/pvlib/solarposition.py index cdcacd7ec6..38ffa9e51c 100644 --- a/pvlib/solarposition.py +++ b/pvlib/solarposition.py @@ -51,13 +51,13 @@ def get_solarposition(time, latitude, longitude, Longitude in decimal degrees. Positive east of prime meridian, negative to west. - altitude : None or float, default None - If None, computed from pressure. Assumed to be 0 m - if pressure is also None. + altitude : float, optional + If not specified, computed from ``pressure``. Assumed to be 0 m + if ``pressure`` is not supplied. - pressure : None or float, default None - If None, computed from altitude. Assumed to be 101325 Pa - if altitude is also None. + pressure : float, optional + If not specified, computed from ``altitude``. Assumed to be 101325 Pa + if ``altitude`` is not supplied. method : string, default 'nrel_numpy' 'nrel_numpy' uses an implementation of the NREL SPA algorithm @@ -312,7 +312,7 @@ def spa_python(time, latitude, longitude, *Note: delta_t = None will break code using nrel_numba, this will be fixed in a future version.* The USNO has historical and forecasted delta_t [3]_. - atmos_refrac : None or float, optional, default None + atmos_refrac : float, optional The approximate atmospheric refraction (in degrees) at sunrise and sunset. how : str, optional, default 'numpy' diff --git a/pvlib/spectrum/mismatch.py b/pvlib/spectrum/mismatch.py index 10f8db3564..e51cdf8625 100644 --- a/pvlib/spectrum/mismatch.py +++ b/pvlib/spectrum/mismatch.py @@ -115,7 +115,7 @@ def get_am15g(wavelength=None): References ---------- .. [1] ASTM "G173-03 Standard Tables for Reference Solar Spectral - Irradiances: Direct Normal and Hemispherical on 37° Tilted Surface." + Irradiances: Direct Normal and Hemispherical on 37° Tilted Surface." ''' # Contributed by Anton Driesse (@adriesse), PV Performance Labs. Aug. 2022 diff --git a/pvlib/spectrum/spectrl2.py b/pvlib/spectrum/spectrl2.py index f679825014..1c1f102fec 100644 --- a/pvlib/spectrum/spectrl2.py +++ b/pvlib/spectrum/spectrl2.py @@ -270,7 +270,7 @@ def spectrl2(apparent_zenith, aoi, surface_tilt, ground_albedo, .. [1] Bird, R, and Riordan, C., 1984, "Simple solar spectral model for direct and diffuse irradiance on horizontal and tilted planes at the earth's surface for cloudless atmospheres", NREL Technical Report - TR-215-2436 doi:10.2172/5986936. + TR-215-2436 :doi:`10.2172/5986936`. .. [2] Bird Simple Spectral Model: spectrl2_2.c. https://www.nrel.gov/grid/solar-resource/spectral.html """ diff --git a/pvlib/temperature.py b/pvlib/temperature.py index 63cf4baf79..6901b4e5e0 100644 --- a/pvlib/temperature.py +++ b/pvlib/temperature.py @@ -721,7 +721,7 @@ def fuentes(poa_global, temp_air, wind_speed, noct_installed, module_height=5, http://prod.sandia.gov/techlib/access-control.cgi/1985/850330.pdf .. [2] Dobos, A. P., 2014, "PVWatts Version 5 Manual", NREL/TP-6A20-62641, National Renewable Energy Laboratory, Golden CO. - doi:10.2172/1158421. + :doi:`10.2172/1158421`. """ # ported from the FORTRAN77 code provided in Appendix A of Fuentes 1987; # nearly all variable names are kept the same for ease of comparison. @@ -872,8 +872,8 @@ def noct_sam(poa_global, temp_air, wind_speed, noct, module_efficiency, :math:`\eta_{m} = \frac{V_{mp} I_{mp}}{A \times 1000 W/m^2}` where A is module area [m^2]. - effective_irradiance : numeric, default None. - The irradiance that is converted to photocurrent. If None, + effective_irradiance : numeric, optional + The irradiance that is converted to photocurrent. If not specified, assumed equal to poa_global. [W/m^2] transmittance_absorptance : numeric, default 0.9 From c18a00413fde8021055f8709bbc67ed9789bf9f8 Mon Sep 17 00:00:00 2001 From: Anton Driesse <anton.driesse@pvperformancelabs.com> Date: Tue, 12 Dec 2023 16:03:46 +0000 Subject: [PATCH 3/6] Implement reverse transposition using Perez-Driesse forward transposition (#1907) * Add reverse transposition function and two helpers. * Add missing import. * Add to docs under transposition until a better place is found/made. * Minor doc string fixes. * Add full_output option similar to newton(). * First example for reverse transposition. * Second example for reverse transposition. * Some improvements to the two examples. * Placate flake8. * Add tests for reverse transpostion. * Refine examples and fix test. * Update whatsnew. * Refine examples. * Try to get rid of matplotlib warning in example. * Remove unused import. * Update pvlib/irradiance.py Co-authored-by: Cliff Hansen <cwhanse@sandia.gov> * Improve examples based on reviews. * Settle conflict. * Try again. * Remove one space. * Final(?) changes. * Update reference in erbs_driesse(). * Fix links in examples. * Update docs/examples/irradiance-transposition/plot_rtranpose_limitations.py Co-authored-by: Adam R. Jensen <39184289+AdamRJensen@users.noreply.github.com> * Address review comments. * Final renames. --------- Co-authored-by: Cliff Hansen <cwhanse@sandia.gov> Co-authored-by: Adam R. Jensen <39184289+AdamRJensen@users.noreply.github.com> --- .../plot_rtranpose_limitations.py | 181 ++++++++++++++++++ .../plot_rtranpose_year.py | 152 +++++++++++++++ .../reference/irradiance/transposition.rst | 1 + docs/sphinx/source/whatsnew/v0.10.3.rst | 5 + pvlib/irradiance.py | 176 ++++++++++++++++- pvlib/tests/test_irradiance.py | 44 +++++ 6 files changed, 554 insertions(+), 5 deletions(-) create mode 100644 docs/examples/irradiance-transposition/plot_rtranpose_limitations.py create mode 100644 docs/examples/irradiance-transposition/plot_rtranpose_year.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..8df8339ad4 --- /dev/null +++ b/docs/examples/irradiance-transposition/plot_rtranpose_limitations.py @@ -0,0 +1,181 @@ +""" +Reverse transposition limitations +==================================== + +Unfortunately, sometimes there is not a unique solution. + +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." +# +# 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:meth:`pvlib.irradiance.ghi_from_poa_driesse_2023` to estimate +# the original GHI from POA global. +# +# 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` +# + +import numpy as np + +import matplotlib +import matplotlib.pyplot as plt + +from pvlib.irradiance import (erbs_driesse, + get_total_irradiance, + ghi_from_poa_driesse_2023, + ) + +matplotlib.rcParams['axes.grid'] = True + +# %% +# +# Define the conditions that were used for figure 3 in [1]_. +# + +dni_extra = 1366.1 +albedo = 0.25 +surface_tilt = 40 +surface_azimuth = 180 + +solar_azimuth = 82 +solar_zenith = 75 + +# %% +# +# 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'] +dhi = erbsout['dhi'] + +irrads = get_total_irradiance(surface_tilt, surface_azimuth, + solar_zenith, solar_azimuth, + dni, ghi, dhi, + dni_extra, + model='perez-driesse') + +poa_global = irrads['poa_global'] + +# %% +# +# Suppose you measure that POA global is 200 W/m2. What would GHI be? +# + +poa_test = 200 + +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) + +# %% +# +# Show this result on the graph of all possible combinations of GHI and POA. +# + +plt.figure() +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', + arrowprops={'arrowstyle': 'simple'}) +plt.xlim(0, 500) +plt.ylim(0, 250) +plt.xlabel('GHI [W/m²]') +plt.ylabel('POA [W/m²]') +plt.show() + +# %% +# +# Now change the solar azimuth to match the conditions for figure 4 in [1]_. +# + +solar_azimuth = 76 + +# %% +# +# 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) + +dni = erbsout['dni'] +dhi = erbsout['dhi'] + +irrads = get_total_irradiance(surface_tilt, surface_azimuth, + solar_zenith, solar_azimuth, + dni, ghi, dhi, + dni_extra, + model='perez-driesse') + +poa_global = irrads['poa_global'] + +# %% +# +# 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, 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 = 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) + +plt.figure() +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') + +plt.xlim(0, 500) +plt.ylim(0, 250) +plt.xlabel('GHI [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 new file mode 100644 index 0000000000..c0b9860bba --- /dev/null +++ b/docs/examples/irradiance-transposition/plot_rtranpose_year.py @@ -0,0 +1,152 @@ +""" +Reverse transposition using one year of hourly data +=================================================== + +With a brief look at accuracy and speed. + +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: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 +import pandas as pd + +import matplotlib.pyplot as plt + +import pvlib +from pvlib import iotools, location +from pvlib.irradiance import (get_extra_radiation, + get_total_irradiance, + ghi_from_poa_driesse_2023, + aoi, + ) + +# %% +# +# 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 the timestamps to the middle of the hour and 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 and oriented 30 degrees east of south. +# + +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. +# 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'] = 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)) + +# %% +# +# 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. +# +# 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') + +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 [°]') +pc.set_alpha(0.5) + +plt.xlabel('GHI original [W/m²]') +plt.ylabel('GHI from POA [W/m²]') + +plt.show() diff --git a/docs/sphinx/source/reference/irradiance/transposition.rst b/docs/sphinx/source/reference/irradiance/transposition.rst index 7b3624e692..22136f0c58 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.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 007eb8d34b..63f7b90b84 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.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 shaded fraction in returned variables. (:pull:`1871`) @@ -27,8 +30,10 @@ Documentation ~~~~~~~~~~~~~ * Create :ref:`weatherdata` User's Guide page. (:pull:`1754`) * 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`) + Requirements ~~~~~~~~~~~~ * Minimum version of scipy advanced from 1.4.0 to 1.5.0. (:issue:`1918`, :pull:`1919`) diff --git a/pvlib/irradiance.py b/pvlib/irradiance.py index acccf97ba5..174a88e6a0 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 @@ -1381,9 +1382,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,6 +1446,170 @@ def perez_driesse(surface_tilt, surface_azimuth, dhi, dni, dni_extra, return sky_diffuse +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 ghi_from_poa_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 _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 ghi_from_poa_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 = _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 + 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 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 + 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] + 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] + 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 + ----- + Since :py:func:`scipy.optimize.bisect` is not vectorized, high-resolution + time series can be quite slow to process. + + 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` + + See also + -------- + perez_driesse + erbs_driesse + gti_dirint + ''' + # Contributed by Anton Driesse (@adriesse), PV Performance Labs. Nov., 2023 + + ghi_from_poa_array = np.vectorize(_ghi_from_poa) + + 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) + conv = pd.Series(conv, poa_global.index) + niter = pd.Series(niter, poa_global.index) + + if full_output: + return ghi, conv, niter + else: + return ghi + + def clearsky_index(ghi, clearsky_ghi, max_clearsky_index=2.0): """ Calculate the clearsky index. @@ -2568,8 +2733,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 diff --git a/pvlib/tests/test_irradiance.py b/pvlib/tests/test_irradiance.py index ba66f4dc36..55fef490ba 100644 --- a/pvlib/tests/test_irradiance.py +++ b/pvlib/tests/test_irradiance.py @@ -782,6 +782,50 @@ def test_dirint_min_cos_zenith_max_zenith(): assert_series_equal(out, expected, check_less_precise=True) +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']) + poa_global = np.array([20, 300, 1000]) + zenith = np.array([80, 45, 20]) + azimuth = np.array([90, 135, 180]) + surface_tilt = 30 + surface_azimuth = 180 + + # test core function + output = irradiance.ghi_from_poa_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.ghi_from_poa_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.ghi_from_poa_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 f430a745528d5d645024bb6b4b6d82ee350043ad Mon Sep 17 00:00:00 2001 From: GillesFischerV <147727952+GillesFischerV@users.noreply.github.com> Date: Tue, 12 Dec 2023 20:29:34 +0100 Subject: [PATCH 4/6] Fix CAMS message error handler (#1905) * Fix CAMS message error handler - Issue#1799 * Fix CAMS message error handler - Issue#1799 * fix Python Flake8 Linter error * Fix associated UT and add contribution in WhatsNews * Fix line code length in test_sodapro.py * Fix typo in contributor ghuser * Update geographical coverage description * correction in maximum longitude available for CAMS Radiation * revert of last change * Add doc string changes from adriesse Co-authored-by: Anton Driesse <anton.driesse@pvperformancelabs.com> * Flake8 correction --------- Co-authored-by: Adam R. Jensen <39184289+AdamRJensen@users.noreply.github.com> Co-authored-by: Anton Driesse <anton.driesse@pvperformancelabs.com> --- docs/sphinx/source/whatsnew/v0.10.3.rst | 5 ++++- pvlib/iotools/sodapro.py | 21 ++++++++++++++------- pvlib/tests/iotools/test_sodapro.py | 9 +++++---- 3 files changed, 23 insertions(+), 12 deletions(-) diff --git a/docs/sphinx/source/whatsnew/v0.10.3.rst b/docs/sphinx/source/whatsnew/v0.10.3.rst index 63f7b90b84..2a466cdb4b 100644 --- a/docs/sphinx/source/whatsnew/v0.10.3.rst +++ b/docs/sphinx/source/whatsnew/v0.10.3.rst @@ -18,6 +18,8 @@ Enhancements Bug fixes ~~~~~~~~~ +* Fixed CAMS error message handler in + :py:func:`pvlib.iotools.get_cams` (:issue:`1799`, :pull:`1905`) * Fix mapping of the dew point column to ``temp_dew`` when ``map_variables`` is True in :py:func:`pvlib.iotools.get_psm3`. (:pull:`1920`) @@ -45,7 +47,8 @@ Contributors * Miguel Sánchez de León Peque (:ghuser:`Peque`) * Will Hobbs (:ghuser:`williamhobbs`) * Anton Driesse (:ghuser:`adriesse`) +* Gilles Fischer (:ghuser:`GillesFischerV`) +* Adam R. Jensen (:ghusuer:`AdamRJensen`) * :ghuser:`matsuobasho` * Harry Jack (:ghuser:`harry-solcast`) -* Adam R. Jensen (:ghuser:`AdamRJensen`) * Kevin Anderson (:ghuser:`kandersolar`) diff --git a/pvlib/iotools/sodapro.py b/pvlib/iotools/sodapro.py index a6c43ad341..b9922af4b8 100644 --- a/pvlib/iotools/sodapro.py +++ b/pvlib/iotools/sodapro.py @@ -57,8 +57,10 @@ def get_cams(latitude, longitude, start, end, email, identifier='mcclear', Access: free, but requires registration, see [2]_ Requests: max. 100 per day + Geographical coverage: worldwide for CAMS McClear and approximately -66° to - 66° in both latitude and longitude for CAMS Radiation. + 66° in latitude and -66° to 180° in longitude for CAMS Radiation. See [3]_ + for a map of the geographical coverage. Parameters ---------- @@ -157,6 +159,9 @@ def get_cams(latitude, longitude, start, end, email, identifier='mcclear', <https://atmosphere.copernicus.eu/solar-radiation>`_ .. [2] `CAMS Radiation Automatic Access (SoDa) <https://www.soda-pro.com/help/cams-services/cams-radiation-service/automatic-access>`_ + .. [3] A. R. Jensen et al., pvlib iotools — Open-source Python functions + for seamless access to solar irradiance data. Solar Energy. 2023. Vol + 266, pp. 112092. :doi:`10.1016/j.solener.2023.112092` """ try: time_step_str = TIME_STEPS_MAP[time_step] @@ -215,14 +220,16 @@ def get_cams(latitude, longitude, start, end, email, identifier='mcclear', res = requests.get(base_url + '?DataInputs=' + data_inputs, params=params, timeout=timeout) - # Invalid requests returns an XML error message and the HTTP staus code 200 - # as if the request was successful. Therefore, errors cannot be handled - # automatic (e.g. res.raise_for_status()) and errors are handled manually - if res.headers['Content-Type'] == 'application/xml': + # Response from CAMS follows the status and reason format of PyWPS4 + # If an error occurs on server side, it will return error 400 - bad request + # Additional information is available in the response text, so it is added + # to the error displayed to facilitate users effort to fix their request + if not res.ok: errors = res.text.split('ows:ExceptionText')[1][1:-2] - raise requests.HTTPError(errors, response=res) + res.reason = "%s: <%s>" % (res.reason, errors) + res.raise_for_status() # Successful requests returns a csv data file - elif res.headers['Content-Type'] == 'application/csv': + else: fbuf = io.StringIO(res.content.decode('utf-8')) data, metadata = parse_cams(fbuf, integrated=integrated, label=label, map_variables=map_variables) diff --git a/pvlib/tests/iotools/test_sodapro.py b/pvlib/tests/iotools/test_sodapro.py index ff17691a98..4729cf2c64 100644 --- a/pvlib/tests/iotools/test_sodapro.py +++ b/pvlib/tests/iotools/test_sodapro.py @@ -248,7 +248,7 @@ def test_get_cams_bad_request(requests_mock): requests inputs. Also tests if the specified server url gets used""" # Subset of an xml file returned for errornous requests - mock_response_bad = """<?xml version="1.0" encoding="utf-8"?> + mock_response_bad_text = """<?xml version="1.0" encoding="utf-8"?> <ows:Exception exceptionCode="NoApplicableCode" locator="None"> <ows:ExceptionText>Failed to execute WPS process [get_mcclear]: Please, register yourself at www.soda-pro.com @@ -256,12 +256,13 @@ def test_get_cams_bad_request(requests_mock): url_cams_bad_request = 'https://pro.soda-is.com/service/wps?DataInputs=latitude=55.7906;longitude=12.5251;altitude=-999;date_begin=2020-01-01;date_end=2020-05-04;time_ref=TST;summarization=PT01H;username=test%2540test.com;verbose=false&Service=WPS&Request=Execute&Identifier=get_mcclear&version=1.0.0&RawDataOutput=irradiation' # noqa: E501 - requests_mock.get(url_cams_bad_request, text=mock_response_bad, - headers={'Content-Type': 'application/xml'}) + requests_mock.get(url_cams_bad_request, status_code=400, + text=mock_response_bad_text) # Test if HTTPError is raised if incorrect input is specified # In the below example a non-registrered email is specified - with pytest.raises(requests.HTTPError, match='Failed to execute WPS'): + with pytest.raises(requests.exceptions.HTTPError, + match='Failed to execute WPS process'): _ = sodapro.get_cams( start=pd.Timestamp('2020-01-01'), end=pd.Timestamp('2020-05-04'), From 304fbb5e8fa3db29d065b792c5a9c5cd56a5842f Mon Sep 17 00:00:00 2001 From: Kevin Anderson <kevin.anderso@gmail.com> Date: Tue, 12 Dec 2023 14:30:57 -0500 Subject: [PATCH 5/6] Add python 3.12 to CI (#1886) * Create requirements-py3.12.yml * add 3.12 to GH Actions test workflows * whatsnew * nix solarfactors * fix yaml syntax * increase min scipy to 1.5; update comments --- .github/workflows/pytest-remote-data.yml | 2 +- .github/workflows/pytest.yml | 2 +- ci/requirements-py3.12.yml | 28 ++++++++++++++++++++++++ docs/sphinx/source/whatsnew/v0.10.3.rst | 2 +- 4 files changed, 31 insertions(+), 3 deletions(-) create mode 100644 ci/requirements-py3.12.yml diff --git a/.github/workflows/pytest-remote-data.yml b/.github/workflows/pytest-remote-data.yml index 7e32486918..0603f632da 100644 --- a/.github/workflows/pytest-remote-data.yml +++ b/.github/workflows/pytest-remote-data.yml @@ -56,7 +56,7 @@ jobs: strategy: fail-fast: false # don't cancel other matrix jobs when one fails matrix: - python-version: [3.7, 3.8, 3.9, "3.10", "3.11"] + python-version: [3.7, 3.8, 3.9, "3.10", "3.11", "3.12"] suffix: [''] # the alternative to "-min" include: - python-version: 3.7 diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml index 5185734e72..0b618d9f92 100644 --- a/.github/workflows/pytest.yml +++ b/.github/workflows/pytest.yml @@ -12,7 +12,7 @@ jobs: fail-fast: false # don't cancel other matrix jobs when one fails matrix: os: [ubuntu-latest, macos-latest, windows-latest] - python-version: [3.7, 3.8, 3.9, "3.10", "3.11"] + python-version: [3.7, 3.8, 3.9, "3.10", "3.11", "3.12"] environment-type: [conda, bare] suffix: [''] # placeholder as an alternative to "-min" include: diff --git a/ci/requirements-py3.12.yml b/ci/requirements-py3.12.yml new file mode 100644 index 0000000000..69632b3ac1 --- /dev/null +++ b/ci/requirements-py3.12.yml @@ -0,0 +1,28 @@ +name: test_env +channels: + - defaults + - conda-forge +dependencies: + - coveralls + - cython + - ephem + - h5py + # - numba # not available for 3.12 as of 2023-12-12 + - numpy >= 1.16.0 + - pandas >= 0.25.0 + - pip + - pytest + - pytest-cov + - pytest-mock + - requests-mock + - pytest-timeout + - pytest-rerunfailures + - pytest-remotedata + - python=3.12 + - pytz + - requests + - scipy >= 1.5.0 + - statsmodels + # - pip: + # - nrel-pysam>=2.0 # not available for 3.12 as of 2023-12-12 + # - solarfactors # required shapely<2 isn't available for 3.12 diff --git a/docs/sphinx/source/whatsnew/v0.10.3.rst b/docs/sphinx/source/whatsnew/v0.10.3.rst index 2a466cdb4b..c8cfaaf86b 100644 --- a/docs/sphinx/source/whatsnew/v0.10.3.rst +++ b/docs/sphinx/source/whatsnew/v0.10.3.rst @@ -26,7 +26,7 @@ Bug fixes Testing ~~~~~~~ * Replace use of deprecated ``pkg_resources``. (:issue:`1881`, :pull:`1882`) - +* Added Python 3.12 to test suite. (:pull:`1886`) Documentation ~~~~~~~~~~~~~ From ae848177c7cafae78e4a28a7f9871c29ac484f92 Mon Sep 17 00:00:00 2001 From: Cliff Hansen <cwhanse@sandia.gov> Date: Thu, 14 Dec 2023 11:06:07 -0700 Subject: [PATCH 6/6] Fix and document clearsky_model for ModelChain (#1924) * add note about clearsky_model * whatsnew * pass clearsky_model to get_clearsky * add bug fix note * test for correct argument * test for correct argument * test for correct argument --- docs/sphinx/source/whatsnew/v0.10.3.rst | 4 ++++ pvlib/modelchain.py | 6 ++++-- pvlib/tests/test_modelchain.py | 6 +++++- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/docs/sphinx/source/whatsnew/v0.10.3.rst b/docs/sphinx/source/whatsnew/v0.10.3.rst index c8cfaaf86b..4d0cc774c8 100644 --- a/docs/sphinx/source/whatsnew/v0.10.3.rst +++ b/docs/sphinx/source/whatsnew/v0.10.3.rst @@ -22,6 +22,8 @@ Bug fixes :py:func:`pvlib.iotools.get_cams` (:issue:`1799`, :pull:`1905`) * Fix mapping of the dew point column to ``temp_dew`` when ``map_variables`` is True in :py:func:`pvlib.iotools.get_psm3`. (:pull:`1920`) +* Fix :py:class:`pvlib.modelchain.ModelChain` to use attribute `clearsky_model` + (:pull:`1924`) Testing ~~~~~~~ @@ -34,6 +36,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`) +* Clarified purpose of `ModelChain.clearsky_model` (:pull:`1924`) Requirements @@ -52,3 +55,4 @@ Contributors * :ghuser:`matsuobasho` * Harry Jack (:ghuser:`harry-solcast`) * Kevin Anderson (:ghuser:`kandersolar`) +* Cliff Hansen (:ghuser:`cwhanse`) diff --git a/pvlib/modelchain.py b/pvlib/modelchain.py index cc42d59077..296356971b 100644 --- a/pvlib/modelchain.py +++ b/pvlib/modelchain.py @@ -460,7 +460,8 @@ class ModelChain: the physical location at which to evaluate the model. clearsky_model : str, default 'ineichen' - Passed to location.get_clearsky. + Passed to location.get_clearsky. Only used when DNI is not found in + the weather inputs. transposition_model : str, default 'haydavies' Passed to system.get_irradiance. @@ -1354,7 +1355,8 @@ def _complete_irradiance(self, weather): "https://github.com/pvlib/pvlib-python \n") if {'ghi', 'dhi'} <= icolumns and 'dni' not in icolumns: clearsky = self.location.get_clearsky( - weather.index, solar_position=self.results.solar_position) + weather.index, model=self.clearsky_model, + solar_position=self.results.solar_position) complete_irrad_df = pvlib.irradiance.complete_irradiance( solar_zenith=self.results.solar_position.zenith, ghi=weather.ghi, diff --git a/pvlib/tests/test_modelchain.py b/pvlib/tests/test_modelchain.py index c59a02e9c4..0632d34212 100644 --- a/pvlib/tests/test_modelchain.py +++ b/pvlib/tests/test_modelchain.py @@ -1847,7 +1847,7 @@ def test_complete_irradiance_clean_run(sapm_dc_snl_ac_system, location): pd.Series([9, 5], index=times, name='ghi')) -def test_complete_irradiance(sapm_dc_snl_ac_system, location): +def test_complete_irradiance(sapm_dc_snl_ac_system, location, mocker): """Check calculations""" mc = ModelChain(sapm_dc_snl_ac_system, location) times = pd.date_range('2010-07-05 7:00:00-0700', periods=2, freq='H') @@ -1867,7 +1867,11 @@ def test_complete_irradiance(sapm_dc_snl_ac_system, location): pd.Series([372.103976116, 497.087579068], index=times, name='ghi')) + # check that clearsky_model is used correctly + m_ineichen = mocker.spy(location, 'get_clearsky') mc.complete_irradiance(i[['dhi', 'ghi']]) + assert m_ineichen.call_count == 1 + assert m_ineichen.call_args[1]['model'] == 'ineichen' assert_series_equal(mc.results.weather['dni'], pd.Series([49.756966, 62.153947], index=times, name='dni'))