{
  "cells": [
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "%matplotlib inline"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "\n# Discontinuous Tracking\n\nExample of a custom Mount class.\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Many real-world tracking arrays adjust their position in discrete steps\nrather than through continuous movement. This example shows how to model\nthis discontinuous tracking by implementing a custom Mount class.\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "from pvlib import tracking, pvsystem, location, modelchain\nfrom pvlib.temperature import TEMPERATURE_MODEL_PARAMETERS\nimport matplotlib.pyplot as plt\nimport pandas as pd"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "We'll define our custom Mount by extending\n:py:class:`~pvlib.pvsystem.SingleAxisTrackerMount` for convenience.\nAnother approach would be to extend ``AbstractMount`` directly; see\nthe source code of :py:class:`~pvlib.pvsystem.SingleAxisTrackerMount`\nand :py:class:`~pvlib.pvsystem.FixedMount` for how that is done.\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "class DiscontinuousTrackerMount(pvsystem.SingleAxisTrackerMount):\n    # inherit from SingleAxisTrackerMount so that we get the\n    # constructor and tracking attributes (axis_tilt etc) automatically\n\n    def get_orientation(self, solar_zenith, solar_azimuth):\n        # Different trackers update at different rates; in this example we'll\n        # assume a relatively slow update interval of 15 minutes to make the\n        # effect more visually apparent.\n        zenith_subset = solar_zenith.resample('15min').first()\n        azimuth_subset = solar_azimuth.resample('15min').first()\n\n        tracking_data_15min = tracking.singleaxis(\n            zenith_subset, azimuth_subset,\n            self.axis_tilt, self.axis_azimuth,\n            self.max_angle, self.backtrack,\n            self.gcr, self.cross_axis_tilt\n        )\n        # propagate the 15-minute positions to 1-minute stair-stepped values:\n        tracking_data_1min = tracking_data_15min.reindex(solar_zenith.index,\n                                                         method='ffill')\n        return tracking_data_1min"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Let's take a look at the tracker rotation curve it produces:\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "times = pd.date_range('2019-06-01', '2019-06-02', freq='1min', tz='US/Eastern')\nloc = location.Location(40, -80)\nsolpos = loc.get_solarposition(times)\nmount = DiscontinuousTrackerMount(axis_azimuth=180, gcr=0.4)\ntracker_data = mount.get_orientation(solpos.apparent_zenith, solpos.azimuth)\ntracker_data['tracker_theta'].plot()\nplt.ylabel('Tracker Rotation [degree]')\nplt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "With our custom tracking logic defined, we can create the corresponding\nArray and PVSystem, and then run a ModelChain as usual:\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "module_parameters = {'pdc0': 1, 'gamma_pdc': -0.004, 'b': 0.05}\ntemp_params = TEMPERATURE_MODEL_PARAMETERS['sapm']['open_rack_glass_polymer']\narray = pvsystem.Array(mount=mount, module_parameters=module_parameters,\n                       temperature_model_parameters=temp_params)\nsystem = pvsystem.PVSystem(arrays=[array], inverter_parameters={'pdc0': 1})\nmc = modelchain.ModelChain(system, loc, spectral_model='no_loss')\n\n# simple simulated weather, just to show the effect of discrete tracking\nweather = loc.get_clearsky(times)\nweather['temp_air'] = 25\nweather['wind_speed'] = 1\nmc.run_model(weather)\n\nfig, axes = plt.subplots(2, 1, sharex=True)\nmc.results.effective_irradiance.plot(ax=axes[0])\naxes[0].set_ylabel('Effective Irradiance [W/m^2]')\nmc.results.ac.plot(ax=axes[1])\naxes[1].set_ylabel('AC Power')\nfig.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "The effect of discontinuous tracking creates a \"jagged\" effect in the\nsimulated plane-of-array irradiance, which then propagates through to\nthe AC power output.\n\n"
      ]
    }
  ],
  "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
}