Created
May 15, 2019 16:25
-
-
Save ceholden/1fd99f9da0ac8797aeb8a9d1ef171027 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"cells": [ | |
{ | |
"cell_type": "code", | |
"execution_count": 1, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"from collections import defaultdict\n", | |
"import datetime as dt\n", | |
"\n", | |
"import numpy as np\n", | |
"import pandas as pd" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 2, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"# in some config.py file...\n", | |
"\n", | |
"# frequency for accounting\n", | |
"REPORT_TIMESTEP = '1YS'" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 51, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"import abc\n", | |
"\n", | |
"\n", | |
"class Flux(object, metaclass=abc.ABCMeta):\n", | |
" \"\"\"\n", | |
" \n", | |
" Parameters\n", | |
" ----------\n", | |
" area : float\n", | |
" Area experiencing this flux\n", | |
" time_start : datetime\n", | |
" Time this flux started\n", | |
" rate_params : \n", | |
" Extra parameters to pass onto subclasses of this\n", | |
" \"\"\"\n", | |
" \n", | |
" RATE_PARAMS = ()\n", | |
" \n", | |
" def __init__(self, area, time_start, time_end=None, rate_params=None, **kwds):\n", | |
" self.area = area\n", | |
" self.time_start = time_start\n", | |
" self.rate_params = rate_params or {}\n", | |
" self.rate_params.update(kwds)\n", | |
" self._validate()\n", | |
" \n", | |
" def _validate(self):\n", | |
" req_params = getattr(self, 'RATE_PARAMS', [])\n", | |
" for param in req_params:\n", | |
" if param not in self.rate_params:\n", | |
" raise ValueError(f'Missing required rate parameter \"{param}\"')\n", | |
" \n", | |
" @abc.abstractmethod\n", | |
" def flux_rate(self, time):\n", | |
" \"\"\" Return the flux rate for a given time\n", | |
" \"\"\"\n", | |
" raise NotImplementedError\n", | |
" \n", | |
" @abc.abstractmethod\n", | |
" def report(self, report_time):\n", | |
" \"\"\" Return the flux rate at a given time\n", | |
" \"\"\"\n", | |
" raise NotImplementedError\n", | |
"\n", | |
" def integrate(self, report_start, report_end, timestep=REPORT_TIMESTEP):\n", | |
" \"\"\" Integrate flux over some reporting time period\n", | |
" \"\"\"\n", | |
" total = 0.\n", | |
" periods = pd.date_range(report_start, report_end, freq=timestep)\n", | |
" for start_, end_ in zip(periods, periods[1:]):\n", | |
" middle = end_ - (end_ - start_) / 2\n", | |
" total += self.report(middle)\n", | |
" return total\n", | |
" \n", | |
" \n", | |
"class ConstantFlux(Flux):\n", | |
" \"\"\" A constant flux of something\n", | |
" \"\"\"\n", | |
" \n", | |
" RATE_PARAMS = ('flux_rate', )\n", | |
" \n", | |
" def flux_rate(self, time):\n", | |
" # Constant flux rate -- not a function of time!\n", | |
" return self.rate_params['flux_rate']\n", | |
" \n", | |
" def report(self, report_time):\n", | |
" flux_rate_ = self.flux_rate(report_time)\n", | |
" return self.area * flux_rate_\n", | |
" \n", | |
" \n", | |
"class ForestToPastureFlux(Flux):\n", | |
" \"\"\" A constant flux of something\n", | |
" \"\"\"\n", | |
" \n", | |
" RATE_PARAMS = ('biomass_0', 'biomass_1', 'decay', )\n", | |
" \n", | |
" def flux_rate(self, time):\n", | |
" dt = (self.time_start - time).value\n", | |
" return (self.rate_params['biomass_1'] - self.rate_params['biomass_0']) * (dt / self.rate_params['decay'] / 1e6)\n", | |
" \n", | |
" def report(self, report_time):\n", | |
" flux_rate_ = self.flux_rate(report_time)\n", | |
" return self.area * flux_rate_" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 52, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": [ | |
"500.0" | |
] | |
}, | |
"execution_count": 52, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"ConstantFlux(\n", | |
" 100.,\n", | |
" pd.to_datetime('2000'),\n", | |
" rate_params={\n", | |
" 'flux_rate': 5.\n", | |
" }\n", | |
"# flux_rate=5.\n", | |
").report(pd.to_datetime('2005'))" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 53, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"cfg = {\n", | |
" 'fluxes': {\n", | |
" 'stable_forest': {\n", | |
" 'category': 'ConstantFlux',\n", | |
" 'rate_params': {\n", | |
" 'flux_rate': -10\n", | |
" }\n", | |
" },\n", | |
" 'stable_nonforest': {\n", | |
" 'category': 'ConstantFlux',\n", | |
" 'rate_params': {\n", | |
" 'flux_rate': 25\n", | |
" }\n", | |
" },\n", | |
" 'forest2pasture': {\n", | |
" 'category': 'ForestToPastureFlux',\n", | |
" 'rate_params': {\n", | |
" 'biomass_0': 100,\n", | |
" 'biomass_1': 15,\n", | |
" 'decay': 0.5\n", | |
" }\n", | |
" }\n", | |
" }\n", | |
"}" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 55, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"class FluxFactory(object):\n", | |
" \"\"\" Flux Factory function (or as a class so we can build a \"registry\")\n", | |
" \"\"\"\n", | |
" \n", | |
" FLUX_DATABASE = {\n", | |
" 'ConstantFlux': ConstantFlux,\n", | |
" 'ForestToPastureFlux': ForestToPastureFlux\n", | |
" }\n", | |
" \n", | |
" def __init__(self, **database):\n", | |
" self.database = self.FLUX_DATABASE.copy()\n", | |
" self.database.update(database)\n", | |
" \n", | |
" def create_flux(self, area, time, category, rate_params):\n", | |
" cls = self.database.get(category, None)\n", | |
" if not cls:\n", | |
" raise KeyError('Cannot find flux category \"{category}\"}')\n", | |
" inst = cls(area, time, **rate_params)\n", | |
" return inst\n", | |
" \n", | |
" def create_flux_from_config(self, area, time, config):\n", | |
" config = config.copy()\n", | |
" category = config.pop('category')\n", | |
" return self.create_flux(area, time, category, config)" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 57, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"history = pd.DataFrame.from_dict({\n", | |
" 'stable_forest': [10, 9, 8, 7],\n", | |
" 'stable_nonforest': [20, 21, 22, 23],\n", | |
" 'forest2pasture': [1, 3, 4, 2]\n", | |
"})\n", | |
"history.index = pd.date_range('2000', '2003', freq='YS')" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 58, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"Automatic pdb calling has been turned OFF\n" | |
] | |
} | |
], | |
"source": [ | |
"%pdb" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 61, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"Subtotal: -91271231998190.0\n", | |
"Subtotal: -37513151998190.0\n", | |
"Subtotal: 16098048001810.0\n", | |
"Subtotal: 69709248001810.0\n", | |
"Subtotal: 123320448001810.0\n", | |
"Subtotal: 177078528001810.0\n", | |
"Subtotal: 230689728001810.0\n", | |
"Subtotal: 284300928001810.0\n", | |
"Subtotal: 337912128001810.0\n", | |
"Subtotal: 391670208001810.0\n", | |
"Subtotal: 445281408001810.0\n", | |
"Subtotal: 498892608001810.0\n", | |
"Subtotal: 552503808001810.0\n", | |
"Subtotal: 606261888001810.0\n", | |
"Subtotal: 659873088001810.0\n", | |
"Subtotal: 713484288001810.0\n", | |
"Subtotal: 767095488001810.0\n", | |
"Subtotal: 820853568001810.0\n", | |
"Subtotal: 874464768001810.0\n", | |
"Subtotal: 928075968001810.0\n", | |
"Subtotal: 981687168001810.0\n" | |
] | |
} | |
], | |
"source": [ | |
"factory = FluxFactory()\n", | |
"\n", | |
"# BOOK KEEPING CLASS\n", | |
"\n", | |
"# How do we want to store and organize the fluxes? By class for all time? By class/time? etc...\n", | |
"flux_objs = defaultdict(list)\n", | |
"\n", | |
"\n", | |
"# CONSTRUCT REPORT\n", | |
"for time in history.index:\n", | |
" for label in history.keys():\n", | |
" area = history.loc[time, label]\n", | |
" flux_ = factory.create_flux_from_config(area, time, cfg['fluxes'][label])\n", | |
" flux_objs[label].append(flux_)\n", | |
" \n", | |
"# DO THE REPORT\n", | |
"report_years = pd.date_range('2000', '2020', freq='YS')\n", | |
"total_of_totals = 0.\n", | |
"for year in report_years:\n", | |
" subtotal = 0.\n", | |
" for category, fluxes_ in flux_objs.items():\n", | |
" for flux_ in fluxes_:\n", | |
" subtotal += flux_.report(year)\n", | |
" print(f'Subtotal: {subtotal}')\n", | |
" total_of_totals += subtotal" | |
] | |
} | |
], | |
"metadata": { | |
"kernelspec": { | |
"display_name": "Python 3", | |
"language": "python", | |
"name": "python3" | |
}, | |
"language_info": { | |
"codemirror_mode": { | |
"name": "ipython", | |
"version": 3 | |
}, | |
"file_extension": ".py", | |
"mimetype": "text/x-python", | |
"name": "python", | |
"nbconvert_exporter": "python", | |
"pygments_lexer": "ipython3", | |
"version": "3.7.1" | |
} | |
}, | |
"nbformat": 4, | |
"nbformat_minor": 2 | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment