Skip to content

Instantly share code, notes, and snippets.

@ceholden
Created May 15, 2019 16:25
Show Gist options
  • Save ceholden/1fd99f9da0ac8797aeb8a9d1ef171027 to your computer and use it in GitHub Desktop.
Save ceholden/1fd99f9da0ac8797aeb8a9d1ef171027 to your computer and use it in GitHub Desktop.
Display the source blob
Display the rendered blob
Raw
{
"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