{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "\n", "# Tutorial 2 - POA Irradiance\n", "\n", "This notebook shows how to use pvlib to transform the three irradiance components (GHI, DHI, and DNI) into POA irradiance, the main driver of a PV system.\n", "\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## PV Concepts\n", "- Plane of Array Irradiance\n", "- Angle of Incidence\n", "- Time delta for solar position\n", "- GHI vs POA for fixed tilt system and tracked systems\n", "\n", "## Python Concepts\n", "- pandas Timedelta\n", "- making a new dataframe from existing columns\n", "- resampling\n", "- bar ploting" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## What is transposition?\n", "\n", "The amount of sunlight collected by a PV panel depends on how well the panel orientation matches incoming sunlight. For example, a rooftop array facing West will produce hardly any energy in the morning when the sun is in the East because the panel can only \"see\" the dim part of the sky away from the sun. \n", "\n", "\n", "\n", "\n", "As the sun comes into view and moves towards the center of the panel's field of view, the panel will collect more and more irradiance. This concept is what defines plane-of-array irradiance -- the amount of sunlight available to be collected at a given panel orientation. Like the three \"basic\" irradiance components, POA irradiance is measured in watts per square meter." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "Each irradiance component is considered separately when modeling POA irradiance. For example, calculating the component of direct irradiance (DNI) that is incident on a panel is solved with straightforward geometry based on the angle of incidence. Finding the POA component of diffuse irradiance (DHI) is more complex and can vary based on atmospheric conditions. Many models, ranging from simple models with lots of assumptions to strongly empirical models, have been published to transpose DHI into the diffuse POA component. A third component of POA irradiance is light that reflects off the ground before being collected by the PV panel. Functions to calculate each of these components are provided by pvlib.\n", "\n", "\n", "" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## How is array orientation defined?\n", "\n", "Two parameters define the panel orientation, one that measures the cardinal direction (North, East, South, West), and one that measures how high in the sky the panel faces:\n", "\n", "- tilt; measured in degrees from horizontal. A flat panel is at tilt=0 and a panel standing on its edge has tilt=90.\n", "- azimuth; measured in degrees from North. The direction along the horizon the panel is facing. N=0, E=90, S=180, W=270.\n", "\n", "A fixed array has fixed tilt and azimuth, but a tracker array constantly changes its orientation to best match the sun's position. So depending on the system configuration, tilt and azimuth may or may not be time series values.\n", "\n", "## Modeling POA from GHI, DHI, and DNI\n", "\n", "As mentioned earlier, pvlib makes it easy to calculate POA irradiance. First, let's load in the example TMY dataset from the previous tutorial:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# if running on google colab, uncomment the next line and execute this cell to install the dependencies and prevent \"ModuleNotFoundError\" in later cells:\n", "# !pip install -r https://raw.githubusercontent.com/PV-Tutorials/pyData-2021-Solar-PV-Modeling/main/requirements.txt" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0.8.1\n" ] } ], "source": [ "import pvlib\n", "import pandas as pd # for data wrangling\n", "import matplotlib.pyplot as plt # for visualization\n", "import pathlib # for finding the example dataset\n", "\n", "print(pvlib.__version__)\n", "\n", "DATA_DIR = pathlib.Path(pvlib.__file__).parent / 'data'\n", "df_tmy, metadata = pvlib.iotools.read_tmy3(DATA_DIR / '723170TYA.CSV', coerce_year=1990)\n", "\n", "# make a Location object corresponding to this TMY\n", "location = pvlib.location.Location(latitude=metadata['latitude'],\n", " longitude=metadata['longitude'])" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "We are looking at data from \"GREENSBORO PIEDMONT TRIAD INT\" , NC\n" ] } ], "source": [ "print(\"We are looking at data from \", metadata['Name'], \",\", metadata['State'])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Because part of the transposition process requires knowing where the sun is in the sky, let's use pvlib to calculate solar position. There is a gotcha here! TMY data represents the average weather conditions across each hour, meaning we need to calculate solar position in the middle of each hour. pvlib calculates solar position for the exact timestamps you specify, so we need to adjust the times by half an interval (30 minutes):" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
| \n", " | apparent_zenith | \n", "zenith | \n", "apparent_elevation | \n", "elevation | \n", "azimuth | \n", "equation_of_time | \n", "
|---|---|---|---|---|---|---|
| 1990-01-01 01:00:00-05:00 | \n", "166.841893 | \n", "166.841893 | \n", "-76.841893 | \n", "-76.841893 | \n", "6.889661 | \n", "-3.395097 | \n", "
| 1990-01-01 02:00:00-05:00 | \n", "160.512529 | \n", "160.512529 | \n", "-70.512529 | \n", "-70.512529 | \n", "52.424214 | \n", "-3.414863 | \n", "
| 1990-01-01 03:00:00-05:00 | \n", "149.674856 | \n", "149.674856 | \n", "-59.674856 | \n", "-59.674856 | \n", "73.251821 | \n", "-3.434619 | \n", "
| 1990-01-01 04:00:00-05:00 | \n", "137.777090 | \n", "137.777090 | \n", "-47.777090 | \n", "-47.777090 | \n", "85.208599 | \n", "-3.454365 | \n", "
| 1990-01-01 05:00:00-05:00 | \n", "125.672180 | \n", "125.672180 | \n", "-35.672180 | \n", "-35.672180 | \n", "94.134730 | \n", "-3.474102 | \n", "