From 1c4b54640f510889227a205be6c916b7f96bae19 Mon Sep 17 00:00:00 2001 From: tobin-ford Date: Fri, 26 Jul 2024 15:59:19 -0600 Subject: [PATCH 1/9] create template tutorial --- .../Geospatial Templates.ipynb | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 tutorials_and_tools/tutorials_and_tools/Geospatial Templates.ipynb diff --git a/tutorials_and_tools/tutorials_and_tools/Geospatial Templates.ipynb b/tutorials_and_tools/tutorials_and_tools/Geospatial Templates.ipynb new file mode 100644 index 0000000..17c6daa --- /dev/null +++ b/tutorials_and_tools/tutorials_and_tools/Geospatial Templates.ipynb @@ -0,0 +1,70 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "import pvdeg\n", + "from pvdeg import TEST_DATA_DIR\n", + "import xarray as xr\n", + "import os" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Geospatial Templates\n", + "\n", + "When running a geospatial analysis using `pvdeg.geospatial.analysis` on arbitary `pvdeg` functions you will need to specify a template for the shape of the output data. This is because the input data comes with dimensions of gid and time while the output will have data in a different shape usually corresonding to coordinates.\n", + "- gid, identification number corresponding to an NSRDB datapoint's location \n", + "- time, timeseries corresponding to the hourly time indicies of NSRDB datapoint's yearly meteorological data.\n", + "\n", + "Follow the steps below to see how we generate templates before running the analysis.\n", + "\n", + "The only functions where this is not required are currently `pvdeg.standards.standoff`, `pvdeg.humidity.moduke` and, `letid.calc_letid_outdoors` as they are predefined within the package." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Creating Templates Programattically\n", + "\n", + "`pvdeg.geospatial.ouput_template` we can produce a template for our result data\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "weather_ds = xr.open_dataset(os.path.join(TEST_DATA_DIR, \"\"))" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "fem_diff", + "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.10.14" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} From 6fb7de0f682fcc54b0578a416bfaa601390160f4 Mon Sep 17 00:00:00 2001 From: tobin-ford Date: Fri, 26 Jul 2024 16:43:53 -0600 Subject: [PATCH 2/9] template example added to notebook --- pvdeg/geospatial.py | 2 +- tests/test_geospatial.py | 5 +- .../Geospatial Templates.ipynb | 615 +++++++++++++++++- 3 files changed, 612 insertions(+), 10 deletions(-) diff --git a/pvdeg/geospatial.py b/pvdeg/geospatial.py index 793ef66..208723a 100644 --- a/pvdeg/geospatial.py +++ b/pvdeg/geospatial.py @@ -312,7 +312,7 @@ def template_parameters(func): add_dims = {} else: - raise ValueError(f"No preset output template for function {func}.") + raise NotImplementedError(f"No preset output template for function {func}.") parameters = { "shapes": shapes, diff --git a/tests/test_geospatial.py b/tests/test_geospatial.py index a83c530..a03b109 100644 --- a/tests/test_geospatial.py +++ b/tests/test_geospatial.py @@ -5,12 +5,11 @@ import numpy as np import os -with open(r"C:\Users\tford\Downloads\summit-weather.pkl", 'rb') as f: +with open(os.path.join(TEST_DATA_DIR, 'summit-weather.pkl'), 'rb') as f: GEO_WEATHER = pickle.load(f) -GEO_META = pd.read_csv(r"C:\Users\tford\Downloads\summit-meta.csv", index_col=0) +GEO_META = pd.read_csv(os.path.join(TEST_DATA_DIR, 'summit-meta.csv'), index_col=0) -# refactor def test_analysis_standoff(): res_ds = pvdeg.geospatial.analysis( weather_ds=GEO_WEATHER, diff --git a/tutorials_and_tools/tutorials_and_tools/Geospatial Templates.ipynb b/tutorials_and_tools/tutorials_and_tools/Geospatial Templates.ipynb index 17c6daa..5a2d7f0 100644 --- a/tutorials_and_tools/tutorials_and_tools/Geospatial Templates.ipynb +++ b/tutorials_and_tools/tutorials_and_tools/Geospatial Templates.ipynb @@ -2,13 +2,15 @@ "cells": [ { "cell_type": "code", - "execution_count": 2, + "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "import pvdeg\n", "from pvdeg import TEST_DATA_DIR\n", + "import pickle\n", "import xarray as xr\n", + "import pandas as pd\n", "import os" ] }, @@ -16,7 +18,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Geospatial Templates\n", + "# Geospatial Templates\n", "\n", "When running a geospatial analysis using `pvdeg.geospatial.analysis` on arbitary `pvdeg` functions you will need to specify a template for the shape of the output data. This is because the input data comes with dimensions of gid and time while the output will have data in a different shape usually corresonding to coordinates.\n", "- gid, identification number corresponding to an NSRDB datapoint's location \n", @@ -31,19 +33,620 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Creating Templates Programattically\n", + "## Loading Geospatial Data\n", + "\n", + "This step skips over making the `pvdeg.weather.get` call with `geospatial == True`. See the [Duramat Demo](./DuraMAT%20Live%20Demo.ipynb) for information on how to do this traditionally.\n", "\n", - "`pvdeg.geospatial.ouput_template` we can produce a template for our result data\n" + "We can also use a `GeospatialScenario` object. See the [Geospatial Scenario Tutorial](./Scenario%20-%20Geospatial.ipynb) for more information on how to use this approach.\n", + "\n", + "*The cell below loads a pickled xarray object, this is not the best way to do this. xarray datasets should be stored at `.nc` - netcdf files*" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
<xarray.Dataset> Size: 9MB\n",
+       "Dimensions:            (time: 17520, gid: 11)\n",
+       "Coordinates:\n",
+       "  * gid                (gid) int64 88B 449211 452064 453020 ... 460613 462498\n",
+       "  * time               (time) datetime64[ns] 140kB 2022-01-01 ... 2022-12-31T...\n",
+       "Data variables:\n",
+       "    temp_air           (time, gid) float64 2MB -12.0 -8.1 -14.2 ... -4.3 -6.2\n",
+       "    wind_speed         (time, gid) float64 2MB 0.6 0.6 0.3 0.6 ... 0.9 1.0 1.1\n",
+       "    dhi                (time, gid) float64 2MB 0.0 0.0 0.0 ... 13.0 18.0 19.0\n",
+       "    ghi                (time, gid) float64 2MB 0.0 0.0 0.0 ... 13.0 24.0 19.0\n",
+       "    dni                (time, gid) float64 2MB 0.0 0.0 0.0 0.0 ... 0.0 126.0 1.0\n",
+       "    relative_humidity  (time, gid) float64 2MB 99.93 79.41 100.0 ... 95.93 100.0\n",
+       "Attributes:\n",
+       "    full_version_record:  {"rex": "0.2.80", "pandas": "2.0.0", "numpy": "1.23...\n",
+       "    package:              rex\n",
+       "    version:              4.0.0
" + ], + "text/plain": [ + " Size: 9MB\n", + "Dimensions: (time: 17520, gid: 11)\n", + "Coordinates:\n", + " * gid (gid) int64 88B 449211 452064 453020 ... 460613 462498\n", + " * time (time) datetime64[ns] 140kB 2022-01-01 ... 2022-12-31T...\n", + "Data variables:\n", + " temp_air (time, gid) float64 2MB -12.0 -8.1 -14.2 ... -4.3 -6.2\n", + " wind_speed (time, gid) float64 2MB 0.6 0.6 0.3 0.6 ... 0.9 1.0 1.1\n", + " dhi (time, gid) float64 2MB 0.0 0.0 0.0 ... 13.0 18.0 19.0\n", + " ghi (time, gid) float64 2MB 0.0 0.0 0.0 ... 13.0 24.0 19.0\n", + " dni (time, gid) float64 2MB 0.0 0.0 0.0 0.0 ... 0.0 126.0 1.0\n", + " relative_humidity (time, gid) float64 2MB 99.93 79.41 100.0 ... 95.93 100.0\n", + "Attributes:\n", + " full_version_record: {\"rex\": \"0.2.80\", \"pandas\": \"2.0.0\", \"numpy\": \"1.23...\n", + " package: rex\n", + " version: 4.0.0" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "geo_meta = pd.read_csv(os.path.join(TEST_DATA_DIR, 'summit-meta.csv'), index_col=0)\n", + "\n", + "with open(os.path.join(TEST_DATA_DIR, 'summit-weather.pkl'), 'rb') as f:\n", + " geo_weather = pickle.load(f)\n", + "\n", + "geo_weather" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Creating Templates Programattically\n", + "\n", + "`pvdeg.geospatial.ouput_template` we can produce a template for our result data. \n", + "\n", + "We need to do this because different functions return different types of values, some return multiple values as tuples, some return only single numerics, others return timeseries results. We need to specify the shape of our data to create an output xarray dataset. " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Examples\n", + "\n", + "### 98ᵗʰ module percential temperature at Standoff Height\n", + "\n", + "Say we want to estimate the 98ᵗʰ percential temperature for the module at the given tilt, azimuth, and x_eff. `PVDeg` has a function to do this, `pvdeg.standards.T98_estimate` BUT it doesn't have a preset geospatial template. We will need to make one.\n", + "\n", + "- look at the function return values. \n", + "From the docstring we can see that `T98_estimate` only has one return value. IMPORTANT, this value is a single float, NOT a timeseries. This means our output shape will only be dependent on the input identifier and NOT time. \n", + "\n", + "Therefore we will map the output variable `T98` to the location identifier `gid` using a dictionary with `str: tuple` mappings.\n", + "\n", + " *IMPORTANT: you must use the syntax below where the variable maps to a tuple of the coordinates. in this case there needs to be a trailing comma in the tuple or python will iterate over the characters in the tuple instead of the elements. See further examples to alleviate confusion.*" + ] + }, + { + "cell_type": "code", + "execution_count": 15, "metadata": {}, "outputs": [], "source": [ - "weather_ds = xr.open_dataset(os.path.join(TEST_DATA_DIR, \"\"))" + "# define output shape\n", + "shapes = {\n", + " \"T98\" : (\"gid\",) # one return value at each datapoint, only dependent on datapoint, not time\n", + "}\n", + "\n", + "# create xarray template for output to be populated when analysis is run\n", + "template = pvdeg.geospatial.output_template(\n", + " ds_gids=geo_weather, \n", + " shapes=shapes,\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The array tilt angle was not provided, therefore the latitude tilt of 39.9 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n" + ] + }, + { + "ename": "TypeError", + "evalue": "cell() got an unexpected keyword argument 'conf_inf'", + "output_type": "error", + "traceback": [ + "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[1;31mTypeError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[1;32mIn[17], line 1\u001b[0m\n\u001b[1;32m----> 1\u001b[0m \u001b[43mpvdeg\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mgeospatial\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43manalysis\u001b[49m\u001b[43m(\u001b[49m\n\u001b[0;32m 2\u001b[0m \u001b[43m \u001b[49m\u001b[43mweather_ds\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mgeo_weather\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 3\u001b[0m \u001b[43m \u001b[49m\u001b[43mmeta_df\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mgeo_meta\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 4\u001b[0m \u001b[43m \u001b[49m\u001b[43mfunc\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mpvdeg\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mstandards\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mT98_estimate\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 5\u001b[0m \n\u001b[0;32m 6\u001b[0m \u001b[43m \u001b[49m\u001b[43mtemplate\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mtemplate\u001b[49m\n\u001b[0;32m 7\u001b[0m \u001b[43m)\u001b[49m\n", + "File \u001b[1;32m~\\dev\\PVDegradationTools\\pvdeg\\geospatial.py:177\u001b[0m, in \u001b[0;36manalysis\u001b[1;34m(weather_ds, meta_df, func, template, **func_kwargs)\u001b[0m\n\u001b[0;32m 172\u001b[0m \u001b[38;5;66;03m# future_meta_df = client.scatter(meta_df)\u001b[39;00m\n\u001b[0;32m 173\u001b[0m kwargs \u001b[38;5;241m=\u001b[39m {\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mfunc\u001b[39m\u001b[38;5;124m\"\u001b[39m: func, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mfuture_meta_df\u001b[39m\u001b[38;5;124m\"\u001b[39m: meta_df, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mfunc_kwargs\u001b[39m\u001b[38;5;124m\"\u001b[39m: func_kwargs}\n\u001b[0;32m 175\u001b[0m stacked \u001b[38;5;241m=\u001b[39m weather_ds\u001b[38;5;241m.\u001b[39mmap_blocks(\n\u001b[0;32m 176\u001b[0m calc_block, kwargs\u001b[38;5;241m=\u001b[39mkwargs, template\u001b[38;5;241m=\u001b[39mtemplate\n\u001b[1;32m--> 177\u001b[0m )\u001b[38;5;241m.\u001b[39mcompute()\n\u001b[0;32m 179\u001b[0m \u001b[38;5;66;03m# lats = stacked.latitude.values.flatten()\u001b[39;00m\n\u001b[0;32m 180\u001b[0m \u001b[38;5;66;03m# lons = stacked.longitude.values.flatten()\u001b[39;00m\n\u001b[0;32m 181\u001b[0m stacked \u001b[38;5;241m=\u001b[39m stacked\u001b[38;5;241m.\u001b[39mdrop([\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mgid\u001b[39m\u001b[38;5;124m\"\u001b[39m])\n", + "File \u001b[1;32mc:\\ProgramData\\anaconda3\\envs\\fem_diff\\lib\\site-packages\\xarray\\core\\dataset.py:8766\u001b[0m, in \u001b[0;36mDataset.map_blocks\u001b[1;34m(self, func, args, kwargs, template)\u001b[0m\n\u001b[0;32m 8664\u001b[0m \u001b[38;5;250m\u001b[39m\u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[0;32m 8665\u001b[0m \u001b[38;5;124;03mApply a function to each block of this Dataset.\u001b[39;00m\n\u001b[0;32m 8666\u001b[0m \n\u001b[1;32m (...)\u001b[0m\n\u001b[0;32m 8762\u001b[0m \u001b[38;5;124;03m a (time) float64 192B dask.array\u001b[39;00m\n\u001b[0;32m 8763\u001b[0m \u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[0;32m 8764\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01mxarray\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mcore\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mparallel\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m map_blocks\n\u001b[1;32m-> 8766\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mmap_blocks\u001b[49m\u001b[43m(\u001b[49m\u001b[43mfunc\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mtemplate\u001b[49m\u001b[43m)\u001b[49m\n", + "File \u001b[1;32mc:\\ProgramData\\anaconda3\\envs\\fem_diff\\lib\\site-packages\\xarray\\core\\parallel.py:409\u001b[0m, in \u001b[0;36mmap_blocks\u001b[1;34m(func, obj, args, kwargs, template)\u001b[0m\n\u001b[0;32m 403\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mTypeError\u001b[39;00m(\n\u001b[0;32m 404\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mCannot pass dask collections in kwargs yet. Please compute or \u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[0;32m 405\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mload values before passing to map_blocks.\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[0;32m 406\u001b[0m )\n\u001b[0;32m 408\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m is_dask_collection(obj):\n\u001b[1;32m--> 409\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m func(obj, \u001b[38;5;241m*\u001b[39margs, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs)\n\u001b[0;32m 411\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m 412\u001b[0m \u001b[38;5;28;01mimport\u001b[39;00m \u001b[38;5;21;01mdask\u001b[39;00m\n", + "File \u001b[1;32m~\\dev\\PVDegradationTools\\pvdeg\\geospatial.py:136\u001b[0m, in \u001b[0;36mcalc_block\u001b[1;34m(weather_ds_block, future_meta_df, func, func_kwargs)\u001b[0m\n\u001b[0;32m 113\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mcalc_block\u001b[39m(weather_ds_block, future_meta_df, func, func_kwargs):\n\u001b[0;32m 114\u001b[0m \u001b[38;5;250m \u001b[39m\u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[0;32m 115\u001b[0m \u001b[38;5;124;03m Calculates a block of gids for a given function.\u001b[39;00m\n\u001b[0;32m 116\u001b[0m \n\u001b[1;32m (...)\u001b[0m\n\u001b[0;32m 131\u001b[0m \u001b[38;5;124;03m Dataset with results for a block of gids.\u001b[39;00m\n\u001b[0;32m 132\u001b[0m \u001b[38;5;124;03m \"\"\"\u001b[39;00m\n\u001b[0;32m 134\u001b[0m res \u001b[38;5;241m=\u001b[39m weather_ds_block\u001b[38;5;241m.\u001b[39mgroupby(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mgid\u001b[39m\u001b[38;5;124m\"\u001b[39m, squeeze\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mFalse\u001b[39;00m)\u001b[38;5;241m.\u001b[39mmap(\n\u001b[0;32m 135\u001b[0m \u001b[38;5;28;01mlambda\u001b[39;00m ds_gid: calc_gid(\n\u001b[1;32m--> 136\u001b[0m ds_gid\u001b[38;5;241m=\u001b[39mds_gid\u001b[38;5;241m.\u001b[39msqueeze(),\n\u001b[0;32m 137\u001b[0m meta_gid\u001b[38;5;241m=\u001b[39mfuture_meta_df\u001b[38;5;241m.\u001b[39mloc[ds_gid[\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mgid\u001b[39m\u001b[38;5;124m\"\u001b[39m]\u001b[38;5;241m.\u001b[39mvalues[\u001b[38;5;241m0\u001b[39m]]\u001b[38;5;241m.\u001b[39mto_dict(),\n\u001b[0;32m 138\u001b[0m func\u001b[38;5;241m=\u001b[39mfunc,\n\u001b[0;32m 139\u001b[0m \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mfunc_kwargs,\n\u001b[0;32m 140\u001b[0m )\n\u001b[0;32m 141\u001b[0m )\n\u001b[0;32m 142\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m res\n", + "File \u001b[1;32mc:\\ProgramData\\anaconda3\\envs\\fem_diff\\lib\\site-packages\\xarray\\core\\groupby.py:1699\u001b[0m, in \u001b[0;36mDatasetGroupByBase.map\u001b[1;34m(self, func, args, shortcut, **kwargs)\u001b[0m\n\u001b[0;32m 1663\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mmap\u001b[39m(\n\u001b[0;32m 1664\u001b[0m \u001b[38;5;28mself\u001b[39m,\n\u001b[0;32m 1665\u001b[0m func: Callable[\u001b[38;5;241m.\u001b[39m\u001b[38;5;241m.\u001b[39m\u001b[38;5;241m.\u001b[39m, Dataset],\n\u001b[1;32m (...)\u001b[0m\n\u001b[0;32m 1668\u001b[0m \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs: Any,\n\u001b[0;32m 1669\u001b[0m ) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m Dataset:\n\u001b[0;32m 1670\u001b[0m \u001b[38;5;250m \u001b[39m\u001b[38;5;124;03m\"\"\"Apply a function to each Dataset in the group and concatenate them\u001b[39;00m\n\u001b[0;32m 1671\u001b[0m \u001b[38;5;124;03m together into a new Dataset.\u001b[39;00m\n\u001b[0;32m 1672\u001b[0m \n\u001b[1;32m (...)\u001b[0m\n\u001b[0;32m 1697\u001b[0m \u001b[38;5;124;03m The result of splitting, applying and combining this dataset.\u001b[39;00m\n\u001b[0;32m 1698\u001b[0m \u001b[38;5;124;03m \"\"\"\u001b[39;00m\n\u001b[1;32m-> 1699\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_map_maybe_warn(func, args, shortcut, warn_squeeze\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mTrue\u001b[39;00m, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs)\n", + "File \u001b[1;32mc:\\ProgramData\\anaconda3\\envs\\fem_diff\\lib\\site-packages\\xarray\\core\\groupby.py:1711\u001b[0m, in \u001b[0;36mDatasetGroupByBase._map_maybe_warn\u001b[1;34m(self, func, args, shortcut, warn_squeeze, **kwargs)\u001b[0m\n\u001b[0;32m 1701\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21m_map_maybe_warn\u001b[39m(\n\u001b[0;32m 1702\u001b[0m \u001b[38;5;28mself\u001b[39m,\n\u001b[0;32m 1703\u001b[0m func: Callable[\u001b[38;5;241m.\u001b[39m\u001b[38;5;241m.\u001b[39m\u001b[38;5;241m.\u001b[39m, Dataset],\n\u001b[1;32m (...)\u001b[0m\n\u001b[0;32m 1708\u001b[0m ) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m Dataset:\n\u001b[0;32m 1709\u001b[0m \u001b[38;5;66;03m# ignore shortcut if set (for now)\u001b[39;00m\n\u001b[0;32m 1710\u001b[0m applied \u001b[38;5;241m=\u001b[39m (func(ds, \u001b[38;5;241m*\u001b[39margs, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs) \u001b[38;5;28;01mfor\u001b[39;00m ds \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_iter_grouped(warn_squeeze))\n\u001b[1;32m-> 1711\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_combine\u001b[49m\u001b[43m(\u001b[49m\u001b[43mapplied\u001b[49m\u001b[43m)\u001b[49m\n", + "File \u001b[1;32mc:\\ProgramData\\anaconda3\\envs\\fem_diff\\lib\\site-packages\\xarray\\core\\groupby.py:1731\u001b[0m, in \u001b[0;36mDatasetGroupByBase._combine\u001b[1;34m(self, applied)\u001b[0m\n\u001b[0;32m 1729\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21m_combine\u001b[39m(\u001b[38;5;28mself\u001b[39m, applied):\n\u001b[0;32m 1730\u001b[0m \u001b[38;5;250m \u001b[39m\u001b[38;5;124;03m\"\"\"Recombine the applied objects like the original.\"\"\"\u001b[39;00m\n\u001b[1;32m-> 1731\u001b[0m applied_example, applied \u001b[38;5;241m=\u001b[39m \u001b[43mpeek_at\u001b[49m\u001b[43m(\u001b[49m\u001b[43mapplied\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 1732\u001b[0m coord, dim, positions \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_infer_concat_args(applied_example)\n\u001b[0;32m 1733\u001b[0m combined \u001b[38;5;241m=\u001b[39m concat(applied, dim)\n", + "File \u001b[1;32mc:\\ProgramData\\anaconda3\\envs\\fem_diff\\lib\\site-packages\\xarray\\core\\utils.py:205\u001b[0m, in \u001b[0;36mpeek_at\u001b[1;34m(iterable)\u001b[0m\n\u001b[0;32m 201\u001b[0m \u001b[38;5;250m\u001b[39m\u001b[38;5;124;03m\"\"\"Returns the first value from iterable, as well as a new iterator with\u001b[39;00m\n\u001b[0;32m 202\u001b[0m \u001b[38;5;124;03mthe same content as the original iterable\u001b[39;00m\n\u001b[0;32m 203\u001b[0m \u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[0;32m 204\u001b[0m gen \u001b[38;5;241m=\u001b[39m \u001b[38;5;28miter\u001b[39m(iterable)\n\u001b[1;32m--> 205\u001b[0m peek \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mnext\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mgen\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 206\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m peek, itertools\u001b[38;5;241m.\u001b[39mchain([peek], gen)\n", + "File \u001b[1;32mc:\\ProgramData\\anaconda3\\envs\\fem_diff\\lib\\site-packages\\xarray\\core\\groupby.py:1710\u001b[0m, in \u001b[0;36m\u001b[1;34m(.0)\u001b[0m\n\u001b[0;32m 1701\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21m_map_maybe_warn\u001b[39m(\n\u001b[0;32m 1702\u001b[0m \u001b[38;5;28mself\u001b[39m,\n\u001b[0;32m 1703\u001b[0m func: Callable[\u001b[38;5;241m.\u001b[39m\u001b[38;5;241m.\u001b[39m\u001b[38;5;241m.\u001b[39m, Dataset],\n\u001b[1;32m (...)\u001b[0m\n\u001b[0;32m 1708\u001b[0m ) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m Dataset:\n\u001b[0;32m 1709\u001b[0m \u001b[38;5;66;03m# ignore shortcut if set (for now)\u001b[39;00m\n\u001b[1;32m-> 1710\u001b[0m applied \u001b[38;5;241m=\u001b[39m (func(ds, \u001b[38;5;241m*\u001b[39margs, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs) \u001b[38;5;28;01mfor\u001b[39;00m ds \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_iter_grouped(warn_squeeze))\n\u001b[0;32m 1711\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_combine(applied)\n", + "File \u001b[1;32m~\\dev\\PVDegradationTools\\pvdeg\\geospatial.py:137\u001b[0m, in \u001b[0;36m\u001b[1;34m(ds_gid)\u001b[0m\n\u001b[0;32m 113\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mcalc_block\u001b[39m(weather_ds_block, future_meta_df, func, func_kwargs):\n\u001b[0;32m 114\u001b[0m \u001b[38;5;250m \u001b[39m\u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[0;32m 115\u001b[0m \u001b[38;5;124;03m Calculates a block of gids for a given function.\u001b[39;00m\n\u001b[0;32m 116\u001b[0m \n\u001b[1;32m (...)\u001b[0m\n\u001b[0;32m 131\u001b[0m \u001b[38;5;124;03m Dataset with results for a block of gids.\u001b[39;00m\n\u001b[0;32m 132\u001b[0m \u001b[38;5;124;03m \"\"\"\u001b[39;00m\n\u001b[0;32m 134\u001b[0m res \u001b[38;5;241m=\u001b[39m weather_ds_block\u001b[38;5;241m.\u001b[39mgroupby(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mgid\u001b[39m\u001b[38;5;124m\"\u001b[39m, squeeze\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mFalse\u001b[39;00m)\u001b[38;5;241m.\u001b[39mmap(\n\u001b[0;32m 135\u001b[0m \u001b[38;5;28;01mlambda\u001b[39;00m ds_gid: calc_gid(\n\u001b[0;32m 136\u001b[0m ds_gid\u001b[38;5;241m=\u001b[39mds_gid\u001b[38;5;241m.\u001b[39msqueeze(),\n\u001b[1;32m--> 137\u001b[0m meta_gid\u001b[38;5;241m=\u001b[39mfuture_meta_df\u001b[38;5;241m.\u001b[39mloc[ds_gid[\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mgid\u001b[39m\u001b[38;5;124m\"\u001b[39m]\u001b[38;5;241m.\u001b[39mvalues[\u001b[38;5;241m0\u001b[39m]]\u001b[38;5;241m.\u001b[39mto_dict(),\n\u001b[0;32m 138\u001b[0m func\u001b[38;5;241m=\u001b[39mfunc,\n\u001b[0;32m 139\u001b[0m \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mfunc_kwargs,\n\u001b[0;32m 140\u001b[0m )\n\u001b[0;32m 141\u001b[0m )\n\u001b[0;32m 142\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m res\n", + "File \u001b[1;32m~\\dev\\PVDegradationTools\\pvdeg\\geospatial.py:106\u001b[0m, in \u001b[0;36mcalc_gid\u001b[1;34m(ds_gid, meta_gid, func, **kwargs)\u001b[0m\n\u001b[0;32m 0\u001b[0m \n", + "File \u001b[1;32m~\\dev\\PVDegradationTools\\pvdeg\\standards.py:502\u001b[0m, in \u001b[0;36mT98_estimate\u001b[1;34m(weather_df, meta, weather_kwarg, sky_model, temp_model, conf_0, conf_inf, wind_factor, tilt, azimuth, x_eff, x_0)\u001b[0m\n\u001b[0;32m 492\u001b[0m solar_position \u001b[38;5;241m=\u001b[39m spectral\u001b[38;5;241m.\u001b[39msolar_position(weather_df, meta)\n\u001b[0;32m 493\u001b[0m poa \u001b[38;5;241m=\u001b[39m spectral\u001b[38;5;241m.\u001b[39mpoa_irradiance(\n\u001b[0;32m 494\u001b[0m weather_df\u001b[38;5;241m=\u001b[39mweather_df,\n\u001b[0;32m 495\u001b[0m meta\u001b[38;5;241m=\u001b[39mmeta,\n\u001b[1;32m (...)\u001b[0m\n\u001b[0;32m 499\u001b[0m sky_model\u001b[38;5;241m=\u001b[39msky_model,\n\u001b[0;32m 500\u001b[0m )\n\u001b[1;32m--> 502\u001b[0m T_inf \u001b[38;5;241m=\u001b[39m \u001b[43mtemperature\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mcell\u001b[49m\u001b[43m(\u001b[49m\n\u001b[0;32m 503\u001b[0m \u001b[43m \u001b[49m\u001b[43mweather_df\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mweather_df\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 504\u001b[0m \u001b[43m \u001b[49m\u001b[43mmeta\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mmeta\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 505\u001b[0m \u001b[43m \u001b[49m\u001b[43mpoa\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mpoa\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 506\u001b[0m \u001b[43m \u001b[49m\u001b[43mtemp_model\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mtemp_model\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 507\u001b[0m \u001b[43m \u001b[49m\u001b[43mconf_inf\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mconf_inf\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 508\u001b[0m \u001b[43m \u001b[49m\u001b[43mwind_factor\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mwind_factor\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 509\u001b[0m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 510\u001b[0m T98_inf \u001b[38;5;241m=\u001b[39m T_inf\u001b[38;5;241m.\u001b[39mquantile(q\u001b[38;5;241m=\u001b[39m\u001b[38;5;241m0.98\u001b[39m, interpolation\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mlinear\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[0;32m 512\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m x_eff \u001b[38;5;241m==\u001b[39m \u001b[38;5;28;01mNone\u001b[39;00m:\n", + "\u001b[1;31mTypeError\u001b[0m: cell() got an unexpected keyword argument 'conf_inf'" + ] + } + ], + "source": [ + "pvdeg.geospatial.analysis(\n", + " weather_ds=geo_weather,\n", + " meta_df=geo_meta,\n", + " func=pvdeg.standards.T98_estimate,\n", + "\n", + " template=template\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The array tilt angle was not provided, therefore the latitude tilt of 39.9 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n" + ] + }, + { + "data": { + "text/plain": [ + "50.695646304025196" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "pvdeg.standards.T98_estimate(\n", + " weather_df=geo_weather.isel(gid=0).to_dataframe(),\n", + " meta=geo_meta.iloc[0].to_dict()\n", + ")" ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { From 70ef3d2f3983cc0d1fffdfb5c089d8d9d494dc9e Mon Sep 17 00:00:00 2001 From: tobin-ford Date: Fri, 26 Jul 2024 16:46:34 -0600 Subject: [PATCH 3/9] t98_estimate breaking bug fix --- pvdeg/standards.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pvdeg/standards.py b/pvdeg/standards.py index 895896b..0a1f0d2 100644 --- a/pvdeg/standards.py +++ b/pvdeg/standards.py @@ -504,7 +504,7 @@ def T98_estimate( meta=meta, poa=poa, temp_model=temp_model, - conf_inf=conf_inf, + conf=conf_inf, wind_factor=wind_factor, ) T98_inf = T_inf.quantile(q=0.98, interpolation="linear") @@ -517,7 +517,7 @@ def T98_estimate( meta=meta, poa=poa, temp_model=temp_model, - conf_0=conf_0, + conf=conf_0, wind_factor=wind_factor, ) T98_0 = T_0.quantile(q=0.98, interpolation="linear") From ce3031360947539447592d1532a2fd304af736b0 Mon Sep 17 00:00:00 2001 From: tobin-ford Date: Fri, 26 Jul 2024 16:53:58 -0600 Subject: [PATCH 4/9] run docs --- .../source/_autosummary/pvdeg.degradation.rst | 8 ++ .../pvdeg.degradation.vecArrhenius.rst | 6 ++ docs/source/_autosummary/pvdeg.humidity.rst | 8 -- ...g.montecarlo.generateCorrelatedSamples.rst | 6 ++ docs/source/_autosummary/pvdeg.montecarlo.rst | 74 +++++++++++++++++++ .../pvdeg.montecarlo.simulate.rst | 6 ++ .../pvdeg.standards.T98_estimate.rst | 6 ++ .../pvdeg.standards.eff_gap_parameters.rst | 6 ++ .../pvdeg.standards.interpret_standoff.rst | 6 ++ docs/source/_autosummary/pvdeg.standards.rst | 40 ++++++++-- .../_autosummary/pvdeg.standards.standoff.rst | 6 ++ .../pvdeg.standards.standoff_x.rst | 6 ++ .../pvdeg.utilities.meta_as_dict.rst | 6 ++ docs/source/_autosummary/pvdeg.utilities.rst | 16 ++++ .../pvdeg.utilities.tilt_azimuth_scan.rst | 6 ++ .../_autosummary/pvdeg.weather.csv_read.rst | 6 ++ .../pvdeg.weather.get_satellite.rst | 6 ++ .../pvdeg.weather.ini_h5_geospatial.rst | 6 ++ .../pvdeg.weather.is_leap_year.rst | 6 ++ .../_autosummary/pvdeg.weather.map_meta.rst | 6 ++ .../pvdeg.weather.map_weather.rst | 6 ++ ...vdeg.weather.repeat_annual_time_series.rst | 6 ++ docs/source/_autosummary/pvdeg.weather.rst | 64 ++++++++++++++++ .../_autosummary/pvdeg.weather.write.rst | 6 ++ docs/source/whatsnew/index.rst | 1 + docs/source/whatsnew/releases/v0.3.3.rst | 2 +- docs/source/whatsnew/releases/v0.3.4.rst | 11 +++ 27 files changed, 315 insertions(+), 17 deletions(-) create mode 100644 docs/source/_autosummary/pvdeg.degradation.vecArrhenius.rst create mode 100644 docs/source/_autosummary/pvdeg.montecarlo.generateCorrelatedSamples.rst create mode 100644 docs/source/_autosummary/pvdeg.montecarlo.rst create mode 100644 docs/source/_autosummary/pvdeg.montecarlo.simulate.rst create mode 100644 docs/source/_autosummary/pvdeg.standards.T98_estimate.rst create mode 100644 docs/source/_autosummary/pvdeg.standards.eff_gap_parameters.rst create mode 100644 docs/source/_autosummary/pvdeg.standards.interpret_standoff.rst create mode 100644 docs/source/_autosummary/pvdeg.standards.standoff.rst create mode 100644 docs/source/_autosummary/pvdeg.standards.standoff_x.rst create mode 100644 docs/source/_autosummary/pvdeg.utilities.meta_as_dict.rst create mode 100644 docs/source/_autosummary/pvdeg.utilities.tilt_azimuth_scan.rst create mode 100644 docs/source/_autosummary/pvdeg.weather.csv_read.rst create mode 100644 docs/source/_autosummary/pvdeg.weather.get_satellite.rst create mode 100644 docs/source/_autosummary/pvdeg.weather.ini_h5_geospatial.rst create mode 100644 docs/source/_autosummary/pvdeg.weather.is_leap_year.rst create mode 100644 docs/source/_autosummary/pvdeg.weather.map_meta.rst create mode 100644 docs/source/_autosummary/pvdeg.weather.map_weather.rst create mode 100644 docs/source/_autosummary/pvdeg.weather.repeat_annual_time_series.rst create mode 100644 docs/source/_autosummary/pvdeg.weather.write.rst create mode 100644 docs/source/whatsnew/releases/v0.3.4.rst diff --git a/docs/source/_autosummary/pvdeg.degradation.rst b/docs/source/_autosummary/pvdeg.degradation.rst index 52645e7..9e2e6ca 100644 --- a/docs/source/_autosummary/pvdeg.degradation.rst +++ b/docs/source/_autosummary/pvdeg.degradation.rst @@ -24,6 +24,7 @@ pvdeg.degradation pvdeg.degradation.arrhenius_deg pvdeg.degradation.degradation pvdeg.degradation.vantHoff_deg + pvdeg.degradation.vecArrhenius @@ -73,6 +74,13 @@ pvdeg.degradation .. minigallery:: pvdeg.degradation.vantHoff_deg :add-heading: + + .. autofunction:: vecArrhenius + + .. _sphx_glr_backref_pvdeg.degradation.vecArrhenius: + + .. minigallery:: pvdeg.degradation.vecArrhenius + :add-heading: diff --git a/docs/source/_autosummary/pvdeg.degradation.vecArrhenius.rst b/docs/source/_autosummary/pvdeg.degradation.vecArrhenius.rst new file mode 100644 index 0000000..774aae7 --- /dev/null +++ b/docs/source/_autosummary/pvdeg.degradation.vecArrhenius.rst @@ -0,0 +1,6 @@ +pvdeg.degradation.vecArrhenius +============================== + +.. currentmodule:: pvdeg.degradation + +.. autofunction:: vecArrhenius \ No newline at end of file diff --git a/docs/source/_autosummary/pvdeg.humidity.rst b/docs/source/_autosummary/pvdeg.humidity.rst index 5f4df11..afa61db 100644 --- a/docs/source/_autosummary/pvdeg.humidity.rst +++ b/docs/source/_autosummary/pvdeg.humidity.rst @@ -27,7 +27,6 @@ pvdeg.humidity pvdeg.humidity.front_encap pvdeg.humidity.module pvdeg.humidity.psat - pvdeg.humidity.run_module pvdeg.humidity.surface_outside @@ -100,13 +99,6 @@ pvdeg.humidity .. minigallery:: pvdeg.humidity.psat :add-heading: - .. autofunction:: run_module - - .. _sphx_glr_backref_pvdeg.humidity.run_module: - - .. minigallery:: pvdeg.humidity.run_module - :add-heading: - .. autofunction:: surface_outside .. _sphx_glr_backref_pvdeg.humidity.surface_outside: diff --git a/docs/source/_autosummary/pvdeg.montecarlo.generateCorrelatedSamples.rst b/docs/source/_autosummary/pvdeg.montecarlo.generateCorrelatedSamples.rst new file mode 100644 index 0000000..b9974f3 --- /dev/null +++ b/docs/source/_autosummary/pvdeg.montecarlo.generateCorrelatedSamples.rst @@ -0,0 +1,6 @@ +pvdeg.montecarlo.generateCorrelatedSamples +========================================== + +.. currentmodule:: pvdeg.montecarlo + +.. autofunction:: generateCorrelatedSamples \ No newline at end of file diff --git a/docs/source/_autosummary/pvdeg.montecarlo.rst b/docs/source/_autosummary/pvdeg.montecarlo.rst new file mode 100644 index 0000000..02f7641 --- /dev/null +++ b/docs/source/_autosummary/pvdeg.montecarlo.rst @@ -0,0 +1,74 @@ +.. Please when editing this file make sure to keep it matching the + docs in ../configuration.rst:reference_to_examples + +pvdeg.montecarlo +================ + +.. automodule:: pvdeg.montecarlo + + .. this is crazy + + + + + Function Overview + ----------------- + + .. autosummary:: + :toctree: + :nosignatures: + + + pvdeg.montecarlo.generateCorrelatedSamples + pvdeg.montecarlo.simulate + + + + + .. this is crazy + + + + +.. + Functions + --------- + + + + .. autofunction:: generateCorrelatedSamples + + .. _sphx_glr_backref_pvdeg.montecarlo.generateCorrelatedSamples: + + .. minigallery:: pvdeg.montecarlo.generateCorrelatedSamples + :add-heading: + + .. autofunction:: simulate + + .. _sphx_glr_backref_pvdeg.montecarlo.simulate: + + .. minigallery:: pvdeg.montecarlo.simulate + :add-heading: + + + + + + + Classes + ------- + + + .. autoclass:: Corr + :members: + + .. _sphx_glr_backref_pvdeg.montecarlo.Corr: + + .. minigallery:: pvdeg.montecarlo.Corr + :add-heading: + + + + + + \ No newline at end of file diff --git a/docs/source/_autosummary/pvdeg.montecarlo.simulate.rst b/docs/source/_autosummary/pvdeg.montecarlo.simulate.rst new file mode 100644 index 0000000..c0c00be --- /dev/null +++ b/docs/source/_autosummary/pvdeg.montecarlo.simulate.rst @@ -0,0 +1,6 @@ +pvdeg.montecarlo.simulate +========================= + +.. currentmodule:: pvdeg.montecarlo + +.. autofunction:: simulate \ No newline at end of file diff --git a/docs/source/_autosummary/pvdeg.standards.T98_estimate.rst b/docs/source/_autosummary/pvdeg.standards.T98_estimate.rst new file mode 100644 index 0000000..da5ad84 --- /dev/null +++ b/docs/source/_autosummary/pvdeg.standards.T98_estimate.rst @@ -0,0 +1,6 @@ +pvdeg.standards.T98\_estimate +============================= + +.. currentmodule:: pvdeg.standards + +.. autofunction:: T98_estimate \ No newline at end of file diff --git a/docs/source/_autosummary/pvdeg.standards.eff_gap_parameters.rst b/docs/source/_autosummary/pvdeg.standards.eff_gap_parameters.rst new file mode 100644 index 0000000..223d771 --- /dev/null +++ b/docs/source/_autosummary/pvdeg.standards.eff_gap_parameters.rst @@ -0,0 +1,6 @@ +pvdeg.standards.eff\_gap\_parameters +==================================== + +.. currentmodule:: pvdeg.standards + +.. autofunction:: eff_gap_parameters \ No newline at end of file diff --git a/docs/source/_autosummary/pvdeg.standards.interpret_standoff.rst b/docs/source/_autosummary/pvdeg.standards.interpret_standoff.rst new file mode 100644 index 0000000..3843af5 --- /dev/null +++ b/docs/source/_autosummary/pvdeg.standards.interpret_standoff.rst @@ -0,0 +1,6 @@ +pvdeg.standards.interpret\_standoff +=================================== + +.. currentmodule:: pvdeg.standards + +.. autofunction:: interpret_standoff \ No newline at end of file diff --git a/docs/source/_autosummary/pvdeg.standards.rst b/docs/source/_autosummary/pvdeg.standards.rst index cc59797..0aa8920 100644 --- a/docs/source/_autosummary/pvdeg.standards.rst +++ b/docs/source/_autosummary/pvdeg.standards.rst @@ -19,9 +19,12 @@ pvdeg.standards :nosignatures: - pvdeg.standards.calc_standoff + pvdeg.standards.T98_estimate pvdeg.standards.eff_gap - pvdeg.standards.run_calc_standoff + pvdeg.standards.eff_gap_parameters + pvdeg.standards.interpret_standoff + pvdeg.standards.standoff + pvdeg.standards.standoff_x @@ -37,11 +40,11 @@ pvdeg.standards - .. autofunction:: calc_standoff + .. autofunction:: T98_estimate - .. _sphx_glr_backref_pvdeg.standards.calc_standoff: + .. _sphx_glr_backref_pvdeg.standards.T98_estimate: - .. minigallery:: pvdeg.standards.calc_standoff + .. minigallery:: pvdeg.standards.T98_estimate :add-heading: .. autofunction:: eff_gap @@ -51,11 +54,32 @@ pvdeg.standards .. minigallery:: pvdeg.standards.eff_gap :add-heading: - .. autofunction:: run_calc_standoff + .. autofunction:: eff_gap_parameters - .. _sphx_glr_backref_pvdeg.standards.run_calc_standoff: + .. _sphx_glr_backref_pvdeg.standards.eff_gap_parameters: - .. minigallery:: pvdeg.standards.run_calc_standoff + .. minigallery:: pvdeg.standards.eff_gap_parameters + :add-heading: + + .. autofunction:: interpret_standoff + + .. _sphx_glr_backref_pvdeg.standards.interpret_standoff: + + .. minigallery:: pvdeg.standards.interpret_standoff + :add-heading: + + .. autofunction:: standoff + + .. _sphx_glr_backref_pvdeg.standards.standoff: + + .. minigallery:: pvdeg.standards.standoff + :add-heading: + + .. autofunction:: standoff_x + + .. _sphx_glr_backref_pvdeg.standards.standoff_x: + + .. minigallery:: pvdeg.standards.standoff_x :add-heading: diff --git a/docs/source/_autosummary/pvdeg.standards.standoff.rst b/docs/source/_autosummary/pvdeg.standards.standoff.rst new file mode 100644 index 0000000..8ebc1ea --- /dev/null +++ b/docs/source/_autosummary/pvdeg.standards.standoff.rst @@ -0,0 +1,6 @@ +pvdeg.standards.standoff +======================== + +.. currentmodule:: pvdeg.standards + +.. autofunction:: standoff \ No newline at end of file diff --git a/docs/source/_autosummary/pvdeg.standards.standoff_x.rst b/docs/source/_autosummary/pvdeg.standards.standoff_x.rst new file mode 100644 index 0000000..2887f41 --- /dev/null +++ b/docs/source/_autosummary/pvdeg.standards.standoff_x.rst @@ -0,0 +1,6 @@ +pvdeg.standards.standoff\_x +=========================== + +.. currentmodule:: pvdeg.standards + +.. autofunction:: standoff_x \ No newline at end of file diff --git a/docs/source/_autosummary/pvdeg.utilities.meta_as_dict.rst b/docs/source/_autosummary/pvdeg.utilities.meta_as_dict.rst new file mode 100644 index 0000000..220be6e --- /dev/null +++ b/docs/source/_autosummary/pvdeg.utilities.meta_as_dict.rst @@ -0,0 +1,6 @@ +pvdeg.utilities.meta\_as\_dict +============================== + +.. currentmodule:: pvdeg.utilities + +.. autofunction:: meta_as_dict \ No newline at end of file diff --git a/docs/source/_autosummary/pvdeg.utilities.rst b/docs/source/_autosummary/pvdeg.utilities.rst index 16bd6fd..8a4e3ae 100644 --- a/docs/source/_autosummary/pvdeg.utilities.rst +++ b/docs/source/_autosummary/pvdeg.utilities.rst @@ -22,7 +22,9 @@ pvdeg.utilities pvdeg.utilities.convert_tmy pvdeg.utilities.get_kinetics pvdeg.utilities.gid_downsampling + pvdeg.utilities.meta_as_dict pvdeg.utilities.quantile_df + pvdeg.utilities.tilt_azimuth_scan pvdeg.utilities.ts_gid_df pvdeg.utilities.write_gids @@ -61,6 +63,13 @@ pvdeg.utilities .. minigallery:: pvdeg.utilities.gid_downsampling :add-heading: + .. autofunction:: meta_as_dict + + .. _sphx_glr_backref_pvdeg.utilities.meta_as_dict: + + .. minigallery:: pvdeg.utilities.meta_as_dict + :add-heading: + .. autofunction:: quantile_df .. _sphx_glr_backref_pvdeg.utilities.quantile_df: @@ -68,6 +77,13 @@ pvdeg.utilities .. minigallery:: pvdeg.utilities.quantile_df :add-heading: + .. autofunction:: tilt_azimuth_scan + + .. _sphx_glr_backref_pvdeg.utilities.tilt_azimuth_scan: + + .. minigallery:: pvdeg.utilities.tilt_azimuth_scan + :add-heading: + .. autofunction:: ts_gid_df .. _sphx_glr_backref_pvdeg.utilities.ts_gid_df: diff --git a/docs/source/_autosummary/pvdeg.utilities.tilt_azimuth_scan.rst b/docs/source/_autosummary/pvdeg.utilities.tilt_azimuth_scan.rst new file mode 100644 index 0000000..5ffcf7f --- /dev/null +++ b/docs/source/_autosummary/pvdeg.utilities.tilt_azimuth_scan.rst @@ -0,0 +1,6 @@ +pvdeg.utilities.tilt\_azimuth\_scan +=================================== + +.. currentmodule:: pvdeg.utilities + +.. autofunction:: tilt_azimuth_scan \ No newline at end of file diff --git a/docs/source/_autosummary/pvdeg.weather.csv_read.rst b/docs/source/_autosummary/pvdeg.weather.csv_read.rst new file mode 100644 index 0000000..d0c3e75 --- /dev/null +++ b/docs/source/_autosummary/pvdeg.weather.csv_read.rst @@ -0,0 +1,6 @@ +pvdeg.weather.csv\_read +======================= + +.. currentmodule:: pvdeg.weather + +.. autofunction:: csv_read \ No newline at end of file diff --git a/docs/source/_autosummary/pvdeg.weather.get_satellite.rst b/docs/source/_autosummary/pvdeg.weather.get_satellite.rst new file mode 100644 index 0000000..ba43539 --- /dev/null +++ b/docs/source/_autosummary/pvdeg.weather.get_satellite.rst @@ -0,0 +1,6 @@ +pvdeg.weather.get\_satellite +============================ + +.. currentmodule:: pvdeg.weather + +.. autofunction:: get_satellite \ No newline at end of file diff --git a/docs/source/_autosummary/pvdeg.weather.ini_h5_geospatial.rst b/docs/source/_autosummary/pvdeg.weather.ini_h5_geospatial.rst new file mode 100644 index 0000000..efa847c --- /dev/null +++ b/docs/source/_autosummary/pvdeg.weather.ini_h5_geospatial.rst @@ -0,0 +1,6 @@ +pvdeg.weather.ini\_h5\_geospatial +================================= + +.. currentmodule:: pvdeg.weather + +.. autofunction:: ini_h5_geospatial \ No newline at end of file diff --git a/docs/source/_autosummary/pvdeg.weather.is_leap_year.rst b/docs/source/_autosummary/pvdeg.weather.is_leap_year.rst new file mode 100644 index 0000000..bd16d55 --- /dev/null +++ b/docs/source/_autosummary/pvdeg.weather.is_leap_year.rst @@ -0,0 +1,6 @@ +pvdeg.weather.is\_leap\_year +============================ + +.. currentmodule:: pvdeg.weather + +.. autofunction:: is_leap_year \ No newline at end of file diff --git a/docs/source/_autosummary/pvdeg.weather.map_meta.rst b/docs/source/_autosummary/pvdeg.weather.map_meta.rst new file mode 100644 index 0000000..d234889 --- /dev/null +++ b/docs/source/_autosummary/pvdeg.weather.map_meta.rst @@ -0,0 +1,6 @@ +pvdeg.weather.map\_meta +======================= + +.. currentmodule:: pvdeg.weather + +.. autofunction:: map_meta \ No newline at end of file diff --git a/docs/source/_autosummary/pvdeg.weather.map_weather.rst b/docs/source/_autosummary/pvdeg.weather.map_weather.rst new file mode 100644 index 0000000..c0933f4 --- /dev/null +++ b/docs/source/_autosummary/pvdeg.weather.map_weather.rst @@ -0,0 +1,6 @@ +pvdeg.weather.map\_weather +========================== + +.. currentmodule:: pvdeg.weather + +.. autofunction:: map_weather \ No newline at end of file diff --git a/docs/source/_autosummary/pvdeg.weather.repeat_annual_time_series.rst b/docs/source/_autosummary/pvdeg.weather.repeat_annual_time_series.rst new file mode 100644 index 0000000..eed2f44 --- /dev/null +++ b/docs/source/_autosummary/pvdeg.weather.repeat_annual_time_series.rst @@ -0,0 +1,6 @@ +pvdeg.weather.repeat\_annual\_time\_series +========================================== + +.. currentmodule:: pvdeg.weather + +.. autofunction:: repeat_annual_time_series \ No newline at end of file diff --git a/docs/source/_autosummary/pvdeg.weather.rst b/docs/source/_autosummary/pvdeg.weather.rst index a97538a..72a3566 100644 --- a/docs/source/_autosummary/pvdeg.weather.rst +++ b/docs/source/_autosummary/pvdeg.weather.rst @@ -19,11 +19,19 @@ pvdeg.weather :nosignatures: + pvdeg.weather.csv_read pvdeg.weather.get pvdeg.weather.get_NSRDB pvdeg.weather.get_NSRDB_fnames + pvdeg.weather.get_satellite + pvdeg.weather.ini_h5_geospatial + pvdeg.weather.is_leap_year + pvdeg.weather.map_meta + pvdeg.weather.map_weather pvdeg.weather.read pvdeg.weather.read_h5 + pvdeg.weather.repeat_annual_time_series + pvdeg.weather.write @@ -39,6 +47,13 @@ pvdeg.weather + .. autofunction:: csv_read + + .. _sphx_glr_backref_pvdeg.weather.csv_read: + + .. minigallery:: pvdeg.weather.csv_read + :add-heading: + .. autofunction:: get .. _sphx_glr_backref_pvdeg.weather.get: @@ -60,6 +75,41 @@ pvdeg.weather .. minigallery:: pvdeg.weather.get_NSRDB_fnames :add-heading: + .. autofunction:: get_satellite + + .. _sphx_glr_backref_pvdeg.weather.get_satellite: + + .. minigallery:: pvdeg.weather.get_satellite + :add-heading: + + .. autofunction:: ini_h5_geospatial + + .. _sphx_glr_backref_pvdeg.weather.ini_h5_geospatial: + + .. minigallery:: pvdeg.weather.ini_h5_geospatial + :add-heading: + + .. autofunction:: is_leap_year + + .. _sphx_glr_backref_pvdeg.weather.is_leap_year: + + .. minigallery:: pvdeg.weather.is_leap_year + :add-heading: + + .. autofunction:: map_meta + + .. _sphx_glr_backref_pvdeg.weather.map_meta: + + .. minigallery:: pvdeg.weather.map_meta + :add-heading: + + .. autofunction:: map_weather + + .. _sphx_glr_backref_pvdeg.weather.map_weather: + + .. minigallery:: pvdeg.weather.map_weather + :add-heading: + .. autofunction:: read .. _sphx_glr_backref_pvdeg.weather.read: @@ -73,6 +123,20 @@ pvdeg.weather .. minigallery:: pvdeg.weather.read_h5 :add-heading: + + .. autofunction:: repeat_annual_time_series + + .. _sphx_glr_backref_pvdeg.weather.repeat_annual_time_series: + + .. minigallery:: pvdeg.weather.repeat_annual_time_series + :add-heading: + + .. autofunction:: write + + .. _sphx_glr_backref_pvdeg.weather.write: + + .. minigallery:: pvdeg.weather.write + :add-heading: diff --git a/docs/source/_autosummary/pvdeg.weather.write.rst b/docs/source/_autosummary/pvdeg.weather.write.rst new file mode 100644 index 0000000..2a15be8 --- /dev/null +++ b/docs/source/_autosummary/pvdeg.weather.write.rst @@ -0,0 +1,6 @@ +pvdeg.weather.write +=================== + +.. currentmodule:: pvdeg.weather + +.. autofunction:: write \ No newline at end of file diff --git a/docs/source/whatsnew/index.rst b/docs/source/whatsnew/index.rst index 70fac61..4a01688 100644 --- a/docs/source/whatsnew/index.rst +++ b/docs/source/whatsnew/index.rst @@ -4,6 +4,7 @@ What's New ========== PVDegradationTools (pvdeg) change log: +.. include:: releases/v0.3.4.rst .. include:: releases/v0.3.3.rst .. include:: releases/v0.3.2.rst .. include:: releases/v0.3.1.rst diff --git a/docs/source/whatsnew/releases/v0.3.3.rst b/docs/source/whatsnew/releases/v0.3.3.rst index 1b6bd77..e50391d 100644 --- a/docs/source/whatsnew/releases/v0.3.3.rst +++ b/docs/source/whatsnew/releases/v0.3.3.rst @@ -1,4 +1,4 @@ -v0.3.1 (2024-05-06) +v0.3.3 (2024-05-06) ======================= Bug Fixes diff --git a/docs/source/whatsnew/releases/v0.3.4.rst b/docs/source/whatsnew/releases/v0.3.4.rst new file mode 100644 index 0000000..664dd32 --- /dev/null +++ b/docs/source/whatsnew/releases/v0.3.4.rst @@ -0,0 +1,11 @@ +v0.3.4 (2024-07-26) +======================= + +Bug Fixes +--------- +* Fix incorrect keyword arguments in `pvdeg.standards.T98_estimate` + +Contributors +~~~~~~~~~~~~ +* Tobin Ford (:ghuser:`tobin-ford`) + From 582d422d342ec9b2c43494373142dd4ac7d1715d Mon Sep 17 00:00:00 2001 From: tobin-ford Date: Fri, 26 Jul 2024 17:27:33 -0600 Subject: [PATCH 5/9] func with single numeric return, geospatial --- pvdeg/geospatial.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pvdeg/geospatial.py b/pvdeg/geospatial.py index 208723a..7a5dac6 100644 --- a/pvdeg/geospatial.py +++ b/pvdeg/geospatial.py @@ -101,7 +101,8 @@ def calc_gid(ds_gid, meta_gid, func, **kwargs): """ df_weather = ds_gid.to_dataframe() - df_res = func(weather_df=df_weather, meta=meta_gid, **kwargs) + res = func(weather_df=df_weather, meta=meta_gid, **kwargs) # this will sometimes be a dataframe already, if not -> make it a dataframe + df_res = res if isinstance(res, pd.DataFrame) else pd.DataFrame([res], columns=[func.__name__]) # convert numeric to dataframe ds_res = xr.Dataset.from_dataframe(df_res) if not df_res.index.name: From af726d60d953ae7f5cc8655ec3d8ceed40bbaeb1 Mon Sep 17 00:00:00 2001 From: tobin-ford Date: Fri, 26 Jul 2024 18:07:15 -0600 Subject: [PATCH 6/9] geospatial templating general examples --- pvdeg/geospatial.py | 14 +- .../Geospatial Templates.ipynb | 183 ++++++++++++------ 2 files changed, 141 insertions(+), 56 deletions(-) diff --git a/pvdeg/geospatial.py b/pvdeg/geospatial.py index 7a5dac6..e441274 100644 --- a/pvdeg/geospatial.py +++ b/pvdeg/geospatial.py @@ -78,6 +78,16 @@ def start_dask(hpc=None): return client +def _df_from_arbitrary(res, func): + if isinstance(res, pd.DataFrame): + return res + elif isinstance(res, pd.Series): + return pd.DataFrame(res, columns=[func.__name__]) + elif isinstance(res, (int, float)): + return pd.DataFrame([res], columns=[func.__name__]) + + else: + raise NotImplementedError(f"function return type: {type(res)} not available for geospatial analysis yet.") def calc_gid(ds_gid, meta_gid, func, **kwargs): """ @@ -101,8 +111,8 @@ def calc_gid(ds_gid, meta_gid, func, **kwargs): """ df_weather = ds_gid.to_dataframe() - res = func(weather_df=df_weather, meta=meta_gid, **kwargs) # this will sometimes be a dataframe already, if not -> make it a dataframe - df_res = res if isinstance(res, pd.DataFrame) else pd.DataFrame([res], columns=[func.__name__]) # convert numeric to dataframe + res = func(weather_df=df_weather, meta=meta_gid, **kwargs) + df_res = _df_from_arbitrary(res, func) # convert all return types to dataframe ds_res = xr.Dataset.from_dataframe(df_res) if not df_res.index.name: diff --git a/tutorials_and_tools/tutorials_and_tools/Geospatial Templates.ipynb b/tutorials_and_tools/tutorials_and_tools/Geospatial Templates.ipynb index 5a2d7f0..cd3d529 100644 --- a/tutorials_and_tools/tutorials_and_tools/Geospatial Templates.ipynb +++ b/tutorials_and_tools/tutorials_and_tools/Geospatial Templates.ipynb @@ -39,12 +39,12 @@ "\n", "We can also use a `GeospatialScenario` object. See the [Geospatial Scenario Tutorial](./Scenario%20-%20Geospatial.ipynb) for more information on how to use this approach.\n", "\n", - "*The cell below loads a pickled xarray object, this is not the best way to do this. xarray datasets should be stored at `.nc` - netcdf files*" + "*The cell below loads a pickled xarray object, this is not the best way to do this. xarray datasets should be stored as `.nc` - netcdf files*" ] }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 3, "metadata": {}, "outputs": [ { @@ -428,49 +428,49 @@ "Attributes:\n", " full_version_record: {"rex": "0.2.80", "pandas": "2.0.0", "numpy": "1.23...\n", " package: rex\n", - " version: 4.0.0
  • full_version_record :
    {"rex": "0.2.80", "pandas": "2.0.0", "numpy": "1.23.5", "python": "3.9.16 (main, Mar 8 2023, 14:00:05) \\n[GCC 11.2.0]", "click": "8.1.3", "h5py": "3.7.0", "h5pyd": "0.14.0", "scipy": "1.10.1"}
    package :
    rex
    version :
    4.0.0
  • " ], "text/plain": [ " Size: 9MB\n", @@ -502,7 +502,7 @@ " version: 4.0.0" ] }, - "execution_count": 2, + "execution_count": 3, "metadata": {}, "output_type": "execute_result" } @@ -533,7 +533,7 @@ "source": [ "# Examples\n", "\n", - "### 98ᵗʰ module percential temperature at Standoff Height\n", + "## 98ᵗʰ module percential temperature at Standoff Height\n", "\n", "Say we want to estimate the 98ᵗʰ percential temperature for the module at the given tilt, azimuth, and x_eff. `PVDeg` has a function to do this, `pvdeg.standards.T98_estimate` BUT it doesn't have a preset geospatial template. We will need to make one.\n", "\n", @@ -547,7 +547,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 4, "metadata": {}, "outputs": [], "source": [ @@ -565,7 +565,7 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 10, "metadata": {}, "outputs": [ { @@ -573,46 +573,71 @@ "output_type": "stream", "text": [ "The array tilt angle was not provided, therefore the latitude tilt of 39.9 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.9 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.7 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.8 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.8 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.4 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.5 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.4 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.6 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.5 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.6 was used.\n", "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n" ] - }, - { - "ename": "TypeError", - "evalue": "cell() got an unexpected keyword argument 'conf_inf'", - "output_type": "error", - "traceback": [ - "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[1;31mTypeError\u001b[0m Traceback (most recent call last)", - "Cell \u001b[1;32mIn[17], line 1\u001b[0m\n\u001b[1;32m----> 1\u001b[0m \u001b[43mpvdeg\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mgeospatial\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43manalysis\u001b[49m\u001b[43m(\u001b[49m\n\u001b[0;32m 2\u001b[0m \u001b[43m \u001b[49m\u001b[43mweather_ds\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mgeo_weather\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 3\u001b[0m \u001b[43m \u001b[49m\u001b[43mmeta_df\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mgeo_meta\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 4\u001b[0m \u001b[43m \u001b[49m\u001b[43mfunc\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mpvdeg\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mstandards\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mT98_estimate\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 5\u001b[0m \n\u001b[0;32m 6\u001b[0m \u001b[43m \u001b[49m\u001b[43mtemplate\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mtemplate\u001b[49m\n\u001b[0;32m 7\u001b[0m \u001b[43m)\u001b[49m\n", - "File \u001b[1;32m~\\dev\\PVDegradationTools\\pvdeg\\geospatial.py:177\u001b[0m, in \u001b[0;36manalysis\u001b[1;34m(weather_ds, meta_df, func, template, **func_kwargs)\u001b[0m\n\u001b[0;32m 172\u001b[0m \u001b[38;5;66;03m# future_meta_df = client.scatter(meta_df)\u001b[39;00m\n\u001b[0;32m 173\u001b[0m kwargs \u001b[38;5;241m=\u001b[39m {\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mfunc\u001b[39m\u001b[38;5;124m\"\u001b[39m: func, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mfuture_meta_df\u001b[39m\u001b[38;5;124m\"\u001b[39m: meta_df, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mfunc_kwargs\u001b[39m\u001b[38;5;124m\"\u001b[39m: func_kwargs}\n\u001b[0;32m 175\u001b[0m stacked \u001b[38;5;241m=\u001b[39m weather_ds\u001b[38;5;241m.\u001b[39mmap_blocks(\n\u001b[0;32m 176\u001b[0m calc_block, kwargs\u001b[38;5;241m=\u001b[39mkwargs, template\u001b[38;5;241m=\u001b[39mtemplate\n\u001b[1;32m--> 177\u001b[0m )\u001b[38;5;241m.\u001b[39mcompute()\n\u001b[0;32m 179\u001b[0m \u001b[38;5;66;03m# lats = stacked.latitude.values.flatten()\u001b[39;00m\n\u001b[0;32m 180\u001b[0m \u001b[38;5;66;03m# lons = stacked.longitude.values.flatten()\u001b[39;00m\n\u001b[0;32m 181\u001b[0m stacked \u001b[38;5;241m=\u001b[39m stacked\u001b[38;5;241m.\u001b[39mdrop([\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mgid\u001b[39m\u001b[38;5;124m\"\u001b[39m])\n", - "File \u001b[1;32mc:\\ProgramData\\anaconda3\\envs\\fem_diff\\lib\\site-packages\\xarray\\core\\dataset.py:8766\u001b[0m, in \u001b[0;36mDataset.map_blocks\u001b[1;34m(self, func, args, kwargs, template)\u001b[0m\n\u001b[0;32m 8664\u001b[0m \u001b[38;5;250m\u001b[39m\u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[0;32m 8665\u001b[0m \u001b[38;5;124;03mApply a function to each block of this Dataset.\u001b[39;00m\n\u001b[0;32m 8666\u001b[0m \n\u001b[1;32m (...)\u001b[0m\n\u001b[0;32m 8762\u001b[0m \u001b[38;5;124;03m a (time) float64 192B dask.array\u001b[39;00m\n\u001b[0;32m 8763\u001b[0m \u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[0;32m 8764\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01mxarray\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mcore\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mparallel\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m map_blocks\n\u001b[1;32m-> 8766\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mmap_blocks\u001b[49m\u001b[43m(\u001b[49m\u001b[43mfunc\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mtemplate\u001b[49m\u001b[43m)\u001b[49m\n", - "File \u001b[1;32mc:\\ProgramData\\anaconda3\\envs\\fem_diff\\lib\\site-packages\\xarray\\core\\parallel.py:409\u001b[0m, in \u001b[0;36mmap_blocks\u001b[1;34m(func, obj, args, kwargs, template)\u001b[0m\n\u001b[0;32m 403\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mTypeError\u001b[39;00m(\n\u001b[0;32m 404\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mCannot pass dask collections in kwargs yet. Please compute or \u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[0;32m 405\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mload values before passing to map_blocks.\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[0;32m 406\u001b[0m )\n\u001b[0;32m 408\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m is_dask_collection(obj):\n\u001b[1;32m--> 409\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m func(obj, \u001b[38;5;241m*\u001b[39margs, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs)\n\u001b[0;32m 411\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m 412\u001b[0m \u001b[38;5;28;01mimport\u001b[39;00m \u001b[38;5;21;01mdask\u001b[39;00m\n", - "File \u001b[1;32m~\\dev\\PVDegradationTools\\pvdeg\\geospatial.py:136\u001b[0m, in \u001b[0;36mcalc_block\u001b[1;34m(weather_ds_block, future_meta_df, func, func_kwargs)\u001b[0m\n\u001b[0;32m 113\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mcalc_block\u001b[39m(weather_ds_block, future_meta_df, func, func_kwargs):\n\u001b[0;32m 114\u001b[0m \u001b[38;5;250m \u001b[39m\u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[0;32m 115\u001b[0m \u001b[38;5;124;03m Calculates a block of gids for a given function.\u001b[39;00m\n\u001b[0;32m 116\u001b[0m \n\u001b[1;32m (...)\u001b[0m\n\u001b[0;32m 131\u001b[0m \u001b[38;5;124;03m Dataset with results for a block of gids.\u001b[39;00m\n\u001b[0;32m 132\u001b[0m \u001b[38;5;124;03m \"\"\"\u001b[39;00m\n\u001b[0;32m 134\u001b[0m res \u001b[38;5;241m=\u001b[39m weather_ds_block\u001b[38;5;241m.\u001b[39mgroupby(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mgid\u001b[39m\u001b[38;5;124m\"\u001b[39m, squeeze\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mFalse\u001b[39;00m)\u001b[38;5;241m.\u001b[39mmap(\n\u001b[0;32m 135\u001b[0m \u001b[38;5;28;01mlambda\u001b[39;00m ds_gid: calc_gid(\n\u001b[1;32m--> 136\u001b[0m ds_gid\u001b[38;5;241m=\u001b[39mds_gid\u001b[38;5;241m.\u001b[39msqueeze(),\n\u001b[0;32m 137\u001b[0m meta_gid\u001b[38;5;241m=\u001b[39mfuture_meta_df\u001b[38;5;241m.\u001b[39mloc[ds_gid[\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mgid\u001b[39m\u001b[38;5;124m\"\u001b[39m]\u001b[38;5;241m.\u001b[39mvalues[\u001b[38;5;241m0\u001b[39m]]\u001b[38;5;241m.\u001b[39mto_dict(),\n\u001b[0;32m 138\u001b[0m func\u001b[38;5;241m=\u001b[39mfunc,\n\u001b[0;32m 139\u001b[0m \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mfunc_kwargs,\n\u001b[0;32m 140\u001b[0m )\n\u001b[0;32m 141\u001b[0m )\n\u001b[0;32m 142\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m res\n", - "File \u001b[1;32mc:\\ProgramData\\anaconda3\\envs\\fem_diff\\lib\\site-packages\\xarray\\core\\groupby.py:1699\u001b[0m, in \u001b[0;36mDatasetGroupByBase.map\u001b[1;34m(self, func, args, shortcut, **kwargs)\u001b[0m\n\u001b[0;32m 1663\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mmap\u001b[39m(\n\u001b[0;32m 1664\u001b[0m \u001b[38;5;28mself\u001b[39m,\n\u001b[0;32m 1665\u001b[0m func: Callable[\u001b[38;5;241m.\u001b[39m\u001b[38;5;241m.\u001b[39m\u001b[38;5;241m.\u001b[39m, Dataset],\n\u001b[1;32m (...)\u001b[0m\n\u001b[0;32m 1668\u001b[0m \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs: Any,\n\u001b[0;32m 1669\u001b[0m ) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m Dataset:\n\u001b[0;32m 1670\u001b[0m \u001b[38;5;250m \u001b[39m\u001b[38;5;124;03m\"\"\"Apply a function to each Dataset in the group and concatenate them\u001b[39;00m\n\u001b[0;32m 1671\u001b[0m \u001b[38;5;124;03m together into a new Dataset.\u001b[39;00m\n\u001b[0;32m 1672\u001b[0m \n\u001b[1;32m (...)\u001b[0m\n\u001b[0;32m 1697\u001b[0m \u001b[38;5;124;03m The result of splitting, applying and combining this dataset.\u001b[39;00m\n\u001b[0;32m 1698\u001b[0m \u001b[38;5;124;03m \"\"\"\u001b[39;00m\n\u001b[1;32m-> 1699\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_map_maybe_warn(func, args, shortcut, warn_squeeze\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mTrue\u001b[39;00m, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs)\n", - "File \u001b[1;32mc:\\ProgramData\\anaconda3\\envs\\fem_diff\\lib\\site-packages\\xarray\\core\\groupby.py:1711\u001b[0m, in \u001b[0;36mDatasetGroupByBase._map_maybe_warn\u001b[1;34m(self, func, args, shortcut, warn_squeeze, **kwargs)\u001b[0m\n\u001b[0;32m 1701\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21m_map_maybe_warn\u001b[39m(\n\u001b[0;32m 1702\u001b[0m \u001b[38;5;28mself\u001b[39m,\n\u001b[0;32m 1703\u001b[0m func: Callable[\u001b[38;5;241m.\u001b[39m\u001b[38;5;241m.\u001b[39m\u001b[38;5;241m.\u001b[39m, Dataset],\n\u001b[1;32m (...)\u001b[0m\n\u001b[0;32m 1708\u001b[0m ) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m Dataset:\n\u001b[0;32m 1709\u001b[0m \u001b[38;5;66;03m# ignore shortcut if set (for now)\u001b[39;00m\n\u001b[0;32m 1710\u001b[0m applied \u001b[38;5;241m=\u001b[39m (func(ds, \u001b[38;5;241m*\u001b[39margs, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs) \u001b[38;5;28;01mfor\u001b[39;00m ds \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_iter_grouped(warn_squeeze))\n\u001b[1;32m-> 1711\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_combine\u001b[49m\u001b[43m(\u001b[49m\u001b[43mapplied\u001b[49m\u001b[43m)\u001b[49m\n", - "File \u001b[1;32mc:\\ProgramData\\anaconda3\\envs\\fem_diff\\lib\\site-packages\\xarray\\core\\groupby.py:1731\u001b[0m, in \u001b[0;36mDatasetGroupByBase._combine\u001b[1;34m(self, applied)\u001b[0m\n\u001b[0;32m 1729\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21m_combine\u001b[39m(\u001b[38;5;28mself\u001b[39m, applied):\n\u001b[0;32m 1730\u001b[0m \u001b[38;5;250m \u001b[39m\u001b[38;5;124;03m\"\"\"Recombine the applied objects like the original.\"\"\"\u001b[39;00m\n\u001b[1;32m-> 1731\u001b[0m applied_example, applied \u001b[38;5;241m=\u001b[39m \u001b[43mpeek_at\u001b[49m\u001b[43m(\u001b[49m\u001b[43mapplied\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 1732\u001b[0m coord, dim, positions \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_infer_concat_args(applied_example)\n\u001b[0;32m 1733\u001b[0m combined \u001b[38;5;241m=\u001b[39m concat(applied, dim)\n", - "File \u001b[1;32mc:\\ProgramData\\anaconda3\\envs\\fem_diff\\lib\\site-packages\\xarray\\core\\utils.py:205\u001b[0m, in \u001b[0;36mpeek_at\u001b[1;34m(iterable)\u001b[0m\n\u001b[0;32m 201\u001b[0m \u001b[38;5;250m\u001b[39m\u001b[38;5;124;03m\"\"\"Returns the first value from iterable, as well as a new iterator with\u001b[39;00m\n\u001b[0;32m 202\u001b[0m \u001b[38;5;124;03mthe same content as the original iterable\u001b[39;00m\n\u001b[0;32m 203\u001b[0m \u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[0;32m 204\u001b[0m gen \u001b[38;5;241m=\u001b[39m \u001b[38;5;28miter\u001b[39m(iterable)\n\u001b[1;32m--> 205\u001b[0m peek \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mnext\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mgen\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 206\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m peek, itertools\u001b[38;5;241m.\u001b[39mchain([peek], gen)\n", - "File \u001b[1;32mc:\\ProgramData\\anaconda3\\envs\\fem_diff\\lib\\site-packages\\xarray\\core\\groupby.py:1710\u001b[0m, in \u001b[0;36m\u001b[1;34m(.0)\u001b[0m\n\u001b[0;32m 1701\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21m_map_maybe_warn\u001b[39m(\n\u001b[0;32m 1702\u001b[0m \u001b[38;5;28mself\u001b[39m,\n\u001b[0;32m 1703\u001b[0m func: Callable[\u001b[38;5;241m.\u001b[39m\u001b[38;5;241m.\u001b[39m\u001b[38;5;241m.\u001b[39m, Dataset],\n\u001b[1;32m (...)\u001b[0m\n\u001b[0;32m 1708\u001b[0m ) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m Dataset:\n\u001b[0;32m 1709\u001b[0m \u001b[38;5;66;03m# ignore shortcut if set (for now)\u001b[39;00m\n\u001b[1;32m-> 1710\u001b[0m applied \u001b[38;5;241m=\u001b[39m (func(ds, \u001b[38;5;241m*\u001b[39margs, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs) \u001b[38;5;28;01mfor\u001b[39;00m ds \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_iter_grouped(warn_squeeze))\n\u001b[0;32m 1711\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_combine(applied)\n", - "File \u001b[1;32m~\\dev\\PVDegradationTools\\pvdeg\\geospatial.py:137\u001b[0m, in \u001b[0;36m\u001b[1;34m(ds_gid)\u001b[0m\n\u001b[0;32m 113\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mcalc_block\u001b[39m(weather_ds_block, future_meta_df, func, func_kwargs):\n\u001b[0;32m 114\u001b[0m \u001b[38;5;250m \u001b[39m\u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[0;32m 115\u001b[0m \u001b[38;5;124;03m Calculates a block of gids for a given function.\u001b[39;00m\n\u001b[0;32m 116\u001b[0m \n\u001b[1;32m (...)\u001b[0m\n\u001b[0;32m 131\u001b[0m \u001b[38;5;124;03m Dataset with results for a block of gids.\u001b[39;00m\n\u001b[0;32m 132\u001b[0m \u001b[38;5;124;03m \"\"\"\u001b[39;00m\n\u001b[0;32m 134\u001b[0m res \u001b[38;5;241m=\u001b[39m weather_ds_block\u001b[38;5;241m.\u001b[39mgroupby(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mgid\u001b[39m\u001b[38;5;124m\"\u001b[39m, squeeze\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mFalse\u001b[39;00m)\u001b[38;5;241m.\u001b[39mmap(\n\u001b[0;32m 135\u001b[0m \u001b[38;5;28;01mlambda\u001b[39;00m ds_gid: calc_gid(\n\u001b[0;32m 136\u001b[0m ds_gid\u001b[38;5;241m=\u001b[39mds_gid\u001b[38;5;241m.\u001b[39msqueeze(),\n\u001b[1;32m--> 137\u001b[0m meta_gid\u001b[38;5;241m=\u001b[39mfuture_meta_df\u001b[38;5;241m.\u001b[39mloc[ds_gid[\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mgid\u001b[39m\u001b[38;5;124m\"\u001b[39m]\u001b[38;5;241m.\u001b[39mvalues[\u001b[38;5;241m0\u001b[39m]]\u001b[38;5;241m.\u001b[39mto_dict(),\n\u001b[0;32m 138\u001b[0m func\u001b[38;5;241m=\u001b[39mfunc,\n\u001b[0;32m 139\u001b[0m \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mfunc_kwargs,\n\u001b[0;32m 140\u001b[0m )\n\u001b[0;32m 141\u001b[0m )\n\u001b[0;32m 142\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m res\n", - "File \u001b[1;32m~\\dev\\PVDegradationTools\\pvdeg\\geospatial.py:106\u001b[0m, in \u001b[0;36mcalc_gid\u001b[1;34m(ds_gid, meta_gid, func, **kwargs)\u001b[0m\n\u001b[0;32m 0\u001b[0m \n", - "File \u001b[1;32m~\\dev\\PVDegradationTools\\pvdeg\\standards.py:502\u001b[0m, in \u001b[0;36mT98_estimate\u001b[1;34m(weather_df, meta, weather_kwarg, sky_model, temp_model, conf_0, conf_inf, wind_factor, tilt, azimuth, x_eff, x_0)\u001b[0m\n\u001b[0;32m 492\u001b[0m solar_position \u001b[38;5;241m=\u001b[39m spectral\u001b[38;5;241m.\u001b[39msolar_position(weather_df, meta)\n\u001b[0;32m 493\u001b[0m poa \u001b[38;5;241m=\u001b[39m spectral\u001b[38;5;241m.\u001b[39mpoa_irradiance(\n\u001b[0;32m 494\u001b[0m weather_df\u001b[38;5;241m=\u001b[39mweather_df,\n\u001b[0;32m 495\u001b[0m meta\u001b[38;5;241m=\u001b[39mmeta,\n\u001b[1;32m (...)\u001b[0m\n\u001b[0;32m 499\u001b[0m sky_model\u001b[38;5;241m=\u001b[39msky_model,\n\u001b[0;32m 500\u001b[0m )\n\u001b[1;32m--> 502\u001b[0m T_inf \u001b[38;5;241m=\u001b[39m \u001b[43mtemperature\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mcell\u001b[49m\u001b[43m(\u001b[49m\n\u001b[0;32m 503\u001b[0m \u001b[43m \u001b[49m\u001b[43mweather_df\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mweather_df\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 504\u001b[0m \u001b[43m \u001b[49m\u001b[43mmeta\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mmeta\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 505\u001b[0m \u001b[43m \u001b[49m\u001b[43mpoa\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mpoa\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 506\u001b[0m \u001b[43m \u001b[49m\u001b[43mtemp_model\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mtemp_model\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 507\u001b[0m \u001b[43m \u001b[49m\u001b[43mconf_inf\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mconf_inf\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 508\u001b[0m \u001b[43m \u001b[49m\u001b[43mwind_factor\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mwind_factor\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 509\u001b[0m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 510\u001b[0m T98_inf \u001b[38;5;241m=\u001b[39m T_inf\u001b[38;5;241m.\u001b[39mquantile(q\u001b[38;5;241m=\u001b[39m\u001b[38;5;241m0.98\u001b[39m, interpolation\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mlinear\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[0;32m 512\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m x_eff \u001b[38;5;241m==\u001b[39m \u001b[38;5;28;01mNone\u001b[39;00m:\n", - "\u001b[1;31mTypeError\u001b[0m: cell() got an unexpected keyword argument 'conf_inf'" - ] } ], "source": [ - "pvdeg.geospatial.analysis(\n", + "geo_estimate_temp = pvdeg.geospatial.analysis(\n", " weather_ds=geo_weather,\n", " meta_df=geo_meta,\n", " func=pvdeg.standards.T98_estimate,\n", - "\n", " template=template\n", ")" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Glass Glass Estimated Module Temperature\n", + "\n", + "Now we want to calculate geospatial timeseries temperature values for a module using `pvdeg.temperature.module`. This is not super practical because all `pvdeg` functions that need to use tempeature for their calculations preform the temperature calculation internally, this is just for show. \n", + "\n", + "This calculation differs from the above because the temperature functions return the module temperature in a timeseries format. So we care about 2 dimensions, location identifier and TIME." + ] + }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ + "# define output shape\n", + "shapes = {\n", + " \"module_temperature\" : (\"gid\", \"time\") # one return value at each datapoint, only dependent on datapoint, not time \n", + "}\n", + "\n", + "# create xarray template for output to be populated when analysis is run\n", + "temperature_template = pvdeg.geospatial.output_template(\n", + " ds_gids=geo_weather, \n", + " shapes=shapes,\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 22, "metadata": {}, "outputs": [ { @@ -620,33 +645,83 @@ "output_type": "stream", "text": [ "The array tilt angle was not provided, therefore the latitude tilt of 39.9 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.9 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.7 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.8 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.8 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.4 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.5 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.4 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.6 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.5 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.6 was used.\n", "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n" ] - }, + } + ], + "source": [ + "geo_temperature_res = pvdeg.geospatial.analysis(\n", + " weather_ds=geo_weather,\n", + " meta_df=geo_meta,\n", + " func=pvdeg.temperature.module,\n", + " template=temperature_template, # use the template we created\n", + "\n", + " conf='open_rack_glass_glass' # provide kwargs for function here\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "metadata": {}, + "outputs": [ { "data": { "text/plain": [ - "50.695646304025196" + "[]" ] }, - "execution_count": 3, + "execution_count": 38, "metadata": {}, "output_type": "execute_result" + }, + { + "data": { + "image/png": "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", + "text/plain": [ + "
    " + ] + }, + "metadata": {}, + "output_type": "display_data" } ], "source": [ - "pvdeg.standards.T98_estimate(\n", - " weather_df=geo_weather.isel(gid=0).to_dataframe(),\n", - " meta=geo_meta.iloc[0].to_dict()\n", - ")" + "# plot the temperature at ONE of the geospatial analysis result locations but we have calculated all of these.\n", + "import matplotlib.pyplot as plt\n", + "module_temps = geo_temperature_res['module'].sel(latitude=39.89, longitude='-106.42').values\n", + "\n", + "plt.plot(module_temps)" ] }, { - "cell_type": "code", - "execution_count": null, + "cell_type": "markdown", "metadata": {}, - "outputs": [], - "source": [] + "source": [ + "## Self Explaining Code\n", + "\n", + "If we are looking at adding templates for other functions, we can also look at the 3 presaved templates for existing `pvdeg` functions. Visit [pvdeg.geospatial.template_parameters](../../pvdeg/geospatial.py) and inspect this function to see how these different target functions utilize templates and shapes." + ] } ], "metadata": { From 2af33e3aed140132c397d69b0dd206c83fb73d03 Mon Sep 17 00:00:00 2001 From: tobin-ford Date: Mon, 29 Jul 2024 12:36:14 -0600 Subject: [PATCH 7/9] geospatial autotemplating --- pvdeg/decorators.py | 38 ++ pvdeg/fatigue.py | 26 +- pvdeg/geospatial.py | 27 +- pvdeg/standards.py | 30 +- pvdeg/temperature.py | 17 +- .../Geospatial Templates.ipynb | 561 +++++++++++++++--- 6 files changed, 577 insertions(+), 122 deletions(-) create mode 100644 pvdeg/decorators.py diff --git a/pvdeg/decorators.py b/pvdeg/decorators.py new file mode 100644 index 0000000..6271bec --- /dev/null +++ b/pvdeg/decorators.py @@ -0,0 +1,38 @@ +""" +Utility Decorators for PVDeg. +Private API, should only be used in PVDeg implemenation files. +""" + +def geospatial_result_type(value: bool, shape_names: list[str]) -> None: + """ + Add an attribute to the functions that can be run with geospatial analysis. + Strict typing is not enough for this purpose so we can view this attribute + at runtime to create a template for the function. + + For single numeric results, includes tabular numeric data + >>> value = False (0) + + Example if a function returns a dataframe with 1 row of numerics (not timeseries) + `pvdeg.standards.standoff` does this. + + For timeseries results + >>> value = True (1) + + Example, `pvdeg.temperature.temperature` + + For both numeric and timeseries results, we care about the output names of the funtion. + When a function returns a dataframe, the names will simply be the dataframe column names. + >>> return df # function returns dataframe + >>> df.columns = ["rh", "dry_bulb", "irradiance"] # dataframe column names + >>> func.shape_names = ["rh", "dry_bulb", "irradiance"] # function attribute names + + When a function returns a numeric, or tuple of numerics, the names will correspond to the meanings of each unpacked variable. + >>> return (T98, x_eff) # function return tuple of numerics + >>> func.shape_names = ["T98", "x_eff"] # function attribute names + + """ + def decorator(func): + setattr(func, "numeric_or_timeseries", value) + setattr(func, "shape_names", shape_names) + return func + return decorator \ No newline at end of file diff --git a/pvdeg/fatigue.py b/pvdeg/fatigue.py index 82fbc22..89fcf47 100644 --- a/pvdeg/fatigue.py +++ b/pvdeg/fatigue.py @@ -1,7 +1,8 @@ import numpy as np import pandas as pd from scipy.constants import convert_temperature -from . import temperature +from pvdeg import temperature +from pvdeg.decorators import geospatial_result_type def _avg_daily_temp_change(time_range, temp_cell): @@ -97,18 +98,19 @@ def _times_over_reversal_number(temp_cell, reversal_temp): return num_changes_temp_hist +@geospatial_result_type(0, ['damage']) def solder_fatigue( - weather_df, - meta, - time_range=None, - temp_cell=None, - reversal_temp=54.8, - n=1.9, - b=0.33, - C1=405.6, - Q=0.12, - wind_factor=None, -): + weather_df: pd.DataFrame, + meta: dict, + time_range: pd.Series = None, + temp_cell: pd.Series = None, + reversal_temp: float = 54.8, + n: float = 1.9, + b: float = 0.33, + C1: float = 405.6, + Q: float = 0.12, + wind_factor: float = 0.33, +) -> float: """ Get the Thermomechanical Fatigue of flat plate photovoltaic module solder joints. Damage will be returned as the rate of solder fatigue for one year. Based on: diff --git a/pvdeg/geospatial.py b/pvdeg/geospatial.py index e441274..7a8efbb 100644 --- a/pvdeg/geospatial.py +++ b/pvdeg/geospatial.py @@ -2,9 +2,11 @@ Collection of classes and functions for geospatial analysis. """ -from . import standards -from . import humidity -from . import letid +from . import ( + standards, + humidity, + letid, +) import xarray as xr import dask.array as da @@ -16,6 +18,8 @@ import cartopy.crs as ccrs import cartopy.io.shapereader as shpreader +from collections.abc import Callable + def start_dask(hpc=None): """ @@ -363,6 +367,23 @@ def zero_template( return res +def auto_template(func: Callable, ds_gids: xr.Dataset)->xr.Dataset: + + if not (hasattr(func, "numeric_or_timeseries") and hasattr(func, "shape_names")): + raise ValueError(f"{func.__name__} cannot be autotemplated. create a template manually") + + if func.numeric_or_timeseries == 0: + shapes = {datavar: ("gid",) for datavar in func.shape_names} + elif func.numeric_or_timeseries == 1: + shapes = {datavar: ("gid","time") for datavar in func.shape_names} + + template = output_template( # zeros_template? + ds_gids=ds_gids, + shapes=shapes + ) + + return template + def plot_USA( xr_res, cmap="viridis", vmin=None, vmax=None, title=None, cb_title=None, fp=None ): diff --git a/pvdeg/standards.py b/pvdeg/standards.py index 0a1f0d2..cad602f 100644 --- a/pvdeg/standards.py +++ b/pvdeg/standards.py @@ -11,10 +11,12 @@ from pathlib import Path from random import random from concurrent.futures import ProcessPoolExecutor, as_completed +from typing import Union, Tuple # from gaps import ProjectPoints from pvdeg import temperature, spectral, utilities, weather +from pvdeg.decorators import geospatial_result_type def eff_gap_parameters( @@ -187,20 +189,21 @@ def eff_gap(T_0, T_inf, T_measured, T_ambient, poa, x_0=6.5, poa_min=400, t_amb_ return x_eff +@geospatial_result_type(0, ["x","T98_0", "T98_inf"]) # numeric result, with corresponding datavariable names def standoff( - weather_df=None, - meta=None, - weather_kwarg=None, - tilt=None, - azimuth=None, - sky_model="isotropic", - temp_model="sapm", - conf_0="insulated_back_glass_polymer", - conf_inf="open_rack_glass_polymer", - T98=70, # [°C] - x_0=6.5, # [cm] - wind_factor=0.33, -): + weather_df: pd.DataFrame = None, + meta: dict = None, + weather_kwarg: dict = None, + tilt: Union[float, int] = None, + azimuth: Union[float, int] = None, + sky_model: str = "isotropic", + temp_model: str = "sapm", + conf_0: str = "insulated_back_glass_polymer", + conf_inf: str = "open_rack_glass_polymer", + T98: float = 70, # [°C] + x_0: float = 6.5, # [cm] + wind_factor: float = 0.33, +) -> pd.DataFrame: """ Calculate a minimum standoff distance for roof mounded PV systems. Will default to horizontal tilt. If the azimuth is not provided, it @@ -414,6 +417,7 @@ def interpret_standoff(standoff_1=None, standoff_2=None): return Output +@geospatial_result_type(0, ["T98"]) def T98_estimate( weather_df=None, meta=None, diff --git a/pvdeg/temperature.py b/pvdeg/temperature.py index d49bf2a..e4968c3 100644 --- a/pvdeg/temperature.py +++ b/pvdeg/temperature.py @@ -4,7 +4,8 @@ import pvlib import pvdeg - +import pandas as pd +from typing import Union def module( weather_df, @@ -97,13 +98,13 @@ def module( def cell( - weather_df, - meta, - poa=None, - temp_model="sapm", - conf="open_rack_glass_polymer", - wind_factor=0.33, -): + weather_df: pd.DataFrame, + meta: dict, + poa: Union[pd.DataFrame, pd.Series] = None, + temp_model: str = "sapm", + conf: str = "open_rack_glass_polymer", + wind_factor: float = 0.33, +) -> pd.DataFrame: """ Calculate the PV cell temperature using PVLIB Currently this only supports the SAPM temperature model. diff --git a/tutorials_and_tools/tutorials_and_tools/Geospatial Templates.ipynb b/tutorials_and_tools/tutorials_and_tools/Geospatial Templates.ipynb index cd3d529..d39f3dc 100644 --- a/tutorials_and_tools/tutorials_and_tools/Geospatial Templates.ipynb +++ b/tutorials_and_tools/tutorials_and_tools/Geospatial Templates.ipynb @@ -11,6 +11,7 @@ "import pickle\n", "import xarray as xr\n", "import pandas as pd\n", + "import numpy as np\n", "import os" ] }, @@ -428,49 +429,49 @@ "Attributes:\n", " full_version_record: {"rex": "0.2.80", "pandas": "2.0.0", "numpy": "1.23...\n", " package: rex\n", - " version: 4.0.0
  • full_version_record :
    {"rex": "0.2.80", "pandas": "2.0.0", "numpy": "1.23.5", "python": "3.9.16 (main, Mar 8 2023, 14:00:05) \\n[GCC 11.2.0]", "click": "8.1.3", "h5py": "3.7.0", "h5pyd": "0.14.0", "scipy": "1.10.1"}
    package :
    rex
    version :
    4.0.0
  • " ], "text/plain": [ " Size: 9MB\n", @@ -520,7 +521,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Creating Templates Programattically\n", + "# Creating Templates Manually\n", "\n", "`pvdeg.geospatial.ouput_template` we can produce a template for our result data. \n", "\n", @@ -547,7 +548,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 13, "metadata": {}, "outputs": [], "source": [ @@ -565,38 +566,9 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "The array tilt angle was not provided, therefore the latitude tilt of 39.9 was used.\n", - "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", - "The array tilt angle was not provided, therefore the latitude tilt of 39.9 was used.\n", - "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", - "The array tilt angle was not provided, therefore the latitude tilt of 39.7 was used.\n", - "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", - "The array tilt angle was not provided, therefore the latitude tilt of 39.8 was used.\n", - "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", - "The array tilt angle was not provided, therefore the latitude tilt of 39.8 was used.\n", - "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", - "The array tilt angle was not provided, therefore the latitude tilt of 39.4 was used.\n", - "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", - "The array tilt angle was not provided, therefore the latitude tilt of 39.5 was used.\n", - "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", - "The array tilt angle was not provided, therefore the latitude tilt of 39.4 was used.\n", - "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", - "The array tilt angle was not provided, therefore the latitude tilt of 39.6 was used.\n", - "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", - "The array tilt angle was not provided, therefore the latitude tilt of 39.5 was used.\n", - "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", - "The array tilt angle was not provided, therefore the latitude tilt of 39.6 was used.\n", - "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n" - ] - } - ], + "outputs": [], "source": [ "geo_estimate_temp = pvdeg.geospatial.analysis(\n", " weather_ds=geo_weather,\n", @@ -619,7 +591,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -637,7 +609,54 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "geo_temperature_res = pvdeg.geospatial.analysis(\n", + " weather_ds=geo_weather,\n", + " meta_df=geo_meta,\n", + " func=pvdeg.temperature.module,\n", + " template=temperature_template, # use the template we created\n", + "\n", + " conf='open_rack_glass_glass' # provide kwargs for function here\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# plot the temperature at ONE of the geospatial analysis result locations but we have calculated all of these.\n", + "import matplotlib.pyplot as plt\n", + "module_temps = geo_temperature_res['module'].sel(latitude=39.89, longitude='-106.42').values\n", + "\n", + "plt.plot(module_temps)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Self Explaining Code\n", + "\n", + "If we are looking at adding templates for other functions, we can also look at the 3 presaved templates for existing `pvdeg` functions. Visit [pvdeg.geospatial.template_parameters](../../pvdeg/geospatial.py) and inspect this function to see how these different target functions utilize templates and shapes." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Creating Templates Programatically\n", + "\n", + "We can use the new function below to generate a template for a given function. This can return a bad result which will fail or work improperly when running `pvdeg.geospatial.analysis` with the generated template. Results should be scrutinized to make sure they are the right format." + ] + }, + { + "cell_type": "code", + "execution_count": 5, "metadata": {}, "outputs": [ { @@ -667,61 +686,431 @@ "The array tilt angle was not provided, therefore the latitude tilt of 39.6 was used.\n", "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n" ] - } - ], - "source": [ - "geo_temperature_res = pvdeg.geospatial.analysis(\n", - " weather_ds=geo_weather,\n", - " meta_df=geo_meta,\n", - " func=pvdeg.temperature.module,\n", - " template=temperature_template, # use the template we created\n", - "\n", - " conf='open_rack_glass_glass' # provide kwargs for function here\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": 38, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "[]" - ] - }, - "execution_count": 38, - "metadata": {}, - "output_type": "execute_result" }, { "data": { - "image/png": "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", + "text/html": [ + "
    \n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
    <xarray.Dataset> Size: 784B\n",
    +       "Dimensions:       (latitude: 8, longitude: 10)\n",
    +       "Coordinates:\n",
    +       "  * latitude      (latitude) float64 64B 39.41 39.45 39.53 ... 39.69 39.81 39.89\n",
    +       "  * longitude     (longitude) float64 80B -106.4 -106.3 -106.3 ... -105.9 -105.9\n",
    +       "Data variables:\n",
    +       "    T98_estimate  (latitude, longitude) float64 640B nan nan nan ... nan nan nan
    " + ], "text/plain": [ - "
    " + " Size: 784B\n", + "Dimensions: (latitude: 8, longitude: 10)\n", + "Coordinates:\n", + " * latitude (latitude) float64 64B 39.41 39.45 39.53 ... 39.69 39.81 39.89\n", + " * longitude (longitude) float64 80B -106.4 -106.3 -106.3 ... -105.9 -105.9\n", + "Data variables:\n", + " T98_estimate (latitude, longitude) float64 640B nan nan nan ... nan nan nan" ] }, + "execution_count": 5, "metadata": {}, - "output_type": "display_data" + "output_type": "execute_result" } ], "source": [ - "# plot the temperature at ONE of the geospatial analysis result locations but we have calculated all of these.\n", - "import matplotlib.pyplot as plt\n", - "module_temps = geo_temperature_res['module'].sel(latitude=39.89, longitude='-106.42').values\n", + "standoff_template = pvdeg.geospatial.auto_template(pvdeg.standards.T98_estimate, ds_gids=geo_weather)\n", "\n", - "plt.plot(module_temps)" + "pvdeg.geospatial.analysis(\n", + " weather_ds=geo_weather,\n", + " meta_df=geo_meta,\n", + " func=pvdeg.standards.T98_estimate,\n", + " template=standoff_template\n", + ")" ] }, { - "cell_type": "markdown", + "cell_type": "code", + "execution_count": null, "metadata": {}, - "source": [ - "## Self Explaining Code\n", - "\n", - "If we are looking at adding templates for other functions, we can also look at the 3 presaved templates for existing `pvdeg` functions. Visit [pvdeg.geospatial.template_parameters](../../pvdeg/geospatial.py) and inspect this function to see how these different target functions utilize templates and shapes." - ] + "outputs": [], + "source": [] } ], "metadata": { From 74440d5176cffdd49488397f91022aabf94441cc Mon Sep 17 00:00:00 2001 From: tobin-ford Date: Mon, 29 Jul 2024 15:03:10 -0600 Subject: [PATCH 8/9] run docs, update tutorial --- .github/pull_request_template.md | 1 + .../pvdeg.geospatial.analysis.rst | 6 + .../pvdeg.geospatial.auto_template.rst | 6 + .../pvdeg.geospatial.calc_block.rst | 6 + .../pvdeg.geospatial.calc_gid.rst | 6 + .../pvdeg.geospatial.output_template.rst | 6 + .../pvdeg.geospatial.plot_Europe.rst | 6 + .../pvdeg.geospatial.plot_USA.rst | 6 + docs/source/_autosummary/pvdeg.geospatial.rst | 125 +++ .../pvdeg.geospatial.start_dask.rst | 6 + .../pvdeg.geospatial.template_parameters.rst | 6 + .../pvdeg.geospatial.zero_template.rst | 6 + docs/source/api.rst | 1 + .../user_guide/geospatial-templates.rst | 40 + docs/source/user_guide/index.rst | 1 + docs/source/user_guide/montecarlo.rst | 16 +- docs/source/whatsnew/index.rst | 2 + docs/source/whatsnew/releases/v0.3.5.rst | 2 +- docs/source/whatsnew/releases/v0.4.0.rst | 21 + pvdeg/decorators.py | 21 +- pvdeg/degradation.py | 57 +- pvdeg/design.py | 14 +- pvdeg/geospatial.py | 54 +- pvdeg/humidity.py | 17 +- pvdeg/letid.py | 2 + pvdeg/spectral.py | 40 +- pvdeg/standards.py | 1 + pvdeg/temperature.py | 4 + .../Geospatial Templates.ipynb | 776 ++++++++++++++++-- 29 files changed, 1120 insertions(+), 135 deletions(-) create mode 100644 docs/source/_autosummary/pvdeg.geospatial.analysis.rst create mode 100644 docs/source/_autosummary/pvdeg.geospatial.auto_template.rst create mode 100644 docs/source/_autosummary/pvdeg.geospatial.calc_block.rst create mode 100644 docs/source/_autosummary/pvdeg.geospatial.calc_gid.rst create mode 100644 docs/source/_autosummary/pvdeg.geospatial.output_template.rst create mode 100644 docs/source/_autosummary/pvdeg.geospatial.plot_Europe.rst create mode 100644 docs/source/_autosummary/pvdeg.geospatial.plot_USA.rst create mode 100644 docs/source/_autosummary/pvdeg.geospatial.rst create mode 100644 docs/source/_autosummary/pvdeg.geospatial.start_dask.rst create mode 100644 docs/source/_autosummary/pvdeg.geospatial.template_parameters.rst create mode 100644 docs/source/_autosummary/pvdeg.geospatial.zero_template.rst create mode 100644 docs/source/user_guide/geospatial-templates.rst create mode 100644 docs/source/whatsnew/releases/v0.4.0.rst diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 8a782f9..43e78ea 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -20,6 +20,7 @@ Please delete options that are not relevant. - [ ] I have performed a self-review of my code - [ ] Code changes are covered by tests. - [ ] Code changes have been evaluated for compatibility/integration with Scenario analysis (for future PRs) +- [ ] Code changes have been evaluated for compatibility/integration with geospatial autotemplating (for future PRs) - [ ] New functions added to __init__.py - [ ] API.rst is up to date, along with other sphinx docs pages - [ ] Example notebooks are rerun and differences in results scrutinized diff --git a/docs/source/_autosummary/pvdeg.geospatial.analysis.rst b/docs/source/_autosummary/pvdeg.geospatial.analysis.rst new file mode 100644 index 0000000..8de2ff6 --- /dev/null +++ b/docs/source/_autosummary/pvdeg.geospatial.analysis.rst @@ -0,0 +1,6 @@ +pvdeg.geospatial.analysis +========================= + +.. currentmodule:: pvdeg.geospatial + +.. autofunction:: analysis \ No newline at end of file diff --git a/docs/source/_autosummary/pvdeg.geospatial.auto_template.rst b/docs/source/_autosummary/pvdeg.geospatial.auto_template.rst new file mode 100644 index 0000000..b4df1a2 --- /dev/null +++ b/docs/source/_autosummary/pvdeg.geospatial.auto_template.rst @@ -0,0 +1,6 @@ +pvdeg.geospatial.auto\_template +=============================== + +.. currentmodule:: pvdeg.geospatial + +.. autofunction:: auto_template \ No newline at end of file diff --git a/docs/source/_autosummary/pvdeg.geospatial.calc_block.rst b/docs/source/_autosummary/pvdeg.geospatial.calc_block.rst new file mode 100644 index 0000000..c78b81d --- /dev/null +++ b/docs/source/_autosummary/pvdeg.geospatial.calc_block.rst @@ -0,0 +1,6 @@ +pvdeg.geospatial.calc\_block +============================ + +.. currentmodule:: pvdeg.geospatial + +.. autofunction:: calc_block \ No newline at end of file diff --git a/docs/source/_autosummary/pvdeg.geospatial.calc_gid.rst b/docs/source/_autosummary/pvdeg.geospatial.calc_gid.rst new file mode 100644 index 0000000..4398fd1 --- /dev/null +++ b/docs/source/_autosummary/pvdeg.geospatial.calc_gid.rst @@ -0,0 +1,6 @@ +pvdeg.geospatial.calc\_gid +========================== + +.. currentmodule:: pvdeg.geospatial + +.. autofunction:: calc_gid \ No newline at end of file diff --git a/docs/source/_autosummary/pvdeg.geospatial.output_template.rst b/docs/source/_autosummary/pvdeg.geospatial.output_template.rst new file mode 100644 index 0000000..6d49f0b --- /dev/null +++ b/docs/source/_autosummary/pvdeg.geospatial.output_template.rst @@ -0,0 +1,6 @@ +pvdeg.geospatial.output\_template +================================= + +.. currentmodule:: pvdeg.geospatial + +.. autofunction:: output_template \ No newline at end of file diff --git a/docs/source/_autosummary/pvdeg.geospatial.plot_Europe.rst b/docs/source/_autosummary/pvdeg.geospatial.plot_Europe.rst new file mode 100644 index 0000000..f6b3cb4 --- /dev/null +++ b/docs/source/_autosummary/pvdeg.geospatial.plot_Europe.rst @@ -0,0 +1,6 @@ +pvdeg.geospatial.plot\_Europe +============================= + +.. currentmodule:: pvdeg.geospatial + +.. autofunction:: plot_Europe \ No newline at end of file diff --git a/docs/source/_autosummary/pvdeg.geospatial.plot_USA.rst b/docs/source/_autosummary/pvdeg.geospatial.plot_USA.rst new file mode 100644 index 0000000..b1b86a6 --- /dev/null +++ b/docs/source/_autosummary/pvdeg.geospatial.plot_USA.rst @@ -0,0 +1,6 @@ +pvdeg.geospatial.plot\_USA +========================== + +.. currentmodule:: pvdeg.geospatial + +.. autofunction:: plot_USA \ No newline at end of file diff --git a/docs/source/_autosummary/pvdeg.geospatial.rst b/docs/source/_autosummary/pvdeg.geospatial.rst new file mode 100644 index 0000000..c50d18e --- /dev/null +++ b/docs/source/_autosummary/pvdeg.geospatial.rst @@ -0,0 +1,125 @@ +.. Please when editing this file make sure to keep it matching the + docs in ../configuration.rst:reference_to_examples + +pvdeg.geospatial +================ + +.. automodule:: pvdeg.geospatial + + .. this is crazy + + + + + Function Overview + ----------------- + + .. autosummary:: + :toctree: + :nosignatures: + + + pvdeg.geospatial.analysis + pvdeg.geospatial.auto_template + pvdeg.geospatial.calc_block + pvdeg.geospatial.calc_gid + pvdeg.geospatial.output_template + pvdeg.geospatial.plot_Europe + pvdeg.geospatial.plot_USA + pvdeg.geospatial.start_dask + pvdeg.geospatial.template_parameters + pvdeg.geospatial.zero_template + + + + + .. this is crazy + + + + +.. + Functions + --------- + + + + .. autofunction:: analysis + + .. _sphx_glr_backref_pvdeg.geospatial.analysis: + + .. minigallery:: pvdeg.geospatial.analysis + :add-heading: + + .. autofunction:: auto_template + + .. _sphx_glr_backref_pvdeg.geospatial.auto_template: + + .. minigallery:: pvdeg.geospatial.auto_template + :add-heading: + + .. autofunction:: calc_block + + .. _sphx_glr_backref_pvdeg.geospatial.calc_block: + + .. minigallery:: pvdeg.geospatial.calc_block + :add-heading: + + .. autofunction:: calc_gid + + .. _sphx_glr_backref_pvdeg.geospatial.calc_gid: + + .. minigallery:: pvdeg.geospatial.calc_gid + :add-heading: + + .. autofunction:: output_template + + .. _sphx_glr_backref_pvdeg.geospatial.output_template: + + .. minigallery:: pvdeg.geospatial.output_template + :add-heading: + + .. autofunction:: plot_Europe + + .. _sphx_glr_backref_pvdeg.geospatial.plot_Europe: + + .. minigallery:: pvdeg.geospatial.plot_Europe + :add-heading: + + .. autofunction:: plot_USA + + .. _sphx_glr_backref_pvdeg.geospatial.plot_USA: + + .. minigallery:: pvdeg.geospatial.plot_USA + :add-heading: + + .. autofunction:: start_dask + + .. _sphx_glr_backref_pvdeg.geospatial.start_dask: + + .. minigallery:: pvdeg.geospatial.start_dask + :add-heading: + + .. autofunction:: template_parameters + + .. _sphx_glr_backref_pvdeg.geospatial.template_parameters: + + .. minigallery:: pvdeg.geospatial.template_parameters + :add-heading: + + .. autofunction:: zero_template + + .. _sphx_glr_backref_pvdeg.geospatial.zero_template: + + .. minigallery:: pvdeg.geospatial.zero_template + :add-heading: + + + + + + + + + + \ No newline at end of file diff --git a/docs/source/_autosummary/pvdeg.geospatial.start_dask.rst b/docs/source/_autosummary/pvdeg.geospatial.start_dask.rst new file mode 100644 index 0000000..6f7684f --- /dev/null +++ b/docs/source/_autosummary/pvdeg.geospatial.start_dask.rst @@ -0,0 +1,6 @@ +pvdeg.geospatial.start\_dask +============================ + +.. currentmodule:: pvdeg.geospatial + +.. autofunction:: start_dask \ No newline at end of file diff --git a/docs/source/_autosummary/pvdeg.geospatial.template_parameters.rst b/docs/source/_autosummary/pvdeg.geospatial.template_parameters.rst new file mode 100644 index 0000000..dbc2d63 --- /dev/null +++ b/docs/source/_autosummary/pvdeg.geospatial.template_parameters.rst @@ -0,0 +1,6 @@ +pvdeg.geospatial.template\_parameters +===================================== + +.. currentmodule:: pvdeg.geospatial + +.. autofunction:: template_parameters \ No newline at end of file diff --git a/docs/source/_autosummary/pvdeg.geospatial.zero_template.rst b/docs/source/_autosummary/pvdeg.geospatial.zero_template.rst new file mode 100644 index 0000000..ecae12d --- /dev/null +++ b/docs/source/_autosummary/pvdeg.geospatial.zero_template.rst @@ -0,0 +1,6 @@ +pvdeg.geospatial.zero\_template +=============================== + +.. currentmodule:: pvdeg.geospatial + +.. autofunction:: zero_template \ No newline at end of file diff --git a/docs/source/api.rst b/docs/source/api.rst index c59c6b0..5520f75 100644 --- a/docs/source/api.rst +++ b/docs/source/api.rst @@ -22,6 +22,7 @@ Modules, methods, classes and attributes are explained here. humidity letid montecarlo + geospatial spectral standards temperature diff --git a/docs/source/user_guide/geospatial-templates.rst b/docs/source/user_guide/geospatial-templates.rst new file mode 100644 index 0000000..d45044d --- /dev/null +++ b/docs/source/user_guide/geospatial-templates.rst @@ -0,0 +1,40 @@ +.. _geospatial-templates: + +Geospatial Analysis +=================== +Using 3 dimensional labeled arrays (`Xarray`) we are able to run calculations using meteorological data across many points at once. This process has been parallelized using `dask` and `xarray`. Both of these packages can be run locally or on cloud HPC environments. + +This presents a new issue, our models produce outputs in many different shapes and sizes. We can have single numerical results, multiple numeric results or a timeseries of numeric results at each location. To parallelize this process, we cannot wait until runtime to know what shape to store the outputs in. This is where the need for `templates` arises. + +Previously, ``pvdeg.geospatial`` provided minimal templates and forced users to create their own for each function they wanted to use in a geospatial calculation. + +Auto-templating: allows users to skip creating templates for most functions within pvdeg by using ``pvdeg.geospatial.autotemplate`` to generate templates on the spot, instead of figuring out the output shape. For any given function within the source code decorated with `geospatial_result_type`, we can use `pvdeg.geospatial.autotemplate` + + +Example +-------- + +Here we are providing a function to autotemplate along with an ``Xarray.Dataset`` of weather data. Combining these two will give us enough information to produce an output template. + +Autotemplate approach to creating a template + +.. code-block:: Python + + edge_seal_template = pvdeg.geospatial.auto_template( + func=pvdeg.design.edge_seal_width, + ds_gids=geo_weather + ) + +Manual Approach to Creating the Sample Template + +.. code-block:: Python + + shapes = { + "width" : ("gid",) # one return value at each datapoint, only dependent on datapoint, not time + } + + template = pvdeg.geospatial.output_template( + ds_gids=geo_weather, # xarray dataset + shapes=shapes, # output shapes + ) + diff --git a/docs/source/user_guide/index.rst b/docs/source/user_guide/index.rst index 3f89f77..d4a4357 100644 --- a/docs/source/user_guide/index.rst +++ b/docs/source/user_guide/index.rst @@ -10,6 +10,7 @@ User Guide package_overview NSRDB_API_Key montecarlo + geospatial-templates pv-variables-terms contributing diff --git a/docs/source/user_guide/montecarlo.rst b/docs/source/user_guide/montecarlo.rst index cb116f6..a2fce79 100644 --- a/docs/source/user_guide/montecarlo.rst +++ b/docs/source/user_guide/montecarlo.rst @@ -21,12 +21,12 @@ If your variables are correlated form a list of correlations using pvdeg.monteca Defining Mean and Standard Deviation ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Each variable passed to a Corr object in string form must have its own dictionary entry containing its mean and standard deviation in the following form. -``` -my_dict = { - : {'mean' : , 'stdev' : } -} -``` -*why are there extra backticks this is upsetting* + +.. code-block:: Python + + my_dict = { + : {'mean' : , 'stdev' : } + } Generating Correlated Samples ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -39,13 +39,13 @@ Generating Uncorrelated Samples ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ To create uncorrelated samples the ``corr`` parameter of ``pvdeg.montecarlo.generateCorrelatedSamples()`` provided with an empty list, while still providing the other arguments. like the following. -``` +.. code-block:: Python + pvdeg.montecarlo.generateCorrelatedSamples( corr = [], ... ... ) -``` 3rd Party Samples/Data ^^^^^^^^^^^^^^^^^^^^^^ diff --git a/docs/source/whatsnew/index.rst b/docs/source/whatsnew/index.rst index b687b11..37fe565 100644 --- a/docs/source/whatsnew/index.rst +++ b/docs/source/whatsnew/index.rst @@ -4,6 +4,8 @@ What's New ========== PVDegradationTools (pvdeg) change log: + +.. include:: releases/v0.4.0.rst .. include:: releases/v0.3.5.rst .. include:: releases/v0.3.4.rst .. include:: releases/v0.3.3.rst diff --git a/docs/source/whatsnew/releases/v0.3.5.rst b/docs/source/whatsnew/releases/v0.3.5.rst index e0a0a61..f307ec0 100644 --- a/docs/source/whatsnew/releases/v0.3.5.rst +++ b/docs/source/whatsnew/releases/v0.3.5.rst @@ -7,5 +7,5 @@ Enhancements Contributors ~~~~~~~~~~~~ -* Martin Springer (:ghuser:`tobin-ford`) +* Tobin Ford (:ghuser:`tobin-ford`) diff --git a/docs/source/whatsnew/releases/v0.4.0.rst b/docs/source/whatsnew/releases/v0.4.0.rst new file mode 100644 index 0000000..fc0df30 --- /dev/null +++ b/docs/source/whatsnew/releases/v0.4.0.rst @@ -0,0 +1,21 @@ +v0.4.0 (2024-07-29) +======================= + +Enhancements +--------- +* Autotemplating system for geospatial analysis using `pvdeg.geospatial.autotemplate` +* New module `pvdeg.decorators` that contains `pvdeg` specific decorator functions. +* Implemented `geospatial_result_type` decorator to update functions and preform runtime introspection to determine if a function is autotemplate-able. +* `Geospatial Templates.ipynb` notebook to showcase new and old templating functionality for users. + +Bug Fixes +--------- +* Added type hinting to many `pvdeg` functions +* Replaced deprecated numba `jit(nopython=True)` calls with `njit` +* Fixed whatsnew `v0.3.5` author + +Contributors +~~~~~~~~~~~~ +* Tobin Ford (:ghuser:`tobin-ford`) + + diff --git a/pvdeg/decorators.py b/pvdeg/decorators.py index 6271bec..dabe1fb 100644 --- a/pvdeg/decorators.py +++ b/pvdeg/decorators.py @@ -3,14 +3,15 @@ Private API, should only be used in PVDeg implemenation files. """ + def geospatial_result_type(value: bool, shape_names: list[str]) -> None: """ - Add an attribute to the functions that can be run with geospatial analysis. - Strict typing is not enough for this purpose so we can view this attribute - at runtime to create a template for the function. + Add an attribute to the functions that can be run with geospatial analysis. + Strict typing is not enough for this purpose so we can view this attribute + at runtime to create a template for the function. - For single numeric results, includes tabular numeric data - >>> value = False (0) + For single numeric results, includes tabular numeric data + >>> value = False (0) Example if a function returns a dataframe with 1 row of numerics (not timeseries) `pvdeg.standards.standoff` does this. @@ -22,17 +23,21 @@ def geospatial_result_type(value: bool, shape_names: list[str]) -> None: For both numeric and timeseries results, we care about the output names of the funtion. When a function returns a dataframe, the names will simply be the dataframe column names. - >>> return df # function returns dataframe + >>> return df # function returns dataframe >>> df.columns = ["rh", "dry_bulb", "irradiance"] # dataframe column names >>> func.shape_names = ["rh", "dry_bulb", "irradiance"] # function attribute names When a function returns a numeric, or tuple of numerics, the names will correspond to the meanings of each unpacked variable. >>> return (T98, x_eff) # function return tuple of numerics >>> func.shape_names = ["T98", "x_eff"] # function attribute names - + + * Note: we cannot autotemplate functions with ambiguous return types that depend on runtime input, + the function will need strictly return a timeseries or numeric but not one or the other. """ + def decorator(func): setattr(func, "numeric_or_timeseries", value) setattr(func, "shape_names", shape_names) return func - return decorator \ No newline at end of file + + return decorator diff --git a/pvdeg/degradation.py b/pvdeg/degradation.py index 2660f23..1b521a9 100644 --- a/pvdeg/degradation.py +++ b/pvdeg/degradation.py @@ -1,5 +1,4 @@ -"""Collection of functions for degradation calculations. -""" +"""Collection of functions for degradation calculations.""" import numpy as np import pandas as pd @@ -8,11 +7,14 @@ from rex import Outputs from pathlib import Path from concurrent.futures import ProcessPoolExecutor, as_completed +from typing import Union from . import temperature from . import spectral from . import weather +from pvdeg.decorators import geospatial_result_type + # TODO: Clean up all those functions and add gaps functionality @@ -97,7 +99,7 @@ def _acceleration_factor(numerator, denominator): def vantHoff_deg( - weather_df, meta, I_chamber, temp_chamber, poa=None, temp=None, p=0.5, Tf=1.41 + weather_df: pd.DataFrame, meta: dict, I_chamber, temp_chamber, poa=None, temp=None, p=0.5, Tf=1.41 ): """ Van't Hoff Irradiance Degradation @@ -177,7 +179,16 @@ def _to_eq_vantHoff(temp, Tf=1.41): return Toeq -def IwaVantHoff(weather_df, meta, poa=None, temp=None, Teq=None, p=0.5, Tf=1.41): +@geospatial_result_type(0,["Iwa"]) +def IwaVantHoff( + weather_df: pd.DataFrame, + meta: dict, + poa: Union[pd.Series, pd.DataFrame] = None, + temp=None, + Teq=None, + p=0.5, + Tf=1.41, +): """ IWa : Environment Characterization [W/m²] For one year of degredation the controlled environmnet lamp settings will @@ -299,8 +310,8 @@ def _arrhenius_numerator(I_chamber, rh_chamber, temp_chamber, Ea, p, n): def arrhenius_deg( - weather_df, - meta, + weather_df: pd.DataFrame, + meta: dict, rh_outdoor, I_chamber, rh_chamber, @@ -463,17 +474,17 @@ def _RH_wa_arrhenius(rh_outdoor, temp, Ea, Teq=None, n=1): # TODO: CHECK # STANDARDIZE def IwaArrhenius( - weather_df, - meta, - rh_outdoor, - Ea, - poa=None, - temp=None, - RHwa=None, - Teq=None, - p=0.5, - n=1, -): + weather_df: pd.DataFrame, + meta: dict, + rh_outdoor: pd.Series, + Ea: float, + poa: pd.DataFrame = None, + temp: pd.Series = None, + RHwa: float = None, + Teq: float = None, + p: float = 0.5, + n: float = 1, +) -> float: """ Function to calculate IWa, the Environment Characterization [W/m²]. For one year of degredation the controlled environmnet lamp settings will @@ -643,8 +654,16 @@ def _gJtoMJ(gJ): def degradation( - spectra, rh_module, temp_module, wavelengths, Ea=40.0, n=1.0, p=0.5, C2=0.07, C=1.0 -): + spectra: pd.Series, + rh_module: pd.Series, + temp_module: pd.Series, + wavelengths: Union[int, np.ndarray[float]], + Ea: float = 40.0, + n: float = 1.0, + p: float = 0.5, + C2: float = 0.07, + C: float = 1.0, +) -> float: """ Compute degredation as double integral of Arrhenius (Activation Energy, RH, Temperature) and spectral (wavelength, irradiance) diff --git a/pvdeg/design.py b/pvdeg/design.py index 893d32a..b23730d 100644 --- a/pvdeg/design.py +++ b/pvdeg/design.py @@ -1,7 +1,8 @@ -"""Collection of functions for PV module design considertations. -""" +"""Collection of functions for PV module design considertations.""" from . import humidity +from pvdeg.decorators import geospatial_result_type +import pandas as pd def edge_seal_ingress_rate(avg_psat): @@ -41,7 +42,14 @@ def edge_seal_ingress_rate(avg_psat): return k -def edge_seal_width(weather_df, meta, k=None, years=25, from_dew_point=False): +@geospatial_result_type(0, ["width"]) +def edge_seal_width( + weather_df: pd.DataFrame, + meta: dict, + k: float = None, + years: int = 25, + from_dew_point: bool = False, +): """ Determine the width of edge seal required for given number of years water ingress. diff --git a/pvdeg/geospatial.py b/pvdeg/geospatial.py index 7a8efbb..9cd3dbc 100644 --- a/pvdeg/geospatial.py +++ b/pvdeg/geospatial.py @@ -82,16 +82,28 @@ def start_dask(hpc=None): return client + def _df_from_arbitrary(res, func): + numerics = (int, float, np.number) + arrays = (np.ndarray, pd.Series) + if isinstance(res, pd.DataFrame): return res elif isinstance(res, pd.Series): return pd.DataFrame(res, columns=[func.__name__]) elif isinstance(res, (int, float)): return pd.DataFrame([res], columns=[func.__name__]) - + elif isinstance(res, tuple) and all(isinstance(item, numerics) for item in res): + return pd.DataFrame([res]) + elif isinstance(res, tuple) and all(isinstance(item, arrays) for item in res): + return pd.concat( + res, axis=1 + ) # they must all be the same length here or this will error out else: - raise NotImplementedError(f"function return type: {type(res)} not available for geospatial analysis yet.") + raise NotImplementedError( + f"function return type: {type(res)} not available for geospatial analysis yet." + ) + def calc_gid(ds_gid, meta_gid, func, **kwargs): """ @@ -116,7 +128,7 @@ def calc_gid(ds_gid, meta_gid, func, **kwargs): df_weather = ds_gid.to_dataframe() res = func(weather_df=df_weather, meta=meta_gid, **kwargs) - df_res = _df_from_arbitrary(res, func) # convert all return types to dataframe + df_res = _df_from_arbitrary(res, func) # convert all return types to dataframe ds_res = xr.Dataset.from_dataframe(df_res) if not df_res.index.name: @@ -244,11 +256,12 @@ def output_template( }, coords={dim: ds_gids[dim] for dim in dims}, attrs=global_attrs, - )#.chunk({dim: ds_gids.chunks[dim] for dim in dims}) + ) # .chunk({dim: ds_gids.chunks[dim] for dim in dims}) return output_template +# we should be able to get rid of this with the new autotemplating function and decorator def template_parameters(func): """ Output parameters for xarray template. @@ -367,23 +380,44 @@ def zero_template( return res -def auto_template(func: Callable, ds_gids: xr.Dataset)->xr.Dataset: +def auto_template(func: Callable, ds_gids: xr.Dataset) -> xr.Dataset: + """ + Automatically create a template for a target function: `func`. + Only works on functions that have a strict return type. + Otherwise you will have to create your own template. + Don't worry, this is easy. See the Geospatial Templates Notebook + for more information. + + examples: + the function returns a numeric value + >>> pvdeg.design.edge_seal_width + + the function returns a timeseries result + >>> pvdeg.module.humidity + + counter example: + the function could either return a single numeric or a series based on changed in the input + + Note: Only works on functions that use the `geospatial_result_type` decorator in the source code. + """ if not (hasattr(func, "numeric_or_timeseries") and hasattr(func, "shape_names")): - raise ValueError(f"{func.__name__} cannot be autotemplated. create a template manually") + raise ValueError( + f"{func.__name__} cannot be autotemplated. create a template manually" + ) if func.numeric_or_timeseries == 0: shapes = {datavar: ("gid",) for datavar in func.shape_names} elif func.numeric_or_timeseries == 1: - shapes = {datavar: ("gid","time") for datavar in func.shape_names} + shapes = {datavar: ("gid", "time") for datavar in func.shape_names} - template = output_template( # zeros_template? - ds_gids=ds_gids, - shapes=shapes + template = output_template( # zeros_template? + ds_gids=ds_gids, shapes=shapes ) return template + def plot_USA( xr_res, cmap="viridis", vmin=None, vmax=None, title=None, cb_title=None, fp=None ): diff --git a/pvdeg/humidity.py b/pvdeg/humidity.py index bf421bc..28c81d2 100644 --- a/pvdeg/humidity.py +++ b/pvdeg/humidity.py @@ -4,15 +4,19 @@ import numpy as np import pandas as pd import pvlib -from numba import jit +from numba import njit from rex import NSRDBX from rex import Outputs from pathlib import Path from concurrent.futures import ProcessPoolExecutor, as_completed -from . import temperature -from . import spectral -from . import weather +from . import ( + temperature, + spectral, + weather +) + +from pvdeg.decorators import geospatial_result_type def _ambient(weather_df): @@ -52,7 +56,7 @@ def _ambient(weather_df): # TODO: When is dew_yield used? -@jit(nopython=True, error_model="python") +@njit def dew_yield(elevation, dew_point, dry_bulb, wind_speed, n): """ Estimates the dew yield in [mm/day]. Calculation taken from: @@ -389,7 +393,7 @@ def _ceq(Csat, rh_SurfaceOutside): return Ceq -@jit(nopython=True) +@njit def Ce_numba( start, temp_module, @@ -650,6 +654,7 @@ def backsheet( return backsheet +@geospatial_result_type(1, ["RH_surface_outside", "RH_front_encap", "RH_back_encap", "RH_backsheet"]) def module( weather_df, meta, diff --git a/pvdeg/letid.py b/pvdeg/letid.py index 9b80573..0feb2d8 100644 --- a/pvdeg/letid.py +++ b/pvdeg/letid.py @@ -11,6 +11,7 @@ from pvdeg import collection, utilities, standards, DATA_DIR +from pvdeg.decorators import geospatial_result_type def tau_now(tau_0, tau_deg, n_b): @@ -870,6 +871,7 @@ def calc_injection_outdoors(results): return injection +@geospatial_result_type(1, ["Temperature", "Injection", "NA", "NB", "NC", "tau", "Jsc", "Voc", "Isc", "FF", "Pmp", "Pmp_norm"]) def calc_letid_outdoors( tau_0, tau_deg, diff --git a/pvdeg/spectral.py b/pvdeg/spectral.py index 53b73ea..4aecedb 100644 --- a/pvdeg/spectral.py +++ b/pvdeg/spectral.py @@ -3,9 +3,22 @@ """ import pvlib - - -def solar_position(weather_df, meta): +import pandas as pd +from pvdeg.decorators import geospatial_result_type + + +@geospatial_result_type( + 1, + [ + "apparent_zenith", + "zenith", + "apparent_elevation", + "elevation", + "azimuth", + "equation_of_time", + ], +) +def solar_position(weather_df: pd.DataFrame, meta: dict) -> pd.DataFrame: """ Calculate solar position using pvlib based on weather data from the National Solar Radiation Database (NSRDB) for a given location (gid). @@ -14,7 +27,7 @@ def solar_position(weather_df, meta): ---------- weather_df : pandas.DataFrame Weather data for given location. - meta : pandas.Series + meta : dict Meta data of location. Returns @@ -41,9 +54,24 @@ def solar_position(weather_df, meta): return solar_position +@geospatial_result_type( + 1, + [ + "poa_global", + "poa_direct", + "poa_diffuse", + "poa_sky_diffuse", + "poa_ground_diffuse", + ], +) def poa_irradiance( - weather_df, meta, sol_position=None, tilt=None, azimuth=None, sky_model="isotropic" -): + weather_df: pd.DataFrame, + meta: dict, + sol_position=None, + tilt=None, + azimuth=None, + sky_model="isotropic", +) -> pd.DataFrame: """ Calculate plane-of-array (POA) irradiance using pvlib based on weather data from the National Solar Radiation Database (NSRDB) for a given location (gid). diff --git a/pvdeg/standards.py b/pvdeg/standards.py index cad602f..192a195 100644 --- a/pvdeg/standards.py +++ b/pvdeg/standards.py @@ -19,6 +19,7 @@ from pvdeg.decorators import geospatial_result_type +@geospatial_result_type(1, ["T_0", "T_inf", "poa"]) def eff_gap_parameters( weather_df=None, meta=None, diff --git a/pvdeg/temperature.py b/pvdeg/temperature.py index e4968c3..732cedb 100644 --- a/pvdeg/temperature.py +++ b/pvdeg/temperature.py @@ -4,9 +4,12 @@ import pvlib import pvdeg +from pvdeg.decorators import geospatial_result_type import pandas as pd from typing import Union + +@geospatial_result_type(1, ['module_temperature']) def module( weather_df, meta, @@ -97,6 +100,7 @@ def module( return module_temperature +@geospatial_result_type(1, ["cell_temperature"]) def cell( weather_df: pd.DataFrame, meta: dict, diff --git a/tutorials_and_tools/tutorials_and_tools/Geospatial Templates.ipynb b/tutorials_and_tools/tutorials_and_tools/Geospatial Templates.ipynb index d39f3dc..3423e93 100644 --- a/tutorials_and_tools/tutorials_and_tools/Geospatial Templates.ipynb +++ b/tutorials_and_tools/tutorials_and_tools/Geospatial Templates.ipynb @@ -45,7 +45,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 2, "metadata": {}, "outputs": [ { @@ -429,49 +429,49 @@ "Attributes:\n", " full_version_record: {"rex": "0.2.80", "pandas": "2.0.0", "numpy": "1.23...\n", " package: rex\n", - " version: 4.0.0
  • full_version_record :
    {"rex": "0.2.80", "pandas": "2.0.0", "numpy": "1.23.5", "python": "3.9.16 (main, Mar 8 2023, 14:00:05) \\n[GCC 11.2.0]", "click": "8.1.3", "h5py": "3.7.0", "h5pyd": "0.14.0", "scipy": "1.10.1"}
    package :
    rex
    version :
    4.0.0
  • " ], "text/plain": [ " Size: 9MB\n", @@ -503,7 +503,7 @@ " version: 4.0.0" ] }, - "execution_count": 3, + "execution_count": 2, "metadata": {}, "output_type": "execute_result" } @@ -532,7 +532,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Examples\n", + "## Examples\n", "\n", "## 98ᵗʰ module percential temperature at Standoff Height\n", "\n", @@ -548,7 +548,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -651,42 +651,213 @@ "source": [ "# Creating Templates Programatically\n", "\n", - "We can use the new function below to generate a template for a given function. This can return a bad result which will fail or work improperly when running `pvdeg.geospatial.analysis` with the generated template. Results should be scrutinized to make sure they are the right format." + "We can use `pvdeg.geospatial.autotemplate` to generate a template for a given function. This can return a bad result which will fail or work improperly when running `pvdeg.geospatial.analysis` with the generated template. Results should be scrutinized to make sure they are the right format." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Examples Below\n", + "Steps\n", + "- Create template using autotemplating function. Pulls in information about function to determine shape of output. Not usable on functions with ambigious return types.\n", + "- Call geospatial analysis function using template" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Geospatial Cell Temperature Calculation" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "cell_temp_template = pvdeg.geospatial.auto_template(\n", + " func=pvdeg.temperature.cell,\n", + " ds_gids=geo_weather\n", + ")\n", + "\n", + "pvdeg.geospatial.analysis(\n", + " weather_ds=geo_weather,\n", + " meta_df=geo_meta,\n", + " func=pvdeg.temperature.cell,\n", + " template=cell_temp_template\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Geospatial Module Temperature Calculation" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "module_temp_template = pvdeg.geospatial.auto_template(\n", + " func=pvdeg.temperature.module,\n", + " ds_gids=geo_weather\n", + ")\n", + "\n", + "pvdeg.geospatial.analysis(\n", + " weather_ds=geo_weather,\n", + " meta_df=geo_meta,\n", + " func=pvdeg.temperature.module,\n", + " template=module_temp_template\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Geospatial Solar Position Calculation" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "solar_position_template = pvdeg.geospatial.auto_template(\n", + " func=pvdeg.spectral.solar_position,\n", + " ds_gids=geo_weather\n", + ")\n", + "\n", + "pvdeg.geospatial.analysis(\n", + " weather_ds=geo_weather,\n", + " meta_df=geo_meta,\n", + " func=pvdeg.spectral.solar_position,\n", + " template=solar_position_template\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Geospatial POA Irradiance Calculation" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "poa_irradiance_template = pvdeg.geospatial.auto_template(\n", + " func=pvdeg.spectral.poa_irradiance,\n", + " ds_gids=geo_weather\n", + ")\n", + "\n", + "pvdeg.geospatial.analysis(\n", + " weather_ds=geo_weather,\n", + " meta_df=geo_meta,\n", + " func=pvdeg.spectral.poa_irradiance,\n", + " template=poa_irradiance_template\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Geospatial 98th Percentile Operating Temperature Calculation" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "standoff_template = pvdeg.geospatial.auto_template(\n", + " func=pvdeg.standards.T98_estimate, \n", + " ds_gids=geo_weather\n", + " )\n", + "\n", + "pvdeg.geospatial.analysis(\n", + " weather_ds=geo_weather,\n", + " meta_df=geo_meta,\n", + " func=pvdeg.standards.T98_estimate,\n", + " template=standoff_template\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Geospatial Module Humidity Calculation" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "humidity_template = pvdeg.geospatial.auto_template(\n", + " func=pvdeg.humidity.module,\n", + " ds_gids=geo_weather\n", + ")\n", + "\n", + "pvdeg.geospatial.analysis(\n", + " weather_ds=geo_weather,\n", + " meta_df=geo_meta,\n", + " func=pvdeg.humidity.module,\n", + " template=humidity_template\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Geospatial IwaVantHoff Environment Characterization Calculation" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "iwa_vant_hoff_template = pvdeg.geospatial.auto_template(\n", + " func=pvdeg.degradation.IwaVantHoff,\n", + " ds_gids=geo_weather\n", + ")\n", + "\n", + "pvdeg.geospatial.analysis(\n", + " weather_ds=geo_weather,\n", + " meta_df=geo_meta,\n", + " func=pvdeg.degradation.IwaVantHoff,\n", + " template=iwa_vant_hoff_template\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Geospatial Edge Seal Width Calculation" ] }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 7, "metadata": {}, "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "The array tilt angle was not provided, therefore the latitude tilt of 39.9 was used.\n", - "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", - "The array tilt angle was not provided, therefore the latitude tilt of 39.9 was used.\n", - "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", - "The array tilt angle was not provided, therefore the latitude tilt of 39.7 was used.\n", - "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", - "The array tilt angle was not provided, therefore the latitude tilt of 39.8 was used.\n", - "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", - "The array tilt angle was not provided, therefore the latitude tilt of 39.8 was used.\n", - "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", - "The array tilt angle was not provided, therefore the latitude tilt of 39.4 was used.\n", - "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", - "The array tilt angle was not provided, therefore the latitude tilt of 39.5 was used.\n", - "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", - "The array tilt angle was not provided, therefore the latitude tilt of 39.4 was used.\n", - "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", - "The array tilt angle was not provided, therefore the latitude tilt of 39.6 was used.\n", - "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", - "The array tilt angle was not provided, therefore the latitude tilt of 39.5 was used.\n", - "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", - "The array tilt angle was not provided, therefore the latitude tilt of 39.6 was used.\n", - "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n" - ] - }, { "data": { "text/html": [ @@ -1054,57 +1225,514 @@ " fill: currentColor;\n", "}\n", "
    <xarray.Dataset> Size: 784B\n",
    -       "Dimensions:       (latitude: 8, longitude: 10)\n",
    +       "Dimensions:          (latitude: 8, longitude: 10)\n",
            "Coordinates:\n",
    -       "  * latitude      (latitude) float64 64B 39.41 39.45 39.53 ... 39.69 39.81 39.89\n",
    -       "  * longitude     (longitude) float64 80B -106.4 -106.3 -106.3 ... -105.9 -105.9\n",
    +       "  * latitude         (latitude) float64 64B 39.41 39.45 39.53 ... 39.81 39.89\n",
    +       "  * longitude        (longitude) float64 80B -106.4 -106.3 ... -105.9 -105.9\n",
            "Data variables:\n",
    -       "    T98_estimate  (latitude, longitude) float64 640B nan nan nan ... nan nan nan
  • " ], "text/plain": [ " Size: 784B\n", - "Dimensions: (latitude: 8, longitude: 10)\n", + "Dimensions: (latitude: 8, longitude: 10)\n", "Coordinates:\n", - " * latitude (latitude) float64 64B 39.41 39.45 39.53 ... 39.69 39.81 39.89\n", - " * longitude (longitude) float64 80B -106.4 -106.3 -106.3 ... -105.9 -105.9\n", + " * latitude (latitude) float64 64B 39.41 39.45 39.53 ... 39.81 39.89\n", + " * longitude (longitude) float64 80B -106.4 -106.3 ... -105.9 -105.9\n", "Data variables:\n", - " T98_estimate (latitude, longitude) float64 640B nan nan nan ... nan nan nan" + " edge_seal_width (latitude, longitude) float64 640B nan nan nan ... nan nan" ] }, - "execution_count": 5, + "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "standoff_template = pvdeg.geospatial.auto_template(pvdeg.standards.T98_estimate, ds_gids=geo_weather)\n", + "edge_seal_template = pvdeg.geospatial.auto_template(\n", + " func=pvdeg.design.edge_seal_width,\n", + " ds_gids=geo_weather\n", + ")\n", "\n", "pvdeg.geospatial.analysis(\n", " weather_ds=geo_weather,\n", " meta_df=geo_meta,\n", - " func=pvdeg.standards.T98_estimate,\n", - " template=standoff_template\n", + " func=pvdeg.design.edge_seal_width,\n", + " template=edge_seal_template\n", ")" ] }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
    \n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
    <xarray.Dataset> Size: 176B\n",
    +       "Dimensions:  (gid: 11)\n",
    +       "Coordinates:\n",
    +       "  * gid      (gid) int64 88B 449211 452064 453020 ... 459670 460613 462498\n",
    +       "Data variables:\n",
    +       "    width    (gid) float64 88B dask.array<chunksize=(11,), meta=np.ndarray>
    " + ], + "text/plain": [ + " Size: 176B\n", + "Dimensions: (gid: 11)\n", + "Coordinates:\n", + " * gid (gid) int64 88B 449211 452064 453020 ... 459670 460613 462498\n", + "Data variables:\n", + " width (gid) float64 88B dask.array" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "edge_seal_template" + ] + }, { "cell_type": "code", "execution_count": null, From 0dc50898f268846aa4caf44e15af7bddb3abcc1b Mon Sep 17 00:00:00 2001 From: tobin-ford Date: Thu, 1 Aug 2024 14:53:52 -0600 Subject: [PATCH 9/9] Rename decorator, update docstrings --- pvdeg/decorators.py | 20 +- pvdeg/degradation.py | 4 +- pvdeg/design.py | 4 +- pvdeg/fatigue.py | 4 +- pvdeg/geospatial.py | 14 +- pvdeg/humidity.py | 4 +- pvdeg/letid.py | 4 +- pvdeg/spectral.py | 6 +- pvdeg/standards.py | 8 +- pvdeg/temperature.py | 6 +- .../Geospatial Templates.ipynb | 4182 ++++++++++++++++- 11 files changed, 4040 insertions(+), 216 deletions(-) diff --git a/pvdeg/decorators.py b/pvdeg/decorators.py index dabe1fb..b592739 100644 --- a/pvdeg/decorators.py +++ b/pvdeg/decorators.py @@ -4,7 +4,7 @@ """ -def geospatial_result_type(value: bool, shape_names: list[str]) -> None: +def geospatial_quick_shape(numeric_or_timeseries: bool, shape_names: list[str]) -> None: """ Add an attribute to the functions that can be run with geospatial analysis. Strict typing is not enough for this purpose so we can view this attribute @@ -33,10 +33,26 @@ def geospatial_result_type(value: bool, shape_names: list[str]) -> None: * Note: we cannot autotemplate functions with ambiguous return types that depend on runtime input, the function will need strictly return a timeseries or numeric but not one or the other. + + Parameters: + ----------- + numeric_or_timeseries: bool + indicate whether the function returns a single numeric/tuple of numerics + or a timeseries/tuple of timeseries. False when numeric, True when timeseries + + shape_names: list[str] + list of return value names. These will become the xarray datavariable names in the output. + + Modifies: + --------- + func.numeric_or_timeseries + sets to numeric_or_timeseries argument + func.shape_names + sets to shape_names argument """ def decorator(func): - setattr(func, "numeric_or_timeseries", value) + setattr(func, "numeric_or_timeseries", numeric_or_timeseries) setattr(func, "shape_names", shape_names) return func diff --git a/pvdeg/degradation.py b/pvdeg/degradation.py index 1b521a9..59670e5 100644 --- a/pvdeg/degradation.py +++ b/pvdeg/degradation.py @@ -13,7 +13,7 @@ from . import spectral from . import weather -from pvdeg.decorators import geospatial_result_type +from pvdeg.decorators import geospatial_quick_shape # TODO: Clean up all those functions and add gaps functionality @@ -179,7 +179,7 @@ def _to_eq_vantHoff(temp, Tf=1.41): return Toeq -@geospatial_result_type(0,["Iwa"]) +@geospatial_quick_shape(0,["Iwa"]) def IwaVantHoff( weather_df: pd.DataFrame, meta: dict, diff --git a/pvdeg/design.py b/pvdeg/design.py index b23730d..25c5b07 100644 --- a/pvdeg/design.py +++ b/pvdeg/design.py @@ -1,7 +1,7 @@ """Collection of functions for PV module design considertations.""" from . import humidity -from pvdeg.decorators import geospatial_result_type +from pvdeg.decorators import geospatial_quick_shape import pandas as pd @@ -42,7 +42,7 @@ def edge_seal_ingress_rate(avg_psat): return k -@geospatial_result_type(0, ["width"]) +@geospatial_quick_shape(0, ["width"]) def edge_seal_width( weather_df: pd.DataFrame, meta: dict, diff --git a/pvdeg/fatigue.py b/pvdeg/fatigue.py index 89fcf47..ed0f88f 100644 --- a/pvdeg/fatigue.py +++ b/pvdeg/fatigue.py @@ -2,7 +2,7 @@ import pandas as pd from scipy.constants import convert_temperature from pvdeg import temperature -from pvdeg.decorators import geospatial_result_type +from pvdeg.decorators import geospatial_quick_shape def _avg_daily_temp_change(time_range, temp_cell): @@ -98,7 +98,7 @@ def _times_over_reversal_number(temp_cell, reversal_temp): return num_changes_temp_hist -@geospatial_result_type(0, ['damage']) +@geospatial_quick_shape(0, ['damage']) def solder_fatigue( weather_df: pd.DataFrame, meta: dict, diff --git a/pvdeg/geospatial.py b/pvdeg/geospatial.py index 9cd3dbc..1286e7e 100644 --- a/pvdeg/geospatial.py +++ b/pvdeg/geospatial.py @@ -383,12 +383,16 @@ def zero_template( def auto_template(func: Callable, ds_gids: xr.Dataset) -> xr.Dataset: """ Automatically create a template for a target function: `func`. - Only works on functions that have a strict return type. + Only works on functions that have the `numeric_or_timeseries` and `shape_names` attributes. + These attributes are assigned at function definition with the `@geospatial_quick_shape` decorator. + Otherwise you will have to create your own template. Don't worry, this is easy. See the Geospatial Templates Notebook for more information. examples: + --------- + the function returns a numeric value >>> pvdeg.design.edge_seal_width @@ -396,9 +400,10 @@ def auto_template(func: Callable, ds_gids: xr.Dataset) -> xr.Dataset: >>> pvdeg.module.humidity counter example: - the function could either return a single numeric or a series based on changed in the input - - Note: Only works on functions that use the `geospatial_result_type` decorator in the source code. + ---------------- + the function could either return a single numeric or a series based on changed in + the input. Because it does not have a known result shape we cannot determine the + attributes required for autotemplating ahead of time. """ if not (hasattr(func, "numeric_or_timeseries") and hasattr(func, "shape_names")): @@ -417,7 +422,6 @@ def auto_template(func: Callable, ds_gids: xr.Dataset) -> xr.Dataset: return template - def plot_USA( xr_res, cmap="viridis", vmin=None, vmax=None, title=None, cb_title=None, fp=None ): diff --git a/pvdeg/humidity.py b/pvdeg/humidity.py index 28c81d2..2ef2690 100644 --- a/pvdeg/humidity.py +++ b/pvdeg/humidity.py @@ -16,7 +16,7 @@ weather ) -from pvdeg.decorators import geospatial_result_type +from pvdeg.decorators import geospatial_quick_shape def _ambient(weather_df): @@ -654,7 +654,7 @@ def backsheet( return backsheet -@geospatial_result_type(1, ["RH_surface_outside", "RH_front_encap", "RH_back_encap", "RH_backsheet"]) +@geospatial_quick_shape(1, ["RH_surface_outside", "RH_front_encap", "RH_back_encap", "RH_backsheet"]) def module( weather_df, meta, diff --git a/pvdeg/letid.py b/pvdeg/letid.py index 0feb2d8..2a0c311 100644 --- a/pvdeg/letid.py +++ b/pvdeg/letid.py @@ -11,7 +11,7 @@ from pvdeg import collection, utilities, standards, DATA_DIR -from pvdeg.decorators import geospatial_result_type +from pvdeg.decorators import geospatial_quick_shape def tau_now(tau_0, tau_deg, n_b): @@ -871,7 +871,7 @@ def calc_injection_outdoors(results): return injection -@geospatial_result_type(1, ["Temperature", "Injection", "NA", "NB", "NC", "tau", "Jsc", "Voc", "Isc", "FF", "Pmp", "Pmp_norm"]) +@geospatial_quick_shape(1, ["Temperature", "Injection", "NA", "NB", "NC", "tau", "Jsc", "Voc", "Isc", "FF", "Pmp", "Pmp_norm"]) def calc_letid_outdoors( tau_0, tau_deg, diff --git a/pvdeg/spectral.py b/pvdeg/spectral.py index 4aecedb..cdf273b 100644 --- a/pvdeg/spectral.py +++ b/pvdeg/spectral.py @@ -4,10 +4,10 @@ import pvlib import pandas as pd -from pvdeg.decorators import geospatial_result_type +from pvdeg.decorators import geospatial_quick_shape -@geospatial_result_type( +@geospatial_quick_shape( 1, [ "apparent_zenith", @@ -54,7 +54,7 @@ def solar_position(weather_df: pd.DataFrame, meta: dict) -> pd.DataFrame: return solar_position -@geospatial_result_type( +@geospatial_quick_shape( 1, [ "poa_global", diff --git a/pvdeg/standards.py b/pvdeg/standards.py index 192a195..ea1e755 100644 --- a/pvdeg/standards.py +++ b/pvdeg/standards.py @@ -16,10 +16,10 @@ # from gaps import ProjectPoints from pvdeg import temperature, spectral, utilities, weather -from pvdeg.decorators import geospatial_result_type +from pvdeg.decorators import geospatial_quick_shape -@geospatial_result_type(1, ["T_0", "T_inf", "poa"]) +@geospatial_quick_shape(1, ["T_0", "T_inf", "poa"]) def eff_gap_parameters( weather_df=None, meta=None, @@ -190,7 +190,7 @@ def eff_gap(T_0, T_inf, T_measured, T_ambient, poa, x_0=6.5, poa_min=400, t_amb_ return x_eff -@geospatial_result_type(0, ["x","T98_0", "T98_inf"]) # numeric result, with corresponding datavariable names +@geospatial_quick_shape(0, ["x","T98_0", "T98_inf"]) # numeric result, with corresponding datavariable names def standoff( weather_df: pd.DataFrame = None, meta: dict = None, @@ -418,7 +418,7 @@ def interpret_standoff(standoff_1=None, standoff_2=None): return Output -@geospatial_result_type(0, ["T98"]) +@geospatial_quick_shape(0, ["T98"]) def T98_estimate( weather_df=None, meta=None, diff --git a/pvdeg/temperature.py b/pvdeg/temperature.py index 732cedb..73561cf 100644 --- a/pvdeg/temperature.py +++ b/pvdeg/temperature.py @@ -4,12 +4,12 @@ import pvlib import pvdeg -from pvdeg.decorators import geospatial_result_type +from pvdeg.decorators import geospatial_quick_shape import pandas as pd from typing import Union -@geospatial_result_type(1, ['module_temperature']) +@geospatial_quick_shape(1, ['module_temperature']) def module( weather_df, meta, @@ -100,7 +100,7 @@ def module( return module_temperature -@geospatial_result_type(1, ["cell_temperature"]) +@geospatial_quick_shape(1, ["cell_temperature"]) def cell( weather_df: pd.DataFrame, meta: dict, diff --git a/tutorials_and_tools/tutorials_and_tools/Geospatial Templates.ipynb b/tutorials_and_tools/tutorials_and_tools/Geospatial Templates.ipynb index 3423e93..b9b47c0 100644 --- a/tutorials_and_tools/tutorials_and_tools/Geospatial Templates.ipynb +++ b/tutorials_and_tools/tutorials_and_tools/Geospatial Templates.ipynb @@ -429,49 +429,49 @@ "Attributes:\n", " full_version_record: {"rex": "0.2.80", "pandas": "2.0.0", "numpy": "1.23...\n", " package: rex\n", - " version: 4.0.0
  • full_version_record :
    {"rex": "0.2.80", "pandas": "2.0.0", "numpy": "1.23.5", "python": "3.9.16 (main, Mar 8 2023, 14:00:05) \\n[GCC 11.2.0]", "click": "8.1.3", "h5py": "3.7.0", "h5pyd": "0.14.0", "scipy": "1.10.1"}
    package :
    rex
    version :
    4.0.0
  • " ], "text/plain": [ " Size: 9MB\n", @@ -548,7 +548,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 3, "metadata": {}, "outputs": [], "source": [ @@ -566,9 +566,38 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 4, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The array tilt angle was not provided, therefore the latitude tilt of 39.9 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.9 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.7 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.8 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.8 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.4 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.5 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.4 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.6 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.5 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.6 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n" + ] + } + ], "source": [ "geo_estimate_temp = pvdeg.geospatial.analysis(\n", " weather_ds=geo_weather,\n", @@ -591,7 +620,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 5, "metadata": {}, "outputs": [], "source": [ @@ -609,9 +638,38 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 6, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The array tilt angle was not provided, therefore the latitude tilt of 39.9 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.9 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.7 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.8 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.8 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.4 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.5 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.4 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.6 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.5 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.6 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n" + ] + } + ], "source": [ "geo_temperature_res = pvdeg.geospatial.analysis(\n", " weather_ds=geo_weather,\n", @@ -625,9 +683,30 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 7, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "[]" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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", + "text/plain": [ + "
    " + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "# plot the temperature at ONE of the geospatial analysis result locations but we have calculated all of these.\n", "import matplotlib.pyplot as plt\n", @@ -673,165 +752,3890 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "cell_temp_template = pvdeg.geospatial.auto_template(\n", - " func=pvdeg.temperature.cell,\n", - " ds_gids=geo_weather\n", - ")\n", - "\n", - "pvdeg.geospatial.analysis(\n", - " weather_ds=geo_weather,\n", - " meta_df=geo_meta,\n", - " func=pvdeg.temperature.cell,\n", - " template=cell_temp_template\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Geospatial Module Temperature Calculation" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "module_temp_template = pvdeg.geospatial.auto_template(\n", - " func=pvdeg.temperature.module,\n", - " ds_gids=geo_weather\n", - ")\n", - "\n", - "pvdeg.geospatial.analysis(\n", - " weather_ds=geo_weather,\n", - " meta_df=geo_meta,\n", - " func=pvdeg.temperature.module,\n", - " template=module_temp_template\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Geospatial Solar Position Calculation" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "solar_position_template = pvdeg.geospatial.auto_template(\n", - " func=pvdeg.spectral.solar_position,\n", - " ds_gids=geo_weather\n", - ")\n", - "\n", - "pvdeg.geospatial.analysis(\n", - " weather_ds=geo_weather,\n", - " meta_df=geo_meta,\n", - " func=pvdeg.spectral.solar_position,\n", - " template=solar_position_template\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Geospatial POA Irradiance Calculation" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "poa_irradiance_template = pvdeg.geospatial.auto_template(\n", - " func=pvdeg.spectral.poa_irradiance,\n", - " ds_gids=geo_weather\n", - ")\n", - "\n", - "pvdeg.geospatial.analysis(\n", - " weather_ds=geo_weather,\n", - " meta_df=geo_meta,\n", - " func=pvdeg.spectral.poa_irradiance,\n", - " template=poa_irradiance_template\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Geospatial 98th Percentile Operating Temperature Calculation" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "standoff_template = pvdeg.geospatial.auto_template(\n", - " func=pvdeg.standards.T98_estimate, \n", - " ds_gids=geo_weather\n", - " )\n", - "\n", - "pvdeg.geospatial.analysis(\n", - " weather_ds=geo_weather,\n", - " meta_df=geo_meta,\n", - " func=pvdeg.standards.T98_estimate,\n", - " template=standoff_template\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Geospatial Module Humidity Calculation" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "humidity_template = pvdeg.geospatial.auto_template(\n", - " func=pvdeg.humidity.module,\n", - " ds_gids=geo_weather\n", - ")\n", - "\n", - "pvdeg.geospatial.analysis(\n", - " weather_ds=geo_weather,\n", - " meta_df=geo_meta,\n", - " func=pvdeg.humidity.module,\n", - " template=humidity_template\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Geospatial IwaVantHoff Environment Characterization Calculation" - ] - }, - { - "cell_type": "code", - "execution_count": null, + "execution_count": 8, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The array tilt angle was not provided, therefore the latitude tilt of 39.9 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.9 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.7 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.8 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.8 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.4 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.5 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.4 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.6 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.5 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.6 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n" + ] + }, + { + "data": { + "text/html": [ + "
    \n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
    <xarray.Dataset> Size: 11MB\n",
    +       "Dimensions:    (latitude: 8, longitude: 10, time: 17520)\n",
    +       "Coordinates:\n",
    +       "  * latitude   (latitude) float64 64B 39.41 39.45 39.53 ... 39.69 39.81 39.89\n",
    +       "  * longitude  (longitude) float64 80B -106.4 -106.3 -106.3 ... -105.9 -105.9\n",
    +       "  * time       (time) datetime64[ns] 140kB 2022-01-01 ... 2022-12-31T23:30:00\n",
    +       "Data variables:\n",
    +       "    cell       (time, latitude, longitude) float64 11MB nan nan nan ... nan nan
    " + ], + "text/plain": [ + " Size: 11MB\n", + "Dimensions: (latitude: 8, longitude: 10, time: 17520)\n", + "Coordinates:\n", + " * latitude (latitude) float64 64B 39.41 39.45 39.53 ... 39.69 39.81 39.89\n", + " * longitude (longitude) float64 80B -106.4 -106.3 -106.3 ... -105.9 -105.9\n", + " * time (time) datetime64[ns] 140kB 2022-01-01 ... 2022-12-31T23:30:00\n", + "Data variables:\n", + " cell (time, latitude, longitude) float64 11MB nan nan nan ... nan nan" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# create a template using auto_template for the desired function\n", + "cell_temp_template = pvdeg.geospatial.auto_template(\n", + " func=pvdeg.temperature.cell,\n", + " ds_gids=geo_weather\n", + ")\n", + "\n", + "# run the geospatial analysis with the template\n", + "pvdeg.geospatial.analysis(\n", + " weather_ds=geo_weather,\n", + " meta_df=geo_meta,\n", + " func=pvdeg.temperature.cell,\n", + " template=cell_temp_template\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Geospatial Module Temperature Calculation" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The array tilt angle was not provided, therefore the latitude tilt of 39.9 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.9 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.7 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.8 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.8 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.4 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.5 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.4 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.6 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.5 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.6 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n" + ] + }, + { + "data": { + "text/html": [ + "
    \n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
    <xarray.Dataset> Size: 11MB\n",
    +       "Dimensions:    (latitude: 8, longitude: 10, time: 17520)\n",
    +       "Coordinates:\n",
    +       "  * latitude   (latitude) float64 64B 39.41 39.45 39.53 ... 39.69 39.81 39.89\n",
    +       "  * longitude  (longitude) float64 80B -106.4 -106.3 -106.3 ... -105.9 -105.9\n",
    +       "  * time       (time) datetime64[ns] 140kB 2022-01-01 ... 2022-12-31T23:30:00\n",
    +       "Data variables:\n",
    +       "    module     (time, latitude, longitude) float64 11MB nan nan nan ... nan nan
    " + ], + "text/plain": [ + " Size: 11MB\n", + "Dimensions: (latitude: 8, longitude: 10, time: 17520)\n", + "Coordinates:\n", + " * latitude (latitude) float64 64B 39.41 39.45 39.53 ... 39.69 39.81 39.89\n", + " * longitude (longitude) float64 80B -106.4 -106.3 -106.3 ... -105.9 -105.9\n", + " * time (time) datetime64[ns] 140kB 2022-01-01 ... 2022-12-31T23:30:00\n", + "Data variables:\n", + " module (time, latitude, longitude) float64 11MB nan nan nan ... nan nan" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "module_temp_template = pvdeg.geospatial.auto_template(\n", + " func=pvdeg.temperature.module,\n", + " ds_gids=geo_weather\n", + ")\n", + "\n", + "pvdeg.geospatial.analysis(\n", + " weather_ds=geo_weather,\n", + " meta_df=geo_meta,\n", + " func=pvdeg.temperature.module,\n", + " template=module_temp_template\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Geospatial Solar Position Calculation" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
    \n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
    <xarray.Dataset> Size: 67MB\n",
    +       "Dimensions:             (latitude: 8, longitude: 10, time: 17520)\n",
    +       "Coordinates:\n",
    +       "  * latitude            (latitude) float64 64B 39.41 39.45 39.53 ... 39.81 39.89\n",
    +       "  * longitude           (longitude) float64 80B -106.4 -106.3 ... -105.9 -105.9\n",
    +       "  * time                (time) datetime64[ns] 140kB 2022-01-01 ... 2022-12-31...\n",
    +       "Data variables:\n",
    +       "    apparent_zenith     (time, latitude, longitude) float64 11MB nan nan ... nan\n",
    +       "    zenith              (time, latitude, longitude) float64 11MB nan nan ... nan\n",
    +       "    apparent_elevation  (time, latitude, longitude) float64 11MB nan nan ... nan\n",
    +       "    elevation           (time, latitude, longitude) float64 11MB nan nan ... nan\n",
    +       "    azimuth             (time, latitude, longitude) float64 11MB nan nan ... nan\n",
    +       "    equation_of_time    (time, latitude, longitude) float64 11MB nan nan ... nan
    " + ], + "text/plain": [ + " Size: 67MB\n", + "Dimensions: (latitude: 8, longitude: 10, time: 17520)\n", + "Coordinates:\n", + " * latitude (latitude) float64 64B 39.41 39.45 39.53 ... 39.81 39.89\n", + " * longitude (longitude) float64 80B -106.4 -106.3 ... -105.9 -105.9\n", + " * time (time) datetime64[ns] 140kB 2022-01-01 ... 2022-12-31...\n", + "Data variables:\n", + " apparent_zenith (time, latitude, longitude) float64 11MB nan nan ... nan\n", + " zenith (time, latitude, longitude) float64 11MB nan nan ... nan\n", + " apparent_elevation (time, latitude, longitude) float64 11MB nan nan ... nan\n", + " elevation (time, latitude, longitude) float64 11MB nan nan ... nan\n", + " azimuth (time, latitude, longitude) float64 11MB nan nan ... nan\n", + " equation_of_time (time, latitude, longitude) float64 11MB nan nan ... nan" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "solar_position_template = pvdeg.geospatial.auto_template(\n", + " func=pvdeg.spectral.solar_position,\n", + " ds_gids=geo_weather\n", + ")\n", + "\n", + "pvdeg.geospatial.analysis(\n", + " weather_ds=geo_weather,\n", + " meta_df=geo_meta,\n", + " func=pvdeg.spectral.solar_position,\n", + " template=solar_position_template\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Geospatial POA Irradiance Calculation" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The array tilt angle was not provided, therefore the latitude tilt of 39.9 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.9 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.7 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.8 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.8 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.4 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.5 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.4 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.6 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.5 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.6 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n" + ] + }, + { + "data": { + "text/html": [ + "
    \n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
    <xarray.Dataset> Size: 56MB\n",
    +       "Dimensions:             (latitude: 8, longitude: 10, time: 17520)\n",
    +       "Coordinates:\n",
    +       "  * latitude            (latitude) float64 64B 39.41 39.45 39.53 ... 39.81 39.89\n",
    +       "  * longitude           (longitude) float64 80B -106.4 -106.3 ... -105.9 -105.9\n",
    +       "  * time                (time) datetime64[ns] 140kB 2022-01-01 ... 2022-12-31...\n",
    +       "Data variables:\n",
    +       "    poa_global          (time, latitude, longitude) float64 11MB nan nan ... nan\n",
    +       "    poa_direct          (time, latitude, longitude) float64 11MB nan nan ... nan\n",
    +       "    poa_diffuse         (time, latitude, longitude) float64 11MB nan nan ... nan\n",
    +       "    poa_sky_diffuse     (time, latitude, longitude) float64 11MB nan nan ... nan\n",
    +       "    poa_ground_diffuse  (time, latitude, longitude) float64 11MB nan nan ... nan
    " + ], + "text/plain": [ + " Size: 56MB\n", + "Dimensions: (latitude: 8, longitude: 10, time: 17520)\n", + "Coordinates:\n", + " * latitude (latitude) float64 64B 39.41 39.45 39.53 ... 39.81 39.89\n", + " * longitude (longitude) float64 80B -106.4 -106.3 ... -105.9 -105.9\n", + " * time (time) datetime64[ns] 140kB 2022-01-01 ... 2022-12-31...\n", + "Data variables:\n", + " poa_global (time, latitude, longitude) float64 11MB nan nan ... nan\n", + " poa_direct (time, latitude, longitude) float64 11MB nan nan ... nan\n", + " poa_diffuse (time, latitude, longitude) float64 11MB nan nan ... nan\n", + " poa_sky_diffuse (time, latitude, longitude) float64 11MB nan nan ... nan\n", + " poa_ground_diffuse (time, latitude, longitude) float64 11MB nan nan ... nan" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "poa_irradiance_template = pvdeg.geospatial.auto_template(\n", + " func=pvdeg.spectral.poa_irradiance,\n", + " ds_gids=geo_weather\n", + ")\n", + "\n", + "pvdeg.geospatial.analysis(\n", + " weather_ds=geo_weather,\n", + " meta_df=geo_meta,\n", + " func=pvdeg.spectral.poa_irradiance,\n", + " template=poa_irradiance_template\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Geospatial 98th Percentile Operating Temperature Calculation" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The array tilt angle was not provided, therefore the latitude tilt of 39.9 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.9 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.7 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.8 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.8 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.4 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.5 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.4 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.6 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.5 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.6 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n" + ] + }, + { + "data": { + "text/html": [ + "
    \n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
    <xarray.Dataset> Size: 784B\n",
    +       "Dimensions:       (latitude: 8, longitude: 10)\n",
    +       "Coordinates:\n",
    +       "  * latitude      (latitude) float64 64B 39.41 39.45 39.53 ... 39.69 39.81 39.89\n",
    +       "  * longitude     (longitude) float64 80B -106.4 -106.3 -106.3 ... -105.9 -105.9\n",
    +       "Data variables:\n",
    +       "    T98_estimate  (latitude, longitude) float64 640B nan nan nan ... nan nan nan
    " + ], + "text/plain": [ + " Size: 784B\n", + "Dimensions: (latitude: 8, longitude: 10)\n", + "Coordinates:\n", + " * latitude (latitude) float64 64B 39.41 39.45 39.53 ... 39.69 39.81 39.89\n", + " * longitude (longitude) float64 80B -106.4 -106.3 -106.3 ... -105.9 -105.9\n", + "Data variables:\n", + " T98_estimate (latitude, longitude) float64 640B nan nan nan ... nan nan nan" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "standoff_template = pvdeg.geospatial.auto_template(\n", + " func=pvdeg.standards.T98_estimate, \n", + " ds_gids=geo_weather\n", + " )\n", + "\n", + "pvdeg.geospatial.analysis(\n", + " weather_ds=geo_weather,\n", + " meta_df=geo_meta,\n", + " func=pvdeg.standards.T98_estimate,\n", + " template=standoff_template\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Geospatial Module Humidity Calculation" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The array tilt angle was not provided, therefore the latitude tilt of 39.9 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.9 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.7 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.8 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.8 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.4 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.5 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.4 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.6 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.5 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.6 was used.\n" + ] + }, + { + "data": { + "text/html": [ + "
    \n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
    <xarray.Dataset> Size: 45MB\n",
    +       "Dimensions:             (latitude: 8, longitude: 10, time: 17520)\n",
    +       "Coordinates:\n",
    +       "  * latitude            (latitude) float64 64B 39.41 39.45 39.53 ... 39.81 39.89\n",
    +       "  * longitude           (longitude) float64 80B -106.4 -106.3 ... -105.9 -105.9\n",
    +       "  * time                (time) datetime64[ns] 140kB 2022-01-01 ... 2022-12-31...\n",
    +       "Data variables:\n",
    +       "    RH_surface_outside  (time, latitude, longitude) float64 11MB nan nan ... nan\n",
    +       "    RH_front_encap      (time, latitude, longitude) float64 11MB nan nan ... nan\n",
    +       "    RH_back_encap       (time, latitude, longitude) float64 11MB nan nan ... nan\n",
    +       "    RH_backsheet        (time, latitude, longitude) float64 11MB nan nan ... nan
    " + ], + "text/plain": [ + " Size: 45MB\n", + "Dimensions: (latitude: 8, longitude: 10, time: 17520)\n", + "Coordinates:\n", + " * latitude (latitude) float64 64B 39.41 39.45 39.53 ... 39.81 39.89\n", + " * longitude (longitude) float64 80B -106.4 -106.3 ... -105.9 -105.9\n", + " * time (time) datetime64[ns] 140kB 2022-01-01 ... 2022-12-31...\n", + "Data variables:\n", + " RH_surface_outside (time, latitude, longitude) float64 11MB nan nan ... nan\n", + " RH_front_encap (time, latitude, longitude) float64 11MB nan nan ... nan\n", + " RH_back_encap (time, latitude, longitude) float64 11MB nan nan ... nan\n", + " RH_backsheet (time, latitude, longitude) float64 11MB nan nan ... nan" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "humidity_template = pvdeg.geospatial.auto_template(\n", + " func=pvdeg.humidity.module,\n", + " ds_gids=geo_weather\n", + ")\n", + "\n", + "pvdeg.geospatial.analysis(\n", + " weather_ds=geo_weather,\n", + " meta_df=geo_meta,\n", + " func=pvdeg.humidity.module,\n", + " template=humidity_template\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Geospatial IwaVantHoff Environment Characterization Calculation" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The array tilt angle was not provided, therefore the latitude tilt of 39.9 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.9 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.7 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.8 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.8 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.4 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.5 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.4 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.6 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.5 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n", + "The array tilt angle was not provided, therefore the latitude tilt of 39.6 was used.\n", + "The array azimuth was not provided, therefore an azimuth of 180.0 was used.\n" + ] + }, + { + "data": { + "text/html": [ + "
    \n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
    <xarray.Dataset> Size: 784B\n",
    +       "Dimensions:      (latitude: 8, longitude: 10)\n",
    +       "Coordinates:\n",
    +       "  * latitude     (latitude) float64 64B 39.41 39.45 39.53 ... 39.69 39.81 39.89\n",
    +       "  * longitude    (longitude) float64 80B -106.4 -106.3 -106.3 ... -105.9 -105.9\n",
    +       "Data variables:\n",
    +       "    IwaVantHoff  (latitude, longitude) float64 640B nan nan nan ... nan nan nan
    " + ], + "text/plain": [ + " Size: 784B\n", + "Dimensions: (latitude: 8, longitude: 10)\n", + "Coordinates:\n", + " * latitude (latitude) float64 64B 39.41 39.45 39.53 ... 39.69 39.81 39.89\n", + " * longitude (longitude) float64 80B -106.4 -106.3 -106.3 ... -105.9 -105.9\n", + "Data variables:\n", + " IwaVantHoff (latitude, longitude) float64 640B nan nan nan ... nan nan nan" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "iwa_vant_hoff_template = pvdeg.geospatial.auto_template(\n", " func=pvdeg.degradation.IwaVantHoff,\n", @@ -855,7 +4659,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 15, "metadata": {}, "outputs": [ { @@ -1230,8 +5034,8 @@ " * latitude (latitude) float64 64B 39.41 39.45 39.53 ... 39.81 39.89\n", " * longitude (longitude) float64 80B -106.4 -106.3 ... -105.9 -105.9\n", "Data variables:\n", - " edge_seal_width (latitude, longitude) float64 640B nan nan nan ... nan nan
  • " ], "text/plain": [ " Size: 784B\n", @@ -1260,7 +5064,7 @@ " edge_seal_width (latitude, longitude) float64 640B nan nan nan ... nan nan" ] }, - "execution_count": 7, + "execution_count": 15, "metadata": {}, "output_type": "execute_result" } @@ -1281,7 +5085,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 16, "metadata": {}, "outputs": [ { @@ -1655,8 +5459,8 @@ "Coordinates:\n", " * gid (gid) int64 88B 449211 452064 453020 ... 459670 460613 462498\n", "Data variables:\n", - " width (gid) float64 88B dask.array<chunksize=(11,), meta=np.ndarray>" + " dtype='int64', name='gid'))
  • " ], "text/plain": [ " Size: 176B\n", @@ -1724,7 +5528,7 @@ " width (gid) float64 88B dask.array" ] }, - "execution_count": 8, + "execution_count": 16, "metadata": {}, "output_type": "execute_result" }