From 8a0e794d27d95e88f5ff27292e41ba4a41d0a7ae Mon Sep 17 00:00:00 2001 From: Will Holmgren Date: Fri, 7 Sep 2018 14:44:50 -0700 Subject: [PATCH 1/9] convert modelchain.ipynb to rst, remove nbsphinx --- docs/environment.yml | 1 - docs/sphinx/source/conf.py | 3 +- docs/sphinx/source/modelchain.ipynb | 738 ---------------------------- docs/sphinx/source/modelchain.rst | 454 +++++++++++++++++ 4 files changed, 455 insertions(+), 741 deletions(-) delete mode 100644 docs/sphinx/source/modelchain.ipynb create mode 100644 docs/sphinx/source/modelchain.rst diff --git a/docs/environment.yml b/docs/environment.yml index 04c5561e15..c871a85cf3 100644 --- a/docs/environment.yml +++ b/docs/environment.yml @@ -22,4 +22,3 @@ dependencies: - hdf4=4.2.12 - sphinx_rtd_theme - docutils - - nbsphinx diff --git a/docs/sphinx/source/conf.py b/docs/sphinx/source/conf.py index 8483da4c44..b0373a1483 100644 --- a/docs/sphinx/source/conf.py +++ b/docs/sphinx/source/conf.py @@ -58,8 +58,7 @@ def __getattr__(cls, name): 'numpydoc', 'sphinx.ext.autosummary', 'IPython.sphinxext.ipython_directive', - 'IPython.sphinxext.ipython_console_highlighting', - 'nbsphinx' + 'IPython.sphinxext.ipython_console_highlighting' ] # Add any paths that contain templates here, relative to this directory. diff --git a/docs/sphinx/source/modelchain.ipynb b/docs/sphinx/source/modelchain.ipynb deleted file mode 100644 index b3e991679a..0000000000 --- a/docs/sphinx/source/modelchain.ipynb +++ /dev/null @@ -1,738 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# ModelChain" - ] - }, - { - "cell_type": "raw", - "metadata": { - "raw_mimetype": "text/restructuredtext" - }, - "source": [ - "The :py:class:`~.modelchain.ModelChain` class provides a high-level interface for standardized PV modeling. The class aims to automate much of the modeling process while providing user-control and remaining extensible. This guide aims to build users' understanding of the ModelChain class. It assumes some familiarity with object-oriented code in Python, but most information should be understandable even without a solid understanding of classes.\n", - "\n", - "A :py:class:`~.modelchain.ModelChain` is composed of a :py:class:`~.pvsystem.PVSystem` object and a :py:class:`~.location.Location` object. A PVSystem object represents an assembled collection of modules, inverters, etc., a Location object represents a particular place on the planet, and a ModelChain object describes the modeling chain used to calculate a system's output at that location. The PVSystem and Location objects will be described in detail in another guide.\n", - "\n", - "Modeling with a :py:class:`~.ModelChain` typically involves 3 steps:\n", - "\n", - "1. Creating the :py:class:`~.ModelChain`.\n", - "2. Executing the :py:meth:`ModelChain.run_model() <.ModelChain.run_model>` method with prepared weather data.\n", - "3. Examining the model results that :py:meth:`~.ModelChain.run_model` stored in attributes of the :py:class:`~.ModelChain`." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## A simple ModelChain example\n", - "\n", - "Before delving into the intricacies of ModelChain, we provide a brief example of the modeling steps using ModelChain. First, we import pvlib's objects, module data, and inverter data." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "import pandas as pd\n", - "import numpy as np\n", - "\n", - "# pvlib imports\n", - "import pvlib\n", - "\n", - "from pvlib.pvsystem import PVSystem\n", - "from pvlib.location import Location\n", - "from pvlib.modelchain import ModelChain\n", - "\n", - "# load some module and inverter specifications\n", - "sandia_modules = pvlib.pvsystem.retrieve_sam('SandiaMod')\n", - "cec_inverters = pvlib.pvsystem.retrieve_sam('cecinverter')\n", - "\n", - "sandia_module = sandia_modules['Canadian_Solar_CS5P_220M___2009_']\n", - "cec_inverter = cec_inverters['ABB__MICRO_0_25_I_OUTD_US_208_208V__CEC_2014_']" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we create a Location object, a PVSystem object, and a ModelChain object." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "location = Location(latitude=32.2, longitude=-110.9)\n", - "system = PVSystem(surface_tilt=20, surface_azimuth=200, \n", - " module_parameters=sandia_module,\n", - " inverter_parameters=cec_inverter)\n", - "mc = ModelChain(system, location)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Printing a ModelChain object will display its models." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "print(mc)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Next, we run a model with some simple weather data." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "weather = pd.DataFrame([[1050, 1000, 100, 30, 5]], \n", - " columns=['ghi', 'dni', 'dhi', 'temp_air', 'wind_speed'], \n", - " index=[pd.Timestamp('20170401 1200', tz='US/Arizona')])\n", - "\n", - "mc.run_model(times=weather.index, weather=weather);" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "ModelChain stores the modeling results on a series of attributes. A few examples are shown below." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "mc.aoi" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "mc.dc" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "mc.ac" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The remainder of this guide examines the ModelChain functionality and explores common pitfalls." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Defining a ModelChain" - ] - }, - { - "cell_type": "raw", - "metadata": { - "raw_mimetype": "text/restructuredtext" - }, - "source": [ - "A :py:class:`~pvlib.modelchain.ModelChain` object is defined by:\n", - "\n", - "1. The properties of its :py:class:`~pvlib.pvsystem.PVSystem` and :py:class:`~pvlib.location.Location` objects\n", - "2. The keyword arguments passed to it at construction\n", - "\n", - "ModelChain uses the keyword arguments passed to it to determine the models for the simulation. The documentation describes the allowed values for each keyword argument. If a keyword argument is not supplied, ModelChain will attempt to infer the correct set of models by inspecting the Location and PVSystem attributes. \n", - "\n", - "Below, we show some examples of how to define a ModelChain." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's make the most basic Location and PVSystem objects and build from there." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "location = Location(32.2, -110.9)\n", - "poorly_specified_system = PVSystem()\n", - "print(location)\n", - "print(poorly_specified_system)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "These basic objects do not have enough information for ModelChain to be able to automatically determine its set of models, so the ModelChain will throw an error when we try to create it." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "ModelChain(poorly_specified_system, location)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If our goal is simply to get the object constructed, we can specify the models that the ModelChain should use. We'll have to fill in missing data on the PVSystem object later, but maybe that's desirable in some workflows." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "mc = ModelChain(poorly_specified_system, location, \n", - " dc_model='singlediode', ac_model='snlinverter', \n", - " aoi_model='physical', spectral_model='no_loss')\n", - "print(mc)" - ] - }, - { - "cell_type": "raw", - "metadata": { - "raw_mimetype": "text/restructuredtext" - }, - "source": [ - "As expected, without additional information, the :py:meth:`~.ModelChain.run_model` method fails at run time." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "mc.run_model(times=weather.index, weather=weather)" - ] - }, - { - "cell_type": "raw", - "metadata": { - "raw_mimetype": "text/restructuredtext" - }, - "source": [ - "The ModelChain attempted to execute the PVSystem object's :py:meth:`~pvlib.pvsystem.PVSystem.singlediode` method, and the method failed because the object's ``module_parameters`` did not include the data necessary to run the model. " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Next, we define a PVSystem with a module from the SAPM database and an inverter from the CEC database. ModelChain will examine the PVSystem object's properties and determine that it should choose the SAPM DC model, AC model, AOI loss model, and spectral loss model." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "sapm_system = PVSystem(module_parameters=sandia_module, inverter_parameters=cec_inverter)\n", - "mc = ModelChain(system, location)\n", - "print(mc)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "mc.run_model(times=weather.index, weather=weather)\n", - "mc.ac" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Alternatively, we could have specified single diode or PVWatts related information in the PVSystem construction. Here we pass PVWatts data to the PVSystem. ModelChain will automatically determine that it should choose PVWatts DC and AC models. ModelChain still needs us to specify ``aoi_model`` and ``spectral_model`` keyword arguments because the ``system.module_parameters`` dictionary does not contain enough information to determine which of those models to choose." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "pvwatts_system = PVSystem(module_parameters={'pdc0': 240, 'gamma_pdc': -0.004})\n", - "mc = ModelChain(pvwatts_system, location, \n", - " aoi_model='physical', spectral_model='no_loss')\n", - "print(mc)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "mc.run_model(times=weather.index, weather=weather)\n", - "mc.ac" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "User-supplied keyword arguments override ModelChain's inspection methods. For example, we can tell ModelChain to use different loss functions for a PVSystem that contains SAPM-specific parameters. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "sapm_system = PVSystem(module_parameters=sandia_module, inverter_parameters=cec_inverter)\n", - "mc = ModelChain(system, location, aoi_model='physical', spectral_model='no_loss')\n", - "print(mc)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "mc.run_model(times=weather.index, weather=weather)\n", - "mc.ac" - ] - }, - { - "cell_type": "raw", - "metadata": { - "raw_mimetype": "text/restructuredtext" - }, - "source": [ - "Of course, these choices can also lead to failure when executing :py:meth:`~pvlib.modelchain.ModelChain.run_model` if your system objects do not contain the required parameters for running the model." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Demystifying ModelChain internals" - ] - }, - { - "cell_type": "raw", - "metadata": { - "raw_mimetype": "text/restructuredtext" - }, - "source": [ - "The ModelChain class has a lot going in inside it in order to make users' code as simple as possible.\n", - "\n", - "The key parts of ModelChain are:\n", - "\n", - "1. The :py:meth:`ModelChain.run_model() <.ModelChain.run_model>` method\n", - "1. A set of methods that wrap and call the PVSystem methods.\n", - "1. A set of methods that inspect user-supplied objects to determine the appropriate default models." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### run_model" - ] - }, - { - "cell_type": "raw", - "metadata": { - "raw_mimetype": "text/restructuredtext" - }, - "source": [ - "Most users will only interact with the :py:meth:`~pvlib.modelchain.ModelChain.run_model` method. The :py:meth:`~pvlib.modelchain.ModelChain.run_model` method, shown below, calls a series of methods to complete the modeling steps. The first method, :py:meth:`~pvlib.modelchain.ModelChain.prepare_inputs`, computes parameters such as solar position, airmass, angle of incidence, and plane of array irradiance. The :py:meth:`~pvlib.modelchain.ModelChain.prepare_inputs` method also assigns default values for irradiance (clear sky), temperature (20 C), and wind speed (0 m/s) if these inputs are not provided.\n", - "\n", - "Next, :py:meth:`~pvlib.modelchain.ModelChain.run_model` calls the wrapper methods for AOI loss, spectral loss, effective irradiance, cell temperature, DC power, AC power, and other losses. These methods are assigned to standard names, as described in the next section.\n", - "\n", - "The methods called by :py:meth:`~pvlib.modelchain.ModelChain.run_model` store their results in a series of ModelChain attributes: ``times``, ``solar_position``, ``airmass``, ``irradiance``, ``total_irrad``, ``effective_irradiance``, ``weather``, ``temps``, ``aoi``, ``aoi_modifier``, ``spectral_modifier``, ``dc``, ``ac``, ``losses``." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "np.source(mc.run_model)" - ] - }, - { - "cell_type": "raw", - "metadata": { - "raw_mimetype": "text/restructuredtext" - }, - "source": [ - "Finally, the :py:meth:`~pvlib.modelchain.ModelChain.complete_irradiance` method is available for calculating the full set of GHI, DNI, or DHI if only two of these three series are provided. The completed dataset can then be passed to :py:meth:`~pvlib.modelchain.ModelChain.run_model`." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Wrapping methods into a unified API\n", - "\n", - "Readers may notice that the source code of the ModelChain.run_model method is model-agnostic. ModelChain.run_model calls generic methods such as ``self.dc_model`` rather than a specific model such as ``singlediode``. So how does the ModelChain.run_model know what models it's supposed to run? The answer comes in two parts, and allows us to explore more of the ModelChain API along the way.\n", - "\n", - "First, ModelChain has a set of methods that wrap the PVSystem methods that perform the calculations (or further wrap the pvsystem.py module's functions). Each of these methods takes the same arguments (``self``) and sets the same attributes, thus creating a uniform API. For example, the ModelChain.pvwatts_dc method is shown below. Its only argument is ``self``, and it sets the ``dc`` attribute." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "np.source(mc.pvwatts_dc)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The ModelChain.pvwatts_dc method calls the pvwatts_dc method of the PVSystem object that we supplied using data that is stored in its own ``effective_irradiance`` and ``temps`` attributes. Then it assigns the result to the ``dc`` attribute of the ModelChain object. The code below shows a simple example of this." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# make the objects\n", - "pvwatts_system = PVSystem(module_parameters={'pdc0': 240, 'gamma_pdc': -0.004})\n", - "mc = ModelChain(pvwatts_system, location, \n", - " aoi_model='no_loss', spectral_model='no_loss')\n", - "\n", - "# manually assign data to the attributes that ModelChain.pvwatts_dc will need.\n", - "# for standard workflows, run_model would assign these attributes.\n", - "mc.effective_irradiance = pd.Series(1000, index=[pd.Timestamp('20170401 1200-0700')])\n", - "mc.temps = pd.DataFrame({'temp_cell': 50, 'temp_module': 50}, index=[pd.Timestamp('20170401 1200-0700')])\n", - "\n", - "# run ModelChain.pvwatts_dc and look at the result\n", - "mc.pvwatts_dc()\n", - "mc.dc" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The ModelChain.sapm method works similarly to the ModelChain.pvwatts_dc method. It calls the PVSystem.sapm method using stored data, then assigns the result to the ``dc`` attribute. The ModelChain.sapm method differs from the ModelChain.pvwatts_dc method in three notable ways. First, the PVSystem.sapm method expects different units for effective irradiance, so ModelChain handles the conversion for us. Second, the PVSystem.sapm method (and the PVSystem.singlediode method) returns a DataFrame with current, voltage, and power parameters rather than a simple Series of power. Finally, this current and voltage information allows the SAPM and single diode model paths to support the concept of modules in series and parallel, which is handled by the PVSystem.scale_voltage_current_power method. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "np.source(mc.sapm)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# make the objects\n", - "sapm_system = PVSystem(module_parameters=sandia_module, inverter_parameters=cec_inverter)\n", - "mc = ModelChain(sapm_system, location)\n", - "\n", - "# manually assign data to the attributes that ModelChain.sapm will need.\n", - "# for standard workflows, run_model would assign these attributes.\n", - "mc.effective_irradiance = pd.Series(1000, index=[pd.Timestamp('20170401 1200-0700')])\n", - "mc.temps = pd.DataFrame({'temp_cell': 50, 'temp_module': 50}, index=[pd.Timestamp('20170401 1200-0700')])\n", - "\n", - "# run ModelChain.sapm and look at the result\n", - "mc.sapm()\n", - "mc.dc" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We've established that the ``ModelChain.pvwatts_dc`` and ``ModelChain.sapm`` have the same API: they take the same arugments (``self``) and they both set the ``dc`` attribute.\\* Because the methods have the same API, we can call them in the same way. ModelChain includes a large number of methods that perform the same API-unification roles for each modeling step.\n", - "\n", - "Again, so how does the ModelChain.run_model know which models it's supposed to run?\n", - "\n", - "At object construction, ModelChain assigns the desired model's method (e.g. ``ModelChain.pvwatts_dc``) to the corresponding generic attribute (e.g. ``ModelChain.dc_model``) using a method described in the next section." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "pvwatts_system = PVSystem(module_parameters={'pdc0': 240, 'gamma_pdc': -0.004})\n", - "mc = ModelChain(pvwatts_system, location, \n", - " aoi_model='no_loss', spectral_model='no_loss')\n", - "mc.dc_model.__func__" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The ModelChain.run_model method can ignorantly call ``self.dc_module`` because the API is the same for all methods that may be assigned to this attribute." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\\* some readers may object that the API is *not* actually the same because the type of the ``dc`` attribute is different (Series vs. DataFrame)!" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Inferring models\n", - "\n", - "How does ModelChain infer the appropriate model types? ModelChain uses a series of methods (ModelChain.infer_dc_model, ModelChain.infer_ac_model, etc.) that examine the user-supplied PVSystem object. The inference methods use set logic to assign one of the model-specific methods, such as ModelChain.sapm or ModelChain.snlinverter, to the universal method names ModelChain.dc_model and ModelChain.ac_model. A few examples are shown below." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "np.source(mc.infer_dc_model)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "np.source(mc.infer_ac_model)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## User-defined models\n", - "\n", - "Users may also write their own functions and pass them as arguments to ModelChain. The first argument of the function must be a ModelChain instance. For example, the functions below implement the PVUSA model and a wrapper function appropriate for use with ModelChain. This follows the pattern of implementing the core models using the simplest possible functions, and then implementing wrappers to make them easier to use in specific applications. Of course, you could implement it in a single function if you wanted to." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def pvusa(poa_global, wind_speed, temp_air, a, b, c, d):\n", - " \"\"\"\n", - " Calculates system power according to the PVUSA equation\n", - " \n", - " P = I * (a + b*I + c*W + d*T)\n", - " \n", - " where\n", - " P is the output power,\n", - " I is the plane of array irradiance,\n", - " W is the wind speed, and\n", - " T is the temperature\n", - " a, b, c, d are empirically derived parameters.\n", - " \"\"\"\n", - " return poa_global * (a + b*poa_global + c*wind_speed + d*temp_air)\n", - "\n", - "\n", - "def pvusa_mc_wrapper(mc):\n", - " # calculate the dc power and assign it to mc.dc\n", - " mc.dc = pvusa(mc.total_irrad['poa_global'], mc.weather['wind_speed'], mc.weather['temp_air'],\n", - " mc.system.module_parameters['a'], mc.system.module_parameters['b'],\n", - " mc.system.module_parameters['c'], mc.system.module_parameters['d'])\n", - " \n", - " # returning mc is optional, but enables method chaining\n", - " return mc\n", - "\n", - "\n", - "def pvusa_ac_mc_wrapper(mc):\n", - " # keep it simple\n", - " mc.ac = mc.dc\n", - " return mc" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "module_parameters = {'a': 0.2, 'b': 0.00001, 'c': 0.001, 'd': -0.00005}\n", - "pvusa_system = PVSystem(module_parameters=module_parameters)\n", - "\n", - "mc = ModelChain(pvusa_system, location, \n", - " dc_model=pvusa_mc_wrapper, ac_model=pvusa_ac_mc_wrapper,\n", - " aoi_model='no_loss', spectral_model='no_loss')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "A ModelChain object uses Python's functools.partial function to assign itself as the argument to the user-supplied functions." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "mc.dc_model.func" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The end result is that ModelChain.run_model works as expected!" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "mc.run_model(times=weather.index, weather=weather)\n", - "mc.dc" - ] - } - ], - "metadata": { - "celltoolbar": "Raw Cell Format", - "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.6.4" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/docs/sphinx/source/modelchain.rst b/docs/sphinx/source/modelchain.rst new file mode 100644 index 0000000000..c49fdb3f2a --- /dev/null +++ b/docs/sphinx/source/modelchain.rst @@ -0,0 +1,454 @@ + +ModelChain +========== + +The :py:class:`~.modelchain.ModelChain` class provides a high-level +interface for standardized PV modeling. The class aims to automate much +of the modeling process while providing user-control and remaining +extensible. This guide aims to build users' understanding of the +ModelChain class. It assumes some familiarity with object-oriented +ipython in Python, but most information should be understandable even +without a solid understanding of classes. + +A :py:class:`~.modelchain.ModelChain` is composed of a +:py:class:`~.pvsystem.PVSystem` object and a +:py:class:`~.location.Location` object. A PVSystem object represents an +assembled collection of modules, inverters, etc., a Location object +represents a particular place on the planet, and a ModelChain object +describes the modeling chain used to calculate a system's output at that +location. The PVSystem and Location objects will be described in detail +in another guide. + +Modeling with a :py:class:`~.ModelChain` typically involves 3 steps: + +1. Creating the :py:class:`~.ModelChain`. +2. Executing the :py:meth:`ModelChain.run_model() <.ModelChain.run_model>` + method with prepared weather data. +3. Examining the model results that :py:meth:`~.ModelChain.run_model` stored in attributes of the :py:class:`~.ModelChain`. + +A simple ModelChain example +--------------------------- + +Before delving into the intricacies of ModelChain, we provide a brief +example of the modeling steps using ModelChain. First, we import pvlib’s +objects, module data, and inverter data. + +.. ipython:: python + + import pandas as pd + import numpy as np + + # pvlib imports + import pvlib + + from pvlib.pvsystem import PVSystem + from pvlib.location import Location + from pvlib.modelchain import ModelChain + + # load some module and inverter specifications + sandia_modules = pvlib.pvsystem.retrieve_sam('SandiaMod') + cec_inverters = pvlib.pvsystem.retrieve_sam('cecinverter') + + sandia_module = sandia_modules['Canadian_Solar_CS5P_220M___2009_'] + cec_inverter = cec_inverters['ABB__MICRO_0_25_I_OUTD_US_208_208V__CEC_2014_'] + +Now we create a Location object, a PVSystem object, and a ModelChain +object. + +.. ipython:: python + + location = Location(latitude=32.2, longitude=-110.9) + system = PVSystem(surface_tilt=20, surface_azimuth=200, + module_parameters=sandia_module, + inverter_parameters=cec_inverter) + mc = ModelChain(system, location) + +Printing a ModelChain object will display its models. + +.. ipython:: python + + print(mc) + +Next, we run a model with some simple weather data. + +.. ipython:: python + + weather = pd.DataFrame([[1050, 1000, 100, 30, 5]], + columns=['ghi', 'dni', 'dhi', 'temp_air', 'wind_speed'], + index=[pd.Timestamp('20170401 1200', tz='US/Arizona')]) + + mc.run_model(times=weather.index, weather=weather); + +ModelChain stores the modeling results on a series of attributes. A few +examples are shown below. + +.. ipython:: python + + mc.aoi + +.. ipython:: python + + mc.dc + +.. ipython:: python + + mc.ac + +The remainder of this guide examines the ModelChain functionality and +explores common pitfalls. + +Defining a ModelChain +--------------------- + +A :py:class:`~pvlib.modelchain.ModelChain` object is defined by: + +1. The properties of its :py:class:`~pvlib.pvsystem.PVSystem` + and :py:class:`~pvlib.location.Location` objects +2. The keyword arguments passed to it at construction + +ModelChain uses the keyword arguments passed to it to determine the +models for the simulation. The documentation describes the allowed +values for each keyword argument. If a keyword argument is not supplied, +ModelChain will attempt to infer the correct set of models by inspecting +the Location and PVSystem attributes. + +Below, we show some examples of how to define a ModelChain. + +Let’s make the most basic Location and PVSystem objects and build from +there. + +.. ipython:: python + + location = Location(32.2, -110.9) + poorly_specified_system = PVSystem() + print(location) + print(poorly_specified_system) + +These basic objects do not have enough information for ModelChain to be +able to automatically determine its set of models, so the ModelChain +will throw an error when we try to create it. + +.. ipython:: python + :okexcept: + + ModelChain(poorly_specified_system, location) + +If our goal is simply to get the object constructed, we can specify the +models that the ModelChain should use. We’ll have to fill in missing +data on the PVSystem object later, but maybe that’s desirable in some +workflows. + +.. ipython:: python + :okexcept: + + mc = ModelChain(poorly_specified_system, location, + dc_model='singlediode', ac_model='snlinverter', + aoi_model='physical', spectral_model='no_loss') + print(mc) + +As expected, without additional information, the +:py:meth:`~.ModelChain.run_model` method fails at run time. + +.. ipython:: python + :okexcept: + + mc.run_model(times=weather.index, weather=weather) + +The ModelChain attempted to execute the PVSystem object's +:py:meth:`~pvlib.pvsystem.PVSystem.singlediode` method, and the method +failed because the object's ``module_parameters`` did not include the +data necessary to run the model. + +Next, we define a PVSystem with a module from the SAPM database and an +inverter from the CEC database. ModelChain will examine the PVSystem +object’s properties and determine that it should choose the SAPM DC +model, AC model, AOI loss model, and spectral loss model. + +.. ipython:: python + + sapm_system = PVSystem(module_parameters=sandia_module, inverter_parameters=cec_inverter) + mc = ModelChain(system, location) + print(mc) + +.. ipython:: python + + mc.run_model(times=weather.index, weather=weather) + mc.ac + +Alternatively, we could have specified single diode or PVWatts related +information in the PVSystem construction. Here we pass PVWatts data to +the PVSystem. ModelChain will automatically determine that it should +choose PVWatts DC and AC models. ModelChain still needs us to specify +``aoi_model`` and ``spectral_model`` keyword arguments because the +``system.module_parameters`` dictionary does not contain enough +information to determine which of those models to choose. + +.. ipython:: python + + pvwatts_system = PVSystem(module_parameters={'pdc0': 240, 'gamma_pdc': -0.004}) + mc = ModelChain(pvwatts_system, location, + aoi_model='physical', spectral_model='no_loss') + print(mc) + +.. ipython:: python + + mc.run_model(times=weather.index, weather=weather) + mc.ac + +User-supplied keyword arguments override ModelChain’s inspection +methods. For example, we can tell ModelChain to use different loss +functions for a PVSystem that contains SAPM-specific parameters. + +.. ipython:: python + + sapm_system = PVSystem(module_parameters=sandia_module, inverter_parameters=cec_inverter) + mc = ModelChain(system, location, aoi_model='physical', spectral_model='no_loss') + print(mc) + +.. ipython:: python + + mc.run_model(times=weather.index, weather=weather) + mc.ac + +Of course, these choices can also lead to failure when executing +:py:meth:`~pvlib.modelchain.ModelChain.run_model` if your system objects +do not contain the required parameters for running the model. + +Demystifying ModelChain internals +--------------------------------- + +The ModelChain class has a lot going in inside it in order to make +users' ipython as simple as possible. + +The key parts of ModelChain are: + +1. The :py:meth:`ModelChain.run_model() <.ModelChain.run_model>` method +1. A set of methods that wrap and call the PVSystem methods. +1. A set of methods that inspect user-supplied objects to determine + the appropriate default models. + +run_model +~~~~~~~~~ + +Most users will only interact with the +:py:meth:`~pvlib.modelchain.ModelChain.run_model` method. The +:py:meth:`~pvlib.modelchain.ModelChain.run_model` method, shown below, +calls a series of methods to complete the modeling steps. The first +method, :py:meth:`~pvlib.modelchain.ModelChain.prepare_inputs`, computes +parameters such as solar position, airmass, angle of incidence, and +plane of array irradiance. The +:py:meth:`~pvlib.modelchain.ModelChain.prepare_inputs` method also +assigns default values for irradiance (clear sky), temperature (20 C), +and wind speed (0 m/s) if these inputs are not provided. + +Next, :py:meth:`~pvlib.modelchain.ModelChain.run_model` calls the +wrapper methods for AOI loss, spectral loss, effective irradiance, cell +temperature, DC power, AC power, and other losses. These methods are +assigned to standard names, as described in the next section. + +The methods called by :py:meth:`~pvlib.modelchain.ModelChain.run_model` +store their results in a series of ModelChain attributes: ``times``, +``solar_position``, ``airmass``, ``irradiance``, ``total_irrad``, +``effective_irradiance``, ``weather``, ``temps``, ``aoi``, +``aoi_modifier``, ``spectral_modifier``, ``dc``, ``ac``, ``losses``. + +.. ipython:: python + + mc.run_model?? + +Finally, the :py:meth:`~pvlib.modelchain.ModelChain.complete_irradiance` +method is available for calculating the full set of GHI, DNI, or DHI if +only two of these three series are provided. The completed dataset can +then be passed to :py:meth:`~pvlib.modelchain.ModelChain.run_model`. + +Wrapping methods into a unified API +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Readers may notice that the source ipython of the ModelChain.run_model +method is model-agnostic. ModelChain.run_model calls generic methods +such as ``self.dc_model`` rather than a specific model such as +``singlediode``. So how does the ModelChain.run_model know what models +it’s supposed to run? The answer comes in two parts, and allows us to +explore more of the ModelChain API along the way. + +First, ModelChain has a set of methods that wrap the PVSystem methods +that perform the calculations (or further wrap the pvsystem.py module’s +functions). Each of these methods takes the same arguments (``self``) +and sets the same attributes, thus creating a uniform API. For example, +the ModelChain.pvwatts_dc method is shown below. Its only argument is +``self``, and it sets the ``dc`` attribute. + +.. ipython:: python + + mc.pvwatts_dc?? + +The ModelChain.pvwatts_dc method calls the pvwatts_dc method of the +PVSystem object that we supplied using data that is stored in its own +``effective_irradiance`` and ``temps`` attributes. Then it assigns the +result to the ``dc`` attribute of the ModelChain object. The ipython below +shows a simple example of this. + +.. ipython:: python + + # make the objects + pvwatts_system = PVSystem(module_parameters={'pdc0': 240, 'gamma_pdc': -0.004}) + mc = ModelChain(pvwatts_system, location, + aoi_model='no_loss', spectral_model='no_loss') + + # manually assign data to the attributes that ModelChain.pvwatts_dc will need. + # for standard workflows, run_model would assign these attributes. + mc.effective_irradiance = pd.Series(1000, index=[pd.Timestamp('20170401 1200-0700')]) + mc.temps = pd.DataFrame({'temp_cell': 50, 'temp_module': 50}, index=[pd.Timestamp('20170401 1200-0700')]) + + # run ModelChain.pvwatts_dc and look at the result + mc.pvwatts_dc() + mc.dc + +The ModelChain.sapm method works similarly to the ModelChain.pvwatts_dc +method. It calls the PVSystem.sapm method using stored data, then +assigns the result to the ``dc`` attribute. The ModelChain.sapm method +differs from the ModelChain.pvwatts_dc method in three notable ways. +First, the PVSystem.sapm method expects different units for effective +irradiance, so ModelChain handles the conversion for us. Second, the +PVSystem.sapm method (and the PVSystem.singlediode method) returns a +DataFrame with current, voltage, and power parameters rather than a +simple Series of power. Finally, this current and voltage information +allows the SAPM and single diode model paths to support the concept of +modules in series and parallel, which is handled by the +PVSystem.scale_voltage_current_power method. + +.. ipython:: python + + mc.sapm?? + +.. ipython:: python + + # make the objects + sapm_system = PVSystem(module_parameters=sandia_module, inverter_parameters=cec_inverter) + mc = ModelChain(sapm_system, location) + + # manually assign data to the attributes that ModelChain.sapm will need. + # for standard workflows, run_model would assign these attributes. + mc.effective_irradiance = pd.Series(1000, index=[pd.Timestamp('20170401 1200-0700')]) + mc.temps = pd.DataFrame({'temp_cell': 50, 'temp_module': 50}, index=[pd.Timestamp('20170401 1200-0700')]) + + # run ModelChain.sapm and look at the result + mc.sapm() + mc.dc + +We’ve established that the ``ModelChain.pvwatts_dc`` and +``ModelChain.sapm`` have the same API: they take the same arugments +(``self``) and they both set the ``dc`` attribute.\* Because the methods +have the same API, we can call them in the same way. ModelChain includes +a large number of methods that perform the same API-unification roles +for each modeling step. + +Again, so how does the ModelChain.run_model know which models it’s +supposed to run? + +At object construction, ModelChain assigns the desired model’s method +(e.g. ``ModelChain.pvwatts_dc``) to the corresponding generic attribute +(e.g. ``ModelChain.dc_model``) using a method described in the next +section. + +.. ipython:: python + + pvwatts_system = PVSystem(module_parameters={'pdc0': 240, 'gamma_pdc': -0.004}) + mc = ModelChain(pvwatts_system, location, + aoi_model='no_loss', spectral_model='no_loss') + mc.dc_model.__func__ + +The ModelChain.run_model method can ignorantly call ``self.dc_module`` +because the API is the same for all methods that may be assigned to this +attribute. + +\* some readers may object that the API is *not* actually the same +because the type of the ``dc`` attribute is different (Series +vs. DataFrame)! + +Inferring models +~~~~~~~~~~~~~~~~ + +How does ModelChain infer the appropriate model types? ModelChain uses a +series of methods (ModelChain.infer_dc_model, ModelChain.infer_ac_model, +etc.) that examine the user-supplied PVSystem object. The inference +methods use set logic to assign one of the model-specific methods, such +as ModelChain.sapm or ModelChain.snlinverter, to the universal method +names ModelChain.dc_model and ModelChain.ac_model. A few examples are +shown below. + +.. ipython:: python + + mc.infer_dc_model?? + +.. ipython:: python + + mc.infer_ac_model?? + +User-defined models +------------------- + +Users may also write their own functions and pass them as arguments to +ModelChain. The first argument of the function must be a ModelChain +instance. For example, the functions below implement the PVUSA model and +a wrapper function appropriate for use with ModelChain. This follows the +pattern of implementing the core models using the simplest possible +functions, and then implementing wrappers to make them easier to use in +specific applications. Of course, you could implement it in a single +function if you wanted to. + +.. ipython:: python + + def pvusa(poa_global, wind_speed, temp_air, a, b, c, d): + """ + Calculates system power according to the PVUSA equation + + P = I * (a + b*I + c*W + d*T) + + where + P is the output power, + I is the plane of array irradiance, + W is the wind speed, and + T is the temperature + a, b, c, d are empirically derived parameters. + """ + return poa_global * (a + b*poa_global + c*wind_speed + d*temp_air) + + + def pvusa_mc_wrapper(mc): + # calculate the dc power and assign it to mc.dc + mc.dc = pvusa(mc.total_irrad['poa_global'], mc.weather['wind_speed'], mc.weather['temp_air'], + mc.system.module_parameters['a'], mc.system.module_parameters['b'], + mc.system.module_parameters['c'], mc.system.module_parameters['d']) + + # returning mc is optional, but enables method chaining + return mc + + + def pvusa_ac_mc_wrapper(mc): + # keep it simple + mc.ac = mc.dc + return mc + +.. ipython:: python + + module_parameters = {'a': 0.2, 'b': 0.00001, 'c': 0.001, 'd': -0.00005} + pvusa_system = PVSystem(module_parameters=module_parameters) + + mc = ModelChain(pvusa_system, location, + dc_model=pvusa_mc_wrapper, ac_model=pvusa_ac_mc_wrapper, + aoi_model='no_loss', spectral_model='no_loss') + +A ModelChain object uses Python’s functools.partial function to assign +itself as the argument to the user-supplied functions. + +.. ipython:: python + + mc.dc_model.func + +The end result is that ModelChain.run_model works as expected! + +.. ipython:: python + + mc.run_model(times=weather.index, weather=weather) + mc.dc From 799aa4edaa0cf117902d605e5973cfe6f7004e03 Mon Sep 17 00:00:00 2001 From: Will Holmgren Date: Sat, 8 Sep 2018 15:06:19 -0700 Subject: [PATCH 2/9] some clean up --- docs/sphinx/source/modelchain.rst | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/sphinx/source/modelchain.rst b/docs/sphinx/source/modelchain.rst index c49fdb3f2a..a8d5ef5081 100644 --- a/docs/sphinx/source/modelchain.rst +++ b/docs/sphinx/source/modelchain.rst @@ -7,7 +7,7 @@ interface for standardized PV modeling. The class aims to automate much of the modeling process while providing user-control and remaining extensible. This guide aims to build users' understanding of the ModelChain class. It assumes some familiarity with object-oriented -ipython in Python, but most information should be understandable even +code in Python, but most information should be understandable even without a solid understanding of classes. A :py:class:`~.modelchain.ModelChain` is composed of a @@ -142,7 +142,7 @@ workflows. :okexcept: mc = ModelChain(poorly_specified_system, location, - dc_model='singlediode', ac_model='snlinverter', + dc_model='desoto', ac_model='snlinverter', aoi_model='physical', spectral_model='no_loss') print(mc) @@ -218,14 +218,14 @@ Demystifying ModelChain internals --------------------------------- The ModelChain class has a lot going in inside it in order to make -users' ipython as simple as possible. +users' code as simple as possible. The key parts of ModelChain are: -1. The :py:meth:`ModelChain.run_model() <.ModelChain.run_model>` method -1. A set of methods that wrap and call the PVSystem methods. -1. A set of methods that inspect user-supplied objects to determine - the appropriate default models. + 1. The :py:meth:`ModelChain.run_model() <.ModelChain.run_model>` method + 2. A set of methods that wrap and call the PVSystem methods. + 3. A set of methods that inspect user-supplied objects to determine + the appropriate default models. run_model ~~~~~~~~~ @@ -264,7 +264,7 @@ then be passed to :py:meth:`~pvlib.modelchain.ModelChain.run_model`. Wrapping methods into a unified API ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Readers may notice that the source ipython of the ModelChain.run_model +Readers may notice that the source code of the ModelChain.run_model method is model-agnostic. ModelChain.run_model calls generic methods such as ``self.dc_model`` rather than a specific model such as ``singlediode``. So how does the ModelChain.run_model know what models @@ -285,7 +285,7 @@ the ModelChain.pvwatts_dc method is shown below. Its only argument is The ModelChain.pvwatts_dc method calls the pvwatts_dc method of the PVSystem object that we supplied using data that is stored in its own ``effective_irradiance`` and ``temps`` attributes. Then it assigns the -result to the ``dc`` attribute of the ModelChain object. The ipython below +result to the ``dc`` attribute of the ModelChain object. The code below shows a simple example of this. .. ipython:: python From 26a50514bfa118f784ecdc4fbe32e967e85838e8 Mon Sep 17 00:00:00 2001 From: Will Holmgren Date: Sat, 8 Sep 2018 21:10:16 -0700 Subject: [PATCH 3/9] remove ipywidgets and docutils from doc env --- docs/environment.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/environment.yml b/docs/environment.yml index c871a85cf3..aaa0058a02 100644 --- a/docs/environment.yml +++ b/docs/environment.yml @@ -13,7 +13,6 @@ dependencies: - ephem - numba - ipython=6.3 - - ipywidgets - numpydoc - matplotlib=2.2.2 - siphon=0.7.0 @@ -21,4 +20,3 @@ dependencies: - netCDF4=1.3.1 - hdf4=4.2.12 - sphinx_rtd_theme - - docutils From b27f733efdd0ae66af7797525c1c0bf412006d4f Mon Sep 17 00:00:00 2001 From: Will Holmgren Date: Sun, 9 Sep 2018 09:40:44 -0700 Subject: [PATCH 4/9] remove outdated section --- docs/sphinx/source/modelchain.rst | 38 +++++-------------------------- 1 file changed, 6 insertions(+), 32 deletions(-) diff --git a/docs/sphinx/source/modelchain.rst b/docs/sphinx/source/modelchain.rst index a8d5ef5081..d10a581b87 100644 --- a/docs/sphinx/source/modelchain.rst +++ b/docs/sphinx/source/modelchain.rst @@ -133,32 +133,6 @@ will throw an error when we try to create it. ModelChain(poorly_specified_system, location) -If our goal is simply to get the object constructed, we can specify the -models that the ModelChain should use. We’ll have to fill in missing -data on the PVSystem object later, but maybe that’s desirable in some -workflows. - -.. ipython:: python - :okexcept: - - mc = ModelChain(poorly_specified_system, location, - dc_model='desoto', ac_model='snlinverter', - aoi_model='physical', spectral_model='no_loss') - print(mc) - -As expected, without additional information, the -:py:meth:`~.ModelChain.run_model` method fails at run time. - -.. ipython:: python - :okexcept: - - mc.run_model(times=weather.index, weather=weather) - -The ModelChain attempted to execute the PVSystem object's -:py:meth:`~pvlib.pvsystem.PVSystem.singlediode` method, and the method -failed because the object's ``module_parameters`` did not include the -data necessary to run the model. - Next, we define a PVSystem with a module from the SAPM database and an inverter from the CEC database. ModelChain will examine the PVSystem object’s properties and determine that it should choose the SAPM DC @@ -172,7 +146,7 @@ model, AC model, AOI loss model, and spectral loss model. .. ipython:: python - mc.run_model(times=weather.index, weather=weather) + mc.run_model(times=weather.index, weather=weather); mc.ac Alternatively, we could have specified single diode or PVWatts related @@ -192,7 +166,7 @@ information to determine which of those models to choose. .. ipython:: python - mc.run_model(times=weather.index, weather=weather) + mc.run_model(times=weather.index, weather=weather); mc.ac User-supplied keyword arguments override ModelChain’s inspection @@ -207,7 +181,7 @@ functions for a PVSystem that contains SAPM-specific parameters. .. ipython:: python - mc.run_model(times=weather.index, weather=weather) + mc.run_model(times=weather.index, weather=weather); mc.ac Of course, these choices can also lead to failure when executing @@ -301,7 +275,7 @@ shows a simple example of this. mc.temps = pd.DataFrame({'temp_cell': 50, 'temp_module': 50}, index=[pd.Timestamp('20170401 1200-0700')]) # run ModelChain.pvwatts_dc and look at the result - mc.pvwatts_dc() + mc.pvwatts_dc(); mc.dc The ModelChain.sapm method works similarly to the ModelChain.pvwatts_dc @@ -333,7 +307,7 @@ PVSystem.scale_voltage_current_power method. mc.temps = pd.DataFrame({'temp_cell': 50, 'temp_module': 50}, index=[pd.Timestamp('20170401 1200-0700')]) # run ModelChain.sapm and look at the result - mc.sapm() + mc.sapm(); mc.dc We’ve established that the ``ModelChain.pvwatts_dc`` and @@ -450,5 +424,5 @@ The end result is that ModelChain.run_model works as expected! .. ipython:: python - mc.run_model(times=weather.index, weather=weather) + mc.run_model(times=weather.index, weather=weather); mc.dc From fa5ba864b5f25a98d2e151f40a286e1d8b7321f4 Mon Sep 17 00:00:00 2001 From: Will Holmgren Date: Sun, 9 Sep 2018 09:40:56 -0700 Subject: [PATCH 5/9] hacks to make other pages compile --- docs/sphinx/source/conf.py | 2 +- docs/sphinx/source/pvsystem.rst | 10 ++++++---- docs/sphinx/source/timetimezones.rst | 11 +++++++---- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/docs/sphinx/source/conf.py b/docs/sphinx/source/conf.py index b0373a1483..4c4164e23a 100644 --- a/docs/sphinx/source/conf.py +++ b/docs/sphinx/source/conf.py @@ -311,7 +311,7 @@ def setup(app): # Example configuration for intersphinx: refer to the Python standard library. intersphinx_mapping = { - 'python': ('https://docs.python.org/3.5/', None), + 'python': ('https://docs.python.org/3.7/', None), 'pandas': ('http://pandas.pydata.org/pandas-docs/stable/', None), 'numpy': ('http://docs.scipy.org/doc/numpy/', None), } diff --git a/docs/sphinx/source/pvsystem.rst b/docs/sphinx/source/pvsystem.rst index d3c6871819..c11de65c8a 100644 --- a/docs/sphinx/source/pvsystem.rst +++ b/docs/sphinx/source/pvsystem.rst @@ -43,11 +43,13 @@ Intrinsic data is stored in object attributes. For example, the data that describes a PV system's module parameters is stored in `PVSystem.module_parameters`. -.. ipython:: python +.. ipython:: + + In [1]: module_parameters = {'pdc0': 10, 'gamma_pdc': -0.004} + + In [1]: system = pvsystem.PVSystem(module_parameters=module_parameters) - module_parameters = {'pdc0': 10, 'gamma_pdc': -0.004} - system = pvsystem.PVSystem(module_parameters=module_parameters) - print(system.module_parameters) + In [1]: print(system.module_parameters) Extrinsic data is passed to a PVSystem as method arguments. For example, the :py:meth:`~pvlib.pvsystem.PVSystem.pvwatts_dc` method accepts extrinsic diff --git a/docs/sphinx/source/timetimezones.rst b/docs/sphinx/source/timetimezones.rst index a28867cce6..4e0ac2bf6f 100644 --- a/docs/sphinx/source/timetimezones.rst +++ b/docs/sphinx/source/timetimezones.rst @@ -24,11 +24,14 @@ using the corresponding pandas functionality where possible. First, we'll import the libraries that we'll use to explore the basic time and time zone functionality in python and pvlib. -.. ipython:: python +.. ipython:: + + In [1]: import datetime + + In [1]: import pandas as pd + + In [1]: import pytz - import datetime - import pandas as pd - import pytz Finding a time zone ******************* From 34da738bc0cc5f3dd42941e6937d52ef71129556 Mon Sep 17 00:00:00 2001 From: Will Holmgren Date: Sun, 9 Sep 2018 09:47:05 -0700 Subject: [PATCH 6/9] other doc clean up --- pvlib/clearsky.py | 3 +-- pvlib/solarposition.py | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/pvlib/clearsky.py b/pvlib/clearsky.py index d6e92ec297..2d85fbe880 100644 --- a/pvlib/clearsky.py +++ b/pvlib/clearsky.py @@ -829,8 +829,7 @@ def bird(zenith, airmass_relative, aod380, aod500, precipitable_water, `SERI/TR-642-761 `_ - `Error Reports - `_ + `Error Reports `_ """ etr = dni_extra # extraradiation ze_rad = np.deg2rad(zenith) # zenith in radians diff --git a/pvlib/solarposition.py b/pvlib/solarposition.py index aeed47486e..9986b1de72 100644 --- a/pvlib/solarposition.py +++ b/pvlib/solarposition.py @@ -934,8 +934,7 @@ def equation_of_time_pvcdrom(dayofyear): `PVCDROM`_ is a website by Solar Power Lab at Arizona State University (ASU) - .. _PVCDROM: - http://www.pveducation.org/pvcdrom/2-properties-sunlight/solar-time + .. _PVCDROM: http://www.pveducation.org/pvcdrom/2-properties-sunlight/solar-time Parameters ---------- From 6ff6b8fcbdaad9f54481217a8b5d8b3117ad7a1b Mon Sep 17 00:00:00 2001 From: Will Holmgren Date: Sun, 9 Sep 2018 10:35:00 -0700 Subject: [PATCH 7/9] 3 spaces for directive options --- docs/sphinx/source/modelchain.rst | 2 +- docs/sphinx/source/pvsystem.rst | 12 +++++------- docs/sphinx/source/timetimezones.rst | 14 ++++++-------- 3 files changed, 12 insertions(+), 16 deletions(-) diff --git a/docs/sphinx/source/modelchain.rst b/docs/sphinx/source/modelchain.rst index d10a581b87..a1eea00ee9 100644 --- a/docs/sphinx/source/modelchain.rst +++ b/docs/sphinx/source/modelchain.rst @@ -129,7 +129,7 @@ able to automatically determine its set of models, so the ModelChain will throw an error when we try to create it. .. ipython:: python - :okexcept: + :okexcept: ModelChain(poorly_specified_system, location) diff --git a/docs/sphinx/source/pvsystem.rst b/docs/sphinx/source/pvsystem.rst index c11de65c8a..ef06b2a2dd 100644 --- a/docs/sphinx/source/pvsystem.rst +++ b/docs/sphinx/source/pvsystem.rst @@ -4,7 +4,7 @@ PVSystem ======== .. ipython:: python - :suppress: + :suppress: import pandas as pd from pvlib import pvsystem @@ -43,13 +43,11 @@ Intrinsic data is stored in object attributes. For example, the data that describes a PV system's module parameters is stored in `PVSystem.module_parameters`. -.. ipython:: - - In [1]: module_parameters = {'pdc0': 10, 'gamma_pdc': -0.004} - - In [1]: system = pvsystem.PVSystem(module_parameters=module_parameters) +.. ipython:: python - In [1]: print(system.module_parameters) + module_parameters = {'pdc0': 10, 'gamma_pdc': -0.004} + system = pvsystem.PVSystem(module_parameters=module_parameters) + print(system.module_parameters) Extrinsic data is passed to a PVSystem as method arguments. For example, the :py:meth:`~pvlib.pvsystem.PVSystem.pvwatts_dc` method accepts extrinsic diff --git a/docs/sphinx/source/timetimezones.rst b/docs/sphinx/source/timetimezones.rst index 4e0ac2bf6f..de1935e33b 100644 --- a/docs/sphinx/source/timetimezones.rst +++ b/docs/sphinx/source/timetimezones.rst @@ -24,13 +24,11 @@ using the corresponding pandas functionality where possible. First, we'll import the libraries that we'll use to explore the basic time and time zone functionality in python and pvlib. -.. ipython:: - - In [1]: import datetime - - In [1]: import pandas as pd +.. ipython:: python - In [1]: import pytz + import datetime + import pandas as pd + import pytz Finding a time zone @@ -97,7 +95,7 @@ It does not make sense to convert a time stamp that has not been localized, and pandas will raise an exception if you try to do so. .. ipython:: python - :okexcept: + :okexcept: midnight = pd.Timestamp('2015-1-1 00:00') midnight.tz_convert('UTC') @@ -239,7 +237,7 @@ passed to ``Timestamp``. You cannot localize a native Python date object. .. ipython:: python - :okexcept: + :okexcept: # fail pytz.timezone('US/Mountain').localize(naive_python_date) From 37584a74a0910f3886abd40b76dbb2f8daa10164 Mon Sep 17 00:00:00 2001 From: Will Holmgren Date: Sun, 9 Sep 2018 11:25:44 -0700 Subject: [PATCH 8/9] use bare .. ipython:: (no python) --- docs/sphinx/source/pvsystem.rst | 18 +-- docs/sphinx/source/timetimezones.rst | 161 ++++++++++++++++++++++++--- 2 files changed, 156 insertions(+), 23 deletions(-) diff --git a/docs/sphinx/source/pvsystem.rst b/docs/sphinx/source/pvsystem.rst index ef06b2a2dd..eb9e795082 100644 --- a/docs/sphinx/source/pvsystem.rst +++ b/docs/sphinx/source/pvsystem.rst @@ -3,11 +3,12 @@ PVSystem ======== -.. ipython:: python +.. ipython:: :suppress: - import pandas as pd - from pvlib import pvsystem + In [1]: import pandas as pd + + In [2]: from pvlib import pvsystem The :py:class:`~pvlib.pvsystem.PVSystem` class wraps many of the functions in the :py:mod:`~pvlib.pvsystem` module. This simplifies the @@ -43,11 +44,14 @@ Intrinsic data is stored in object attributes. For example, the data that describes a PV system's module parameters is stored in `PVSystem.module_parameters`. -.. ipython:: python +.. ipython:: + + In [3]: module_parameters = {'pdc0': 10, 'gamma_pdc': -0.004} + + In [4]: system = pvsystem.PVSystem(module_parameters=module_parameters) - module_parameters = {'pdc0': 10, 'gamma_pdc': -0.004} - system = pvsystem.PVSystem(module_parameters=module_parameters) - print(system.module_parameters) + In [5]: print(system.module_parameters) + {'pdc0': 10, 'gamma_pdc': -0.004} Extrinsic data is passed to a PVSystem as method arguments. For example, the :py:meth:`~pvlib.pvsystem.PVSystem.pvwatts_dc` method accepts extrinsic diff --git a/docs/sphinx/source/timetimezones.rst b/docs/sphinx/source/timetimezones.rst index de1935e33b..2816e7c3dd 100644 --- a/docs/sphinx/source/timetimezones.rst +++ b/docs/sphinx/source/timetimezones.rst @@ -24,11 +24,13 @@ using the corresponding pandas functionality where possible. First, we'll import the libraries that we'll use to explore the basic time and time zone functionality in python and pvlib. -.. ipython:: python +.. ipython:: + + In [1]: import datetime + + In [2]: import pandas as pd - import datetime - import pandas as pd - import pytz + In [3]: import pytz Finding a time zone @@ -38,10 +40,43 @@ pytz is based on the Olson time zone database. You can obtain a list of all valid time zone strings with ``pytz.all_timezones``. It's a long list, so we only print every 20th time zone. -.. ipython:: python - - len(pytz.all_timezones) - pytz.all_timezones[::20] +.. ipython:: + + In [4]: len(pytz.all_timezones) + Out[4]: 591 + + In [5]: pytz.all_timezones[::20] + Out[5]: + ['Africa/Abidjan', + 'Africa/Douala', + 'Africa/Mbabane', + 'America/Argentina/Catamarca', + 'America/Belize', + 'America/Curacao', + 'America/Guatemala', + 'America/Kentucky/Louisville', + 'America/Mexico_City', + 'America/Port-au-Prince', + 'America/Sitka', + 'Antarctica/Casey', + 'Asia/Ashkhabad', + 'Asia/Dubai', + 'Asia/Khandyga', + 'Asia/Qatar', + 'Asia/Ujung_Pandang', + 'Atlantic/South_Georgia', + 'Australia/South', + 'Chile/Continental', + 'Etc/GMT+8', + 'Etc/UTC', + 'Europe/Helsinki', + 'Europe/Podgorica', + 'Europe/Vilnius', + 'Indian/Comoro', + 'NZ-CHAT', + 'Pacific/Johnston', + 'Pacific/Tahiti', + 'US/Hawaii'] Wikipedia's `List of tz database time zones `_ is also @@ -49,15 +84,80 @@ good reference. The ``pytz.country_timezones`` function is useful, too. -.. ipython:: python - - pytz.country_timezones('US') +.. ipython:: + + In [6]: pytz.country_timezones('US') + Out[6]: + ['America/New_York', + 'America/Detroit', + 'America/Kentucky/Louisville', + 'America/Kentucky/Monticello', + 'America/Indiana/Indianapolis', + 'America/Indiana/Vincennes', + 'America/Indiana/Winamac', + 'America/Indiana/Marengo', + 'America/Indiana/Petersburg', + 'America/Indiana/Vevay', + 'America/Chicago', + 'America/Indiana/Tell_City', + 'America/Indiana/Knox', + 'America/Menominee', + 'America/North_Dakota/Center', + 'America/North_Dakota/New_Salem', + 'America/North_Dakota/Beulah', + 'America/Denver', + 'America/Boise', + 'America/Phoenix', + 'America/Los_Angeles', + 'America/Anchorage', + 'America/Juneau', + 'America/Sitka', + 'America/Metlakatla', + 'America/Yakutat', + 'America/Nome', + 'America/Adak', + 'Pacific/Honolulu'] And don't forget about Python's :py:func:`python:filter` function. -.. ipython:: python - - list(filter(lambda x: 'GMT' in x, pytz.all_timezones)) +.. ipython:: + + In [7]: list(filter(lambda x: 'GMT' in x, pytz.all_timezones)) + Out[7]: + ['Etc/GMT', + 'Etc/GMT+0', + 'Etc/GMT+1', + 'Etc/GMT+10', + 'Etc/GMT+11', + 'Etc/GMT+12', + 'Etc/GMT+2', + 'Etc/GMT+3', + 'Etc/GMT+4', + 'Etc/GMT+5', + 'Etc/GMT+6', + 'Etc/GMT+7', + 'Etc/GMT+8', + 'Etc/GMT+9', + 'Etc/GMT-0', + 'Etc/GMT-1', + 'Etc/GMT-10', + 'Etc/GMT-11', + 'Etc/GMT-12', + 'Etc/GMT-13', + 'Etc/GMT-14', + 'Etc/GMT-2', + 'Etc/GMT-3', + 'Etc/GMT-4', + 'Etc/GMT-5', + 'Etc/GMT-6', + 'Etc/GMT-7', + 'Etc/GMT-8', + 'Etc/GMT-9', + 'Etc/GMT0', + 'GMT', + 'GMT+0', + 'GMT-0', + 'GMT0'] Note that while pytz has ``'EST'`` and ``'MST'``, it does not have ``'PST'``. Use ``'Etc/GMT+8'`` instead, or see :ref:`fixedoffsets`. @@ -71,9 +171,10 @@ surrounding them; see the pandas documentation for more information. First, create a time zone naive pandas.Timestamp. -.. ipython:: python +.. ipython:: - pd.Timestamp('2015-1-1 00:00') + In [8]: pd.Timestamp('2015-1-1 00:00') + Out[8]: Timestamp('2015-01-01 00:00:00') You can specify the time zone using the ``tz`` keyword argument or the ``tz_localize`` method of Timestamp and DatetimeIndex objects. @@ -81,6 +182,7 @@ You can specify the time zone using the ``tz`` keyword argument or the .. ipython:: python pd.Timestamp('2015-1-1 00:00', tz='America/Denver') + pd.Timestamp('2015-1-1 00:00').tz_localize('America/Denver') Localized Timestamps can be converted from one time zone to another. @@ -88,7 +190,9 @@ Localized Timestamps can be converted from one time zone to another. .. ipython:: python midnight_mst = pd.Timestamp('2015-1-1 00:00', tz='America/Denver') + corresponding_utc = midnight_mst.tz_convert('UTC') # returns a new Timestamp + corresponding_utc It does not make sense to convert a time stamp that has not been @@ -98,6 +202,7 @@ localized, and pandas will raise an exception if you try to do so. :okexcept: midnight = pd.Timestamp('2015-1-1 00:00') + midnight.tz_convert('UTC') The difference between ``tz_localize`` and ``tz_convert`` is a common @@ -116,6 +221,7 @@ Note the UTC offset in winter... .. ipython:: python pd.Timestamp('2015-1-1 00:00').tz_localize('US/Mountain') + pd.Timestamp('2015-1-1 00:00').tz_localize('Etc/GMT+7') vs. the UTC offset in summer... @@ -123,6 +229,7 @@ vs. the UTC offset in summer... .. ipython:: python pd.Timestamp('2015-6-1 00:00').tz_localize('US/Mountain') + pd.Timestamp('2015-6-1 00:00').tz_localize('Etc/GMT+7') pandas and pytz make this time zone handling possible because pandas @@ -132,6 +239,7 @@ Here is the pandas time representation of the integers 1 and 1e9. .. ipython:: python pd.Timestamp(1) + pd.Timestamp(1e9) So if we specify times consistent with the specified time zone, pandas @@ -266,12 +374,16 @@ Let's first examine how pvlib handles time when it imports a TMY3 file. .. ipython:: python import os + import inspect + import pvlib # some gymnastics to find the example file pvlib_abspath = os.path.dirname(os.path.abspath(inspect.getfile(pvlib))) + file_abspath = os.path.join(pvlib_abspath, 'data', '703165TY.csv') + tmy3_data, tmy3_metadata = pvlib.tmy.readtmy3(file_abspath) tmy3_metadata @@ -307,10 +419,15 @@ DataFrame's index since the index has been localized. ax = solar_position.loc[solar_position.index[0:24], ['apparent_zenith', 'apparent_elevation', 'azimuth']].plot() ax.legend(loc=1); + ax.axhline(0, color='darkgray'); # add 0 deg line for sunrise/sunset + ax.axhline(180, color='darkgray'); # add 180 deg line for azimuth at solar noon + ax.set_ylim(-60, 200); # zoom in, but cuts off full azimuth range + ax.set_xlabel('Local time ({})'.format(solar_position.index.tz)); + @savefig solar-position.png width=6in ax.set_ylabel('(degrees)'); @@ -329,6 +446,7 @@ below? The solar position calculator will assume UTC time. .. ipython:: python index = pd.DatetimeIndex(start='1997-01-01 01:00', freq='1h', periods=24) + index solar_position_notz = pvlib.solarposition.get_solarposition(index, @@ -338,10 +456,15 @@ below? The solar position calculator will assume UTC time. ax = solar_position_notz.loc[solar_position_notz.index[0:24], ['apparent_zenith', 'apparent_elevation', 'azimuth']].plot() ax.legend(loc=1); + ax.axhline(0, color='darkgray'); # add 0 deg line for sunrise/sunset + ax.axhline(180, color='darkgray'); # add 180 deg line for azimuth at solar noon + ax.set_ylim(-60, 200); # zoom in, but cuts off full azimuth range + ax.set_xlabel('Time (UTC)'); + @savefig solar-position-nolocal.png width=6in ax.set_ylabel('(degrees)'); @@ -356,6 +479,7 @@ UTC, and then convert it to the desired time zone. .. ipython:: python fixed_tz = pytz.FixedOffset(tmy3_metadata['TZ'] * 60) + solar_position_hack = solar_position_notz.tz_localize('UTC').tz_convert(fixed_tz) solar_position_hack.index @@ -363,10 +487,15 @@ UTC, and then convert it to the desired time zone. ax = solar_position_hack.loc[solar_position_hack.index[0:24], ['apparent_zenith', 'apparent_elevation', 'azimuth']].plot() ax.legend(loc=1); + ax.axhline(0, color='darkgray'); # add 0 deg line for sunrise/sunset + ax.axhline(180, color='darkgray'); # add 180 deg line for azimuth at solar noon + ax.set_ylim(-60, 200); # zoom in, but cuts off full azimuth range + ax.set_xlabel('Local time ({})'.format(solar_position_hack.index.tz)); + @savefig solar-position-hack.png width=6in ax.set_ylabel('(degrees)'); From af7cdffe64229ad1e106ec4e615cbb2213d7f119 Mon Sep 17 00:00:00 2001 From: Will Holmgren Date: Sun, 9 Sep 2018 14:36:27 -0700 Subject: [PATCH 9/9] enabled install option on rtd. now removing previously added output --- docs/sphinx/source/pvsystem.rst | 19 ++-- docs/sphinx/source/timetimezones.rst | 161 +++------------------------ 2 files changed, 25 insertions(+), 155 deletions(-) diff --git a/docs/sphinx/source/pvsystem.rst b/docs/sphinx/source/pvsystem.rst index eb9e795082..aa10d03767 100644 --- a/docs/sphinx/source/pvsystem.rst +++ b/docs/sphinx/source/pvsystem.rst @@ -3,12 +3,12 @@ PVSystem ======== -.. ipython:: +.. ipython:: python :suppress: - In [1]: import pandas as pd + import pandas as pd + from pvlib import pvsystem - In [2]: from pvlib import pvsystem The :py:class:`~pvlib.pvsystem.PVSystem` class wraps many of the functions in the :py:mod:`~pvlib.pvsystem` module. This simplifies the @@ -44,14 +44,11 @@ Intrinsic data is stored in object attributes. For example, the data that describes a PV system's module parameters is stored in `PVSystem.module_parameters`. -.. ipython:: - - In [3]: module_parameters = {'pdc0': 10, 'gamma_pdc': -0.004} - - In [4]: system = pvsystem.PVSystem(module_parameters=module_parameters) +.. ipython:: python - In [5]: print(system.module_parameters) - {'pdc0': 10, 'gamma_pdc': -0.004} + module_parameters = {'pdc0': 10, 'gamma_pdc': -0.004} + system = pvsystem.PVSystem(module_parameters=module_parameters) + print(system.module_parameters) Extrinsic data is passed to a PVSystem as method arguments. For example, the :py:meth:`~pvlib.pvsystem.PVSystem.pvwatts_dc` method accepts extrinsic @@ -122,6 +119,7 @@ arguments. aoi = system.get_aoi(30, 180) print(aoi) + `module_parameters` and `inverter_parameters` contain the data necessary for computing DC and AC power using one of the available PVSystem methods. These are typically specified using data from @@ -137,6 +135,7 @@ the :py:func:`~pvlib.pvsystem.retrieve_sam` function: inverter_parameters = inverters['ABB__MICRO_0_25_I_OUTD_US_208_208V__CEC_2014_'] system = pvsystem.PVSystem(module_parameters=module_parameters, inverter_parameters=inverter_parameters) + The module and/or inverter parameters can also be specified manually. This is useful for specifying modules and inverters that are not included in the supplied databases. It is also useful for specifying diff --git a/docs/sphinx/source/timetimezones.rst b/docs/sphinx/source/timetimezones.rst index 2816e7c3dd..de1935e33b 100644 --- a/docs/sphinx/source/timetimezones.rst +++ b/docs/sphinx/source/timetimezones.rst @@ -24,13 +24,11 @@ using the corresponding pandas functionality where possible. First, we'll import the libraries that we'll use to explore the basic time and time zone functionality in python and pvlib. -.. ipython:: - - In [1]: import datetime - - In [2]: import pandas as pd +.. ipython:: python - In [3]: import pytz + import datetime + import pandas as pd + import pytz Finding a time zone @@ -40,43 +38,10 @@ pytz is based on the Olson time zone database. You can obtain a list of all valid time zone strings with ``pytz.all_timezones``. It's a long list, so we only print every 20th time zone. -.. ipython:: - - In [4]: len(pytz.all_timezones) - Out[4]: 591 - - In [5]: pytz.all_timezones[::20] - Out[5]: - ['Africa/Abidjan', - 'Africa/Douala', - 'Africa/Mbabane', - 'America/Argentina/Catamarca', - 'America/Belize', - 'America/Curacao', - 'America/Guatemala', - 'America/Kentucky/Louisville', - 'America/Mexico_City', - 'America/Port-au-Prince', - 'America/Sitka', - 'Antarctica/Casey', - 'Asia/Ashkhabad', - 'Asia/Dubai', - 'Asia/Khandyga', - 'Asia/Qatar', - 'Asia/Ujung_Pandang', - 'Atlantic/South_Georgia', - 'Australia/South', - 'Chile/Continental', - 'Etc/GMT+8', - 'Etc/UTC', - 'Europe/Helsinki', - 'Europe/Podgorica', - 'Europe/Vilnius', - 'Indian/Comoro', - 'NZ-CHAT', - 'Pacific/Johnston', - 'Pacific/Tahiti', - 'US/Hawaii'] +.. ipython:: python + + len(pytz.all_timezones) + pytz.all_timezones[::20] Wikipedia's `List of tz database time zones `_ is also @@ -84,80 +49,15 @@ good reference. The ``pytz.country_timezones`` function is useful, too. -.. ipython:: - - In [6]: pytz.country_timezones('US') - Out[6]: - ['America/New_York', - 'America/Detroit', - 'America/Kentucky/Louisville', - 'America/Kentucky/Monticello', - 'America/Indiana/Indianapolis', - 'America/Indiana/Vincennes', - 'America/Indiana/Winamac', - 'America/Indiana/Marengo', - 'America/Indiana/Petersburg', - 'America/Indiana/Vevay', - 'America/Chicago', - 'America/Indiana/Tell_City', - 'America/Indiana/Knox', - 'America/Menominee', - 'America/North_Dakota/Center', - 'America/North_Dakota/New_Salem', - 'America/North_Dakota/Beulah', - 'America/Denver', - 'America/Boise', - 'America/Phoenix', - 'America/Los_Angeles', - 'America/Anchorage', - 'America/Juneau', - 'America/Sitka', - 'America/Metlakatla', - 'America/Yakutat', - 'America/Nome', - 'America/Adak', - 'Pacific/Honolulu'] +.. ipython:: python + + pytz.country_timezones('US') And don't forget about Python's :py:func:`python:filter` function. -.. ipython:: - - In [7]: list(filter(lambda x: 'GMT' in x, pytz.all_timezones)) - Out[7]: - ['Etc/GMT', - 'Etc/GMT+0', - 'Etc/GMT+1', - 'Etc/GMT+10', - 'Etc/GMT+11', - 'Etc/GMT+12', - 'Etc/GMT+2', - 'Etc/GMT+3', - 'Etc/GMT+4', - 'Etc/GMT+5', - 'Etc/GMT+6', - 'Etc/GMT+7', - 'Etc/GMT+8', - 'Etc/GMT+9', - 'Etc/GMT-0', - 'Etc/GMT-1', - 'Etc/GMT-10', - 'Etc/GMT-11', - 'Etc/GMT-12', - 'Etc/GMT-13', - 'Etc/GMT-14', - 'Etc/GMT-2', - 'Etc/GMT-3', - 'Etc/GMT-4', - 'Etc/GMT-5', - 'Etc/GMT-6', - 'Etc/GMT-7', - 'Etc/GMT-8', - 'Etc/GMT-9', - 'Etc/GMT0', - 'GMT', - 'GMT+0', - 'GMT-0', - 'GMT0'] +.. ipython:: python + + list(filter(lambda x: 'GMT' in x, pytz.all_timezones)) Note that while pytz has ``'EST'`` and ``'MST'``, it does not have ``'PST'``. Use ``'Etc/GMT+8'`` instead, or see :ref:`fixedoffsets`. @@ -171,10 +71,9 @@ surrounding them; see the pandas documentation for more information. First, create a time zone naive pandas.Timestamp. -.. ipython:: +.. ipython:: python - In [8]: pd.Timestamp('2015-1-1 00:00') - Out[8]: Timestamp('2015-01-01 00:00:00') + pd.Timestamp('2015-1-1 00:00') You can specify the time zone using the ``tz`` keyword argument or the ``tz_localize`` method of Timestamp and DatetimeIndex objects. @@ -182,7 +81,6 @@ You can specify the time zone using the ``tz`` keyword argument or the .. ipython:: python pd.Timestamp('2015-1-1 00:00', tz='America/Denver') - pd.Timestamp('2015-1-1 00:00').tz_localize('America/Denver') Localized Timestamps can be converted from one time zone to another. @@ -190,9 +88,7 @@ Localized Timestamps can be converted from one time zone to another. .. ipython:: python midnight_mst = pd.Timestamp('2015-1-1 00:00', tz='America/Denver') - corresponding_utc = midnight_mst.tz_convert('UTC') # returns a new Timestamp - corresponding_utc It does not make sense to convert a time stamp that has not been @@ -202,7 +98,6 @@ localized, and pandas will raise an exception if you try to do so. :okexcept: midnight = pd.Timestamp('2015-1-1 00:00') - midnight.tz_convert('UTC') The difference between ``tz_localize`` and ``tz_convert`` is a common @@ -221,7 +116,6 @@ Note the UTC offset in winter... .. ipython:: python pd.Timestamp('2015-1-1 00:00').tz_localize('US/Mountain') - pd.Timestamp('2015-1-1 00:00').tz_localize('Etc/GMT+7') vs. the UTC offset in summer... @@ -229,7 +123,6 @@ vs. the UTC offset in summer... .. ipython:: python pd.Timestamp('2015-6-1 00:00').tz_localize('US/Mountain') - pd.Timestamp('2015-6-1 00:00').tz_localize('Etc/GMT+7') pandas and pytz make this time zone handling possible because pandas @@ -239,7 +132,6 @@ Here is the pandas time representation of the integers 1 and 1e9. .. ipython:: python pd.Timestamp(1) - pd.Timestamp(1e9) So if we specify times consistent with the specified time zone, pandas @@ -374,16 +266,12 @@ Let's first examine how pvlib handles time when it imports a TMY3 file. .. ipython:: python import os - import inspect - import pvlib # some gymnastics to find the example file pvlib_abspath = os.path.dirname(os.path.abspath(inspect.getfile(pvlib))) - file_abspath = os.path.join(pvlib_abspath, 'data', '703165TY.csv') - tmy3_data, tmy3_metadata = pvlib.tmy.readtmy3(file_abspath) tmy3_metadata @@ -419,15 +307,10 @@ DataFrame's index since the index has been localized. ax = solar_position.loc[solar_position.index[0:24], ['apparent_zenith', 'apparent_elevation', 'azimuth']].plot() ax.legend(loc=1); - ax.axhline(0, color='darkgray'); # add 0 deg line for sunrise/sunset - ax.axhline(180, color='darkgray'); # add 180 deg line for azimuth at solar noon - ax.set_ylim(-60, 200); # zoom in, but cuts off full azimuth range - ax.set_xlabel('Local time ({})'.format(solar_position.index.tz)); - @savefig solar-position.png width=6in ax.set_ylabel('(degrees)'); @@ -446,7 +329,6 @@ below? The solar position calculator will assume UTC time. .. ipython:: python index = pd.DatetimeIndex(start='1997-01-01 01:00', freq='1h', periods=24) - index solar_position_notz = pvlib.solarposition.get_solarposition(index, @@ -456,15 +338,10 @@ below? The solar position calculator will assume UTC time. ax = solar_position_notz.loc[solar_position_notz.index[0:24], ['apparent_zenith', 'apparent_elevation', 'azimuth']].plot() ax.legend(loc=1); - ax.axhline(0, color='darkgray'); # add 0 deg line for sunrise/sunset - ax.axhline(180, color='darkgray'); # add 180 deg line for azimuth at solar noon - ax.set_ylim(-60, 200); # zoom in, but cuts off full azimuth range - ax.set_xlabel('Time (UTC)'); - @savefig solar-position-nolocal.png width=6in ax.set_ylabel('(degrees)'); @@ -479,7 +356,6 @@ UTC, and then convert it to the desired time zone. .. ipython:: python fixed_tz = pytz.FixedOffset(tmy3_metadata['TZ'] * 60) - solar_position_hack = solar_position_notz.tz_localize('UTC').tz_convert(fixed_tz) solar_position_hack.index @@ -487,15 +363,10 @@ UTC, and then convert it to the desired time zone. ax = solar_position_hack.loc[solar_position_hack.index[0:24], ['apparent_zenith', 'apparent_elevation', 'azimuth']].plot() ax.legend(loc=1); - ax.axhline(0, color='darkgray'); # add 0 deg line for sunrise/sunset - ax.axhline(180, color='darkgray'); # add 180 deg line for azimuth at solar noon - ax.set_ylim(-60, 200); # zoom in, but cuts off full azimuth range - ax.set_xlabel('Local time ({})'.format(solar_position_hack.index.tz)); - @savefig solar-position-hack.png width=6in ax.set_ylabel('(degrees)');