{
  "cells": [
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "%matplotlib inline"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "\n# Seasonal Tilt\n\nExample of a custom Mount class.\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Some PV systems are built with the option to adjust the module\ntilt to follow seasonal changes in solar position.  For example,\nSAM calls this strategy \"Seasonal Tilt\".  This example shows how\nto use a custom Mount class to use the Seasonal Tilt strategy\nwith :py:class:`~pvlib.modelchain.ModelChain`.\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "import pvlib\nfrom pvlib import pvsystem, location, modelchain, iotools\nfrom pvlib.temperature import TEMPERATURE_MODEL_PARAMETERS\nimport pandas as pd\nimport pathlib\nimport matplotlib.pyplot as plt\nfrom dataclasses import dataclass"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "New Mount classes should extend ``pvlib.pvsystem.AbstractMount``\nand must implement a ``get_orientation(solar_zenith, solar_azimuth)`` method:\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "@dataclass\nclass SeasonalTiltMount(pvsystem.AbstractMount):\n    monthly_tilts: list  # length 12, one tilt per calendar month\n    surface_azimuth: float = 180.0\n\n    def get_orientation(self, solar_zenith, solar_azimuth):\n        tilts = [self.monthly_tilts[m-1] for m in solar_zenith.index.month]\n        return pd.DataFrame({\n            'surface_tilt': tilts,\n            'surface_azimuth': self.surface_azimuth,\n        }, index=solar_zenith.index)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "First let's grab some weather data and make sure our mount produces tilts\nlike we expect:\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "DATA_DIR = pathlib.Path(pvlib.__file__).parent / 'data'\ntmy, metadata = iotools.read_tmy3(DATA_DIR / '723170TYA.CSV', coerce_year=1990)\n# shift from TMY3 right-labeled index to left-labeled index:\ntmy.index = tmy.index - pd.Timedelta(hours=1)\nweather = pd.DataFrame({\n    'ghi': tmy['GHI'], 'dhi': tmy['DHI'], 'dni': tmy['DNI'],\n    'temp_air': tmy['DryBulb'], 'wind_speed': tmy['Wspd'],\n})\nloc = location.Location.from_tmy(metadata)\nsolpos = loc.get_solarposition(weather.index)\n# same default monthly tilts as SAM:\ntilts = [40, 40, 40, 20, 20, 20, 20, 20, 20, 40, 40, 40]\nmount = SeasonalTiltMount(monthly_tilts=tilts)\norientation = mount.get_orientation(solpos.apparent_zenith, solpos.azimuth)\norientation['surface_tilt'].plot()\nplt.ylabel('Surface Tilt [degrees]')\nplt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "With our custom tilt strategy 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_ = mc.run_model(weather)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Now let's re-run the simulation assuming tilt=30 for the entire year:\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "array2 = pvsystem.Array(mount=pvsystem.FixedMount(30, 180),\n                        module_parameters=module_parameters,\n                        temperature_model_parameters=temp_params)\nsystem2 = pvsystem.PVSystem(arrays=[array2], inverter_parameters={'pdc0': 1})\nmc2 = modelchain.ModelChain(system2, loc, spectral_model='no_loss')\n_ = mc2.run_model(weather)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "And finally, compare simulated monthly generation between the two tilt\nstrategies:\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "# sphinx_gallery_thumbnail_number = 2\nresults = pd.DataFrame({\n    'Seasonal 20/40 Production': mc.results.ac,\n    'Fixed 30 Production': mc2.results.ac,\n})\nresults.resample('m').sum().plot()\nplt.ylabel('Monthly Production')\nplt.show()"
      ]
    }
  ],
  "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
}