{
  "cells": [
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "%matplotlib inline"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "\n# Day-Night Masking\n\nMasking day-night periods using the PVAnalytics daytime module.\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Identifying and masking day-night periods in an AC power time series or\nirradiance time series can aid in future data analysis, such as detecting\nif a time series has daylight savings time or time shifts. Here, we use\n:py:func:`pvanalytics.features.daytime.power_or_irradiance` to mask day/night\nperiods, as well as to estimate sunrise and sunset times in the data set.\nThis function is particularly useful for cases where the time zone of a data\nstream is unknown or incorrect, as its outputs can be used to determine time\nzone.\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "import pvanalytics\nfrom pvanalytics.features.daytime import power_or_irradiance\nimport matplotlib.pyplot as plt\nimport pandas as pd\nimport pathlib\nimport pvlib\nimport numpy as np"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "First, read in the 1-minute sampled AC power time series data, taken\nfrom the SERF East installation on the NREL campus.\nThis sample is provided from the NREL PVDAQ database, and contains\na column representing an AC power data stream.\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "pvanalytics_dir = pathlib.Path(pvanalytics.__file__).parent\nac_power_file = pvanalytics_dir / 'data' / 'serf_east_1min_ac_power.csv'\ndata = pd.read_csv(ac_power_file, index_col=0, parse_dates=True)\ndata = data.sort_index()\n# This is the known frequency of the time series. You may need to infer\n# the frequency or set the frequency with your AC power time series.\nfreq = \"1T\"\n# These are the latitude-longitude coordinates associated with the\n# SERF East system.\nlatitude = 39.742\nlongitude = -105.173\n# Plot the time series.\ndata['ac_power__752'].plot()\nplt.xlabel(\"Date\")\nplt.ylabel(\"AC Power (kW)\")\nplt.tight_layout()\nplt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "It is critical to set all negative values in the AC power time series to 0\nfor :py:func:`pvanalytics.features.daytime.power_or_irradiance` to work\nproperly. Negative erroneous data may affect daytime mask assignments.\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "data.loc[data['ac_power__752'] < 0, 'ac_power__752'] = 0"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Now, use :py:func:`pvanalytics.features.daytime.power_or_irradiance`\nto mask day periods in the time series.\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "predicted_day_night_mask = power_or_irradiance(series=data['ac_power__752'],\n                                               freq=freq)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Function :py:func:`pvlib.solarposition.sun_rise_set_transit_spa` is\nused to get ground-truth sunrise and sunset times for each day at the site\nlocation, and a SPA-daytime mask is calculated based on these times. Data\nassociated with SPA daytime periods is labeled as True, and data associated\nwith SPA nighttime periods is labeled as False.\nSPA sunrise and sunset times are used here as a point of comparison to the\n:py:func:`pvanalytics.features.daytime.power_or_irradiance` outputs.\nSPA-based sunrise and sunset values are not\nneeded to run :py:func:`pvanalytics.features.daytime.power_or_irradiance`.\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "sunrise_sunset_df = pvlib.solarposition.sun_rise_set_transit_spa(data.index,\n                                                                 latitude,\n                                                                 longitude)\ndata['sunrise_time'] = sunrise_sunset_df['sunrise']\ndata['sunset_time'] = sunrise_sunset_df['sunset']\n\ndata['daytime_mask'] = True\ndata.loc[(data.index < data.sunrise_time) |\n         (data.index > data.sunset_time), \"daytime_mask\"] = False"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Plot the AC power data stream with the mask output from\n:py:func:`pvanalytics.features.daytime.power_or_irradiance`,\nas well as the SPA-calculated sunrise and sunset\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "data['ac_power__752'].plot()\ndata.loc[predicted_day_night_mask, 'ac_power__752'].plot(ls='', marker='o')\ndata.loc[~predicted_day_night_mask, 'ac_power__752'].plot(ls='', marker='o')\nsunrise_sunset_times = sunrise_sunset_df[['sunrise',\n                                          'sunset']].drop_duplicates()\nfor sunrise, sunset in sunrise_sunset_times.itertuples(index=False):\n    plt.axvline(x=sunrise, c=\"blue\")\n    plt.axvline(x=sunset, c=\"red\")\nplt.legend(labels=[\"AC Power\", \"Daytime\", \"Nighttime\",\n                   \"SPA Sunrise\", \"SPA Sunset\"])\nplt.xlabel(\"Date\")\nplt.ylabel(\"AC Power (kW)\")\nplt.tight_layout()\nplt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Compare the predicted mask to the ground-truth SPA mask, to get the model\naccuracy. Also, compare sunrise and sunset times for the predicted mask\ncompared to the ground truth sunrise and sunset times.\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "acc = 100 * np.sum(np.equal(data.daytime_mask,\n                            predicted_day_night_mask))/len(data.daytime_mask)\nprint(\"Overall model prediction accuracy: \" + str(round(acc, 2)) + \"%\")\n\n# Generate predicted + SPA sunrise times for each day\nprint(\"Sunrise Comparison:\")\nprint(pd.DataFrame({'predicted_sunrise': predicted_day_night_mask\n                    .index[predicted_day_night_mask]\n                    .to_series().resample(\"d\").first(),\n                    'pvlib_spa_sunrise': sunrise_sunset_df[\"sunrise\"]\n                    .resample(\"d\").first()}))\n# Generate predicted + SPA sunset times for each day\nprint(\"Sunset Comparison:\")\nprint(pd.DataFrame({'predicted_sunset': predicted_day_night_mask\n                    .index[predicted_day_night_mask]\n                    .to_series().resample(\"d\").last(),\n                    'pvlib_spa_sunset': sunrise_sunset_df[\"sunrise\"]\n                    .resample(\"d\").last()}))"
      ]
    }
  ],
  "metadata": {
    "kernelspec": {
      "display_name": "Python 3",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3.7.9"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}