diff --git a/docs/examples/bifacial/plot_irradiance_nonuniformity_loss.py b/docs/examples/bifacial/plot_irradiance_nonuniformity_loss.py index 9b02bfb58e..8b19ebeb79 100644 --- a/docs/examples/bifacial/plot_irradiance_nonuniformity_loss.py +++ b/docs/examples/bifacial/plot_irradiance_nonuniformity_loss.py @@ -40,31 +40,30 @@ # # .. sectionauthor:: Echedey Luis +import numpy as np +import matplotlib.pyplot as plt +from matplotlib.cm import ScalarMappable +from matplotlib.colors import Normalize + +from pvlib.bifacial import power_mismatch_deline + # %% # Problem description # ------------------- # Let's set a fixed irradiance to each cell row of the PV array with the values # described in Figure 1 (A), [1]_. We will cover this case for educational # purposes, although it can be achieved with the packages -# `solarfactors `_ and -# `bifacial_radiance `_. +# :ref:`solarfactors ` and +# :ref:`bifacial_radiance `. # # Here we set and plot the global irradiance level of each cell. -import numpy as np -import matplotlib.pyplot as plt -from matplotlib.cm import ScalarMappable -from matplotlib.colors import Normalize - -from pvlib.bifacial import power_mismatch_deline - x = np.arange(12, 0, -1) y = np.arange(6, 0, -1) cells_irrad = np.repeat([1059, 976, 967, 986, 1034, 1128], len(x)).reshape( len(y), len(x) ) -# plot the irradiance levels of each cell color_map = "gray" color_norm = Normalize(930, 1150) diff --git a/docs/examples/floating-pv/plot_floating_pv_cell_temperature.py b/docs/examples/floating-pv/plot_floating_pv_cell_temperature.py index c1b7c93f97..c288b30856 100644 --- a/docs/examples/floating-pv/plot_floating_pv_cell_temperature.py +++ b/docs/examples/floating-pv/plot_floating_pv_cell_temperature.py @@ -19,6 +19,7 @@ The PVSyst model for cell temperature :math:`T_{C}` is given by: .. math:: + :label: pvsyst T_{C} = T_{a} + \frac{\alpha \cdot E \cdot (1 - \eta_{m})}{U_{c} + U_{v} \cdot WS}, diff --git a/docs/sphinx/source/_static/version-alert.js b/docs/sphinx/source/_static/version-alert.js index 09f142d3d0..e8179a6c2f 100644 --- a/docs/sphinx/source/_static/version-alert.js +++ b/docs/sphinx/source/_static/version-alert.js @@ -47,7 +47,7 @@ function warnOnLatestVersion() { "

" + "This document is for an unreleased development version. " + "Documentation is available for the current stable release, " + - "or for older versions through the “v:” menu at bottom left." + + "or for older versions through the “v:” menu at bottom right." + "

"; warning.querySelector('a').href = window.location.pathname.replace('/latest', '/stable'); diff --git a/docs/sphinx/source/user_guide/faq.rst b/docs/sphinx/source/user_guide/faq.rst index f87fa101b5..39e3bec841 100644 --- a/docs/sphinx/source/user_guide/faq.rst +++ b/docs/sphinx/source/user_guide/faq.rst @@ -123,7 +123,7 @@ If you see a function in the pvlib documentation that doesn't seem to exist in your pvlib installation, the documentation is likely for a different version of pvlib. You can check your installed pvlib version by running ``print(pvlib.__version__)`` in python. To switch documentation versions, use -the `v:` version switcher widget in the bottom left corner of this page. +the `v:` version switcher widget in the bottom right corner of this page. You can also upgrade your installed pvlib to the latest compatible version with ``pip install -U pvlib``, but be sure to check the :ref:`whatsnew` diff --git a/pvlib/tracking.py b/pvlib/tracking.py index 9c4103e7f0..41a3cd0b8a 100644 --- a/pvlib/tracking.py +++ b/pvlib/tracking.py @@ -6,9 +6,16 @@ from pvlib import shading -def singleaxis(apparent_zenith, apparent_azimuth, - axis_tilt=0, axis_azimuth=0, max_angle=90, - backtrack=True, gcr=2.0/7.0, cross_axis_tilt=0): +def singleaxis( + apparent_zenith, + apparent_azimuth, + axis_tilt=0, + axis_azimuth=0, + max_angle=90, + backtrack=True, + gcr=2.0 / 7.0, + cross_axis_tilt=0, +): """ Determine the rotation angle of a single-axis tracker when given particular solar zenith and azimuth angles. @@ -125,17 +132,20 @@ def singleaxis(apparent_zenith, apparent_azimuth, apparent_zenith = np.atleast_1d(apparent_zenith) if apparent_azimuth.ndim > 1 or apparent_zenith.ndim > 1: - raise ValueError('Input dimensions must not exceed 1') - - # The ideal tracking angle wid is the rotation to place the sun position - # vector (xp, yp, zp) in the (x, z) plane, which is normal to the panel and - # contains the axis of rotation. wid = 0 indicates that the panel is - # horizontal. Here, our convention is that a clockwise rotation is - # positive, to view rotation angles in the same frame of reference as - # azimuth. For example, for a system with tracking axis oriented south, a - # rotation toward the east is negative, and a rotation to the west is - # positive. This is a right-handed rotation around the tracker y-axis. - wid = shading.projected_solar_zenith_angle( + raise ValueError("Input dimensions must not exceed 1") + + # The ideal tracking angle omega_ideal is the rotation to place the sun + # position vector (xp, yp, zp) in the (x, z) plane, which is normal to the + # panel and contains the axis of rotation. omega_id = 0 indicates that + # the panel is horizontal. Here, our convention is that a clockwise + # rotation is positive, to view rotation angles in the same frame of + # reference as azimuth. For example, for a system with tracking axis + # oriented south, a rotation toward the east is negative, and a rotation + # to the west is positive. This is a right-handed rotation around the + # tracker y-axis. Omega_ideal is the plaintext representation of the + # ideal tracking angle from Lorenzo et al 2011. + # https://doi.org/10.1002/pip.1085 + omega_ideal = shading.projected_solar_zenith_angle( axis_tilt=axis_tilt, axis_azimuth=axis_azimuth, solar_zenith=apparent_zenith, @@ -144,28 +154,28 @@ def singleaxis(apparent_zenith, apparent_azimuth, # filter for sun above panel horizon zen_gt_90 = apparent_zenith > 90 - wid[zen_gt_90] = np.nan + omega_ideal[zen_gt_90] = np.nan # Account for backtracking if backtrack: # distance between rows in terms of rack lengths relative to cross-axis # tilt - axes_distance = 1/(gcr * cosd(cross_axis_tilt)) + axes_distance = 1 / (gcr * cosd(cross_axis_tilt)) # NOTE: account for rare angles below array, see GH 824 - temp = np.abs(axes_distance * cosd(wid - cross_axis_tilt)) + temp = np.abs(axes_distance * cosd(omega_ideal - cross_axis_tilt)) # backtrack angle using [1], Eq. 14 - with np.errstate(invalid='ignore'): - wc = np.degrees(-np.sign(wid)*np.arccos(temp)) + with np.errstate(invalid="ignore"): + wc = np.degrees(-np.sign(omega_ideal) * np.arccos(temp)) # NOTE: in the middle of the day, arccos(temp) is out of range because # there's no row-to-row shade to avoid, & backtracking is unnecessary # [1], Eqs. 15-16 - with np.errstate(invalid='ignore'): - tracker_theta = wid + np.where(temp < 1, wc, 0) + with np.errstate(invalid="ignore"): + tracker_theta = omega_ideal + np.where(temp < 1, wc, 0) else: - tracker_theta = wid + tracker_theta = omega_ideal # NOTE: max_angle defined relative to zero-point rotation, not the # system-plane normal @@ -182,14 +192,19 @@ def singleaxis(apparent_zenith, apparent_azimuth, # Calculate auxiliary angles surface = calc_surface_orientation(tracker_theta, axis_tilt, axis_azimuth) - surface_tilt = surface['surface_tilt'] - surface_azimuth = surface['surface_azimuth'] - aoi = irradiance.aoi(surface_tilt, surface_azimuth, - apparent_zenith, apparent_azimuth) + surface_tilt = surface["surface_tilt"] + surface_azimuth = surface["surface_azimuth"] + aoi = irradiance.aoi( + surface_tilt, surface_azimuth, apparent_zenith, apparent_azimuth + ) # Bundle DataFrame for return values and filter for sun below horizon. - out = {'tracker_theta': tracker_theta, 'aoi': aoi, - 'surface_azimuth': surface_azimuth, 'surface_tilt': surface_tilt} + out = { + "tracker_theta": tracker_theta, + "aoi": aoi, + "surface_azimuth": surface_azimuth, + "surface_tilt": surface_tilt, + } if index is not None: out = pd.DataFrame(out, index=index) out[zen_gt_90] = np.nan @@ -231,25 +246,31 @@ def calc_surface_orientation(tracker_theta, axis_tilt=0, axis_azimuth=0): Tracking of One-Axis Trackers", Technical Report NREL/TP-6A20-58891, July 2013. :doi:`10.2172/1089596` """ - with np.errstate(invalid='ignore', divide='ignore'): + with np.errstate(invalid="ignore", divide="ignore"): surface_tilt = acosd(cosd(tracker_theta) * cosd(axis_tilt)) # clip(..., -1, +1) to prevent arcsin(1 + epsilon) issues: - azimuth_delta = asind(np.clip(sind(tracker_theta) / sind(surface_tilt), - a_min=-1, a_max=1)) + azimuth_delta = asind( + np.clip( + sind(tracker_theta) + / sind(surface_tilt), a_min=-1, a_max=1 + ) + ) # Combine Eqs 2, 3, and 4: - azimuth_delta = np.where(abs(tracker_theta) < 90, - azimuth_delta, - -azimuth_delta + np.sign(tracker_theta) * 180) + azimuth_delta = np.where( + abs(tracker_theta) < 90, + azimuth_delta, + -azimuth_delta + np.sign(tracker_theta) * 180, + ) # handle surface_tilt=0 case: azimuth_delta = np.where(sind(surface_tilt) != 0, azimuth_delta, 90) surface_azimuth = (axis_azimuth + azimuth_delta) % 360 out = { - 'surface_tilt': surface_tilt, - 'surface_azimuth': surface_azimuth, + "surface_tilt": surface_tilt, + "surface_azimuth": surface_azimuth, } - if hasattr(tracker_theta, 'index'): + if hasattr(tracker_theta, "index"): out = pd.DataFrame(out) return out @@ -320,8 +341,8 @@ def _calc_tracker_norm(ba, bg, dg): sin_bg = sind(bg) sin_dg = sind(dg) vx = sin_dg * cos_ba * cos_bg - vy = sind(ba)*sin_bg + cosd(dg)*cos_ba*cos_bg - vz = -sin_dg*sin_bg*cos_ba + vy = sind(ba) * sin_bg + cosd(dg) * cos_ba * cos_bg + vz = -sin_dg * sin_bg * cos_ba return vx, vy, vz @@ -345,12 +366,13 @@ def _calc_beta_c(v, dg, ba): """ vnorm = np.sqrt(np.dot(v, v)) beta_c = np.arcsin( - ((v[0]*cosd(dg) - v[1]*sind(dg)) * sind(ba) + v[2]*cosd(ba)) / vnorm) + ((v[0] * cosd(dg) - v[1] * sind(dg)) * sind(ba) + v[2] * cosd(ba)) + / vnorm + ) return beta_c -def calc_cross_axis_tilt( - slope_azimuth, slope_tilt, axis_azimuth, axis_tilt): +def calc_cross_axis_tilt(slope_azimuth, slope_tilt, axis_azimuth, axis_tilt): """ Calculate the angle, relative to horizontal, of the line formed by the intersection between the slope containing the tracker axes and a plane