{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Model to get started\n",
"\n",
"* File name: model_to_get_started.ipynb\n",
"* Last edited: 2020-06-24\n",
"* Created by: Stefan Bruche (TU Berlin)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"```python\n",
"import aristopy as ar\n",
"\n",
"# Create basic energy system instance\n",
"es = ar.EnergySystem(\n",
" number_of_time_steps=3, hours_per_time_step=1, \n",
" interest_rate=0.05, economic_lifetime=20)\n",
"\n",
"# Add a gas source, two different conversion units and sinks\n",
"gas_source = ar.Source(\n",
" ensys=es, name='gas_source', commodity_cost=20, outlet=ar.Flow('Fuel'))\n",
"\n",
"gas_boiler = ar.Conversion(\n",
" ensys=es, name='gas_boiler', basic_variable='Heat',\n",
" inlet=ar.Flow('Fuel', 'gas_source'), outlet=ar.Flow('Heat', 'heat_sink'),\n",
" capacity_max=150, capex_per_capacity=60e3, \n",
" user_expressions='Heat == 0.9 * Fuel')\n",
"\n",
"chp_unit = ar.Conversion(\n",
" ensys=es, name='chp_unit', basic_variable='Elec',\n",
" inlet=ar.Flow('Fuel', 'gas_source'), \n",
" outlet=[ar.Flow('Heat', 'heat_sink'), ar.Flow('Elec', 'elec_sink')],\n",
" capacity_max=100, capex_per_capacity=600e3,\n",
" user_expressions=['Heat == 0.5 * Fuel', \n",
" 'Elec == 0.4 * Fuel'])\n",
"\n",
"heat_sink = ar.Sink(\n",
" ensys=es, name='heat_sink', inlet=ar.Flow('Heat'),\n",
" commodity_rate_fix=ar.Series('heat_demand', [100, 200, 150]))\n",
"\n",
"elec_sink = ar.Sink(\n",
" ensys=es, name='elec_sink', inlet=ar.Flow('Elec'), commodity_revenues=30)\n",
"\n",
"# Run the optimization\n",
"es.optimize(solver='cbc', results_file='results.json')\n",
"\n",
"# Plot some results\n",
"plotter = ar.Plotter('results.json')\n",
"plotter.plot_operation('heat_sink', 'Heat', lgd_pos='lower center', \n",
" bar_lw=0.5, ylabel='Thermal energy [MWh]')\n",
"plotter.plot_objective(lgd_pos='lower center')\n",
"```"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Create *aristopy* model\n",
"\n",
"First, we need to import the *aristopy* package. If the import fails, you might need to recheck the installation instructions."
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"# Import the required packages (jupyter magic only required for jupyter notebooks)\n",
"%reload_ext autoreload\n",
"%autoreload 2\n",
"%matplotlib inline\n",
"\n",
"import aristopy as ar"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"An *aristopy* model consists of an instance of the EnergySystem class and the added components.
\n",
"To create an energy system, we need to specify the number of considered time steps and the number of hours per time step. Additionally, the interest rate and the economic lifetime of the installed components are required to calculate the net present value (objective function value)."
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"# Create basic energy system instance\n",
"es = ar.EnergySystem(number_of_time_steps=3, hours_per_time_step=1,\n",
" interest_rate=0.05, economic_lifetime=20)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"To instantiate a Component instance (Source, Sink, Conversion, Bus, Storage), we need to specify the EnergySystem instance, where it is added to and set a name for the component. Next, we add flows on the inlets and outlets. A Flow instance represents a connection point of a component and is used to create links with other components. Additionally, the flow introduces a commodity to the component and triggers the creation of an associated commodity variable (usually with the same name). The number of required or accepted inlet and outlet flows and component commodities depends on the component type (see table below). You can add multiple flows on an inlet or outlet for setting different commodities or linking components, by arranging them in a list."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"| Component type | Nbr. of inlet flows | Nbr. of outlet flows | Nbr. of commodities |\n",
"| :--- | :---: | :---: | :---: |\n",
"| Source | 0 | $\\ge$ 1 | 1 |\n",
"| Sink | $\\ge$ 1 | 0 | 1 |\n",
"| Conversion | $\\ge$ 1 | $\\ge$ 1 | $\\ge$ 1 |\n",
"| Storage | $\\ge$ 1 | $\\ge$ 1 | 1 |\n",
"| Bus | $\\ge$ 1 | $\\ge$ 1 | 1 |\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
"# Add a gas source\n",
"gas_source = ar.Source(ensys=es, name='gas_source', outlet=ar.Flow('Fuel'),\n",
" commodity_cost=20)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The conversion instances usually have different commodities on their inlets and outlets. That's why we need to specify the name of the basic variable for conversion components. This basic variable is used to restrict capacities, set operation rates, and calculate CAPEX and OPEX."
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
"# Add a gas boiler conversion unit\n",
"gas_boiler = ar.Conversion(ensys=es, name='gas_boiler',\n",
" basic_variable='Heat',\n",
" inlet=ar.Flow(commodity='Fuel', link='gas_source'),\n",
" outlet=ar.Flow('Heat', 'heat_sink'),\n",
" capacity_max=150, capex_per_capacity=60e3,\n",
" user_expressions='Heat == 0.9 * Fuel')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We can use the keyword argument **user_expressions** to specify commodity conversion rates, limit capacities, and set other internal component constraints manually. Here we can use the names (identifiers) of the commodity variables created by adding flows, and, if applicable, variables with standard names, e.g.:\n",
"\n",
"* CAP - component capacity variable\n",
"* BI_EX - binary existence variable\n",
"* BI_OP - binary operation variable\n",
"* ... (see file utils.py in your aristopy directory) \n",
"\n",
"The expressions are simply added as a list of strings. The options for mathematical operators are:
\n",
"``sum, sin, cos, exp, log, ==, >=, <=, **, *, /, +, -, (, )``.
\n",
"The indexes (sets) of the variables and parameters are processed automatically behind the scenes."
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
"# Add a CHP unit\n",
"chp_unit = ar.Conversion(ensys=es, name='chp_unit', basic_variable='Elec',\n",
" inlet=ar.Flow('Fuel', 'gas_source'),\n",
" outlet=[ar.Flow('Heat', 'heat_sink'), ar.Flow('Elec', 'elec_sink')],\n",
" capacity_max=100, capex_per_capacity=600e3,\n",
" user_expressions=['Heat == 0.5 * Fuel',\n",
" 'Elec == 0.4 * Fuel'])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Time series data can be introduced as an aristopy Series instance and might be applied to set commodity rates, and time-dependent commodity cost or revenues, or generally for the scripting of user expressions."
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [],
"source": [
"# Add a sink with fixed heat demand\n",
"heat_sink = ar.Sink(ensys=es, name='heat_sink', inlet=ar.Flow('Heat'),\n",
" commodity_rate_fix=ar.Series('heat_demand', [100, 200, 150]))\n",
"\n",
"elec_sink = ar.Sink(ensys=es, name='elec_sink', inlet=ar.Flow('Elec'),\n",
" commodity_revenues=30)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"