From 9e4d4124302a6838ccd83fba518b1b2d424c60a7 Mon Sep 17 00:00:00 2001 From: Will Usher Date: Thu, 22 Sep 2022 11:07:57 +0200 Subject: [PATCH] Set up the exercise for life academy --- MJ2383_Lab_1.ipynb | 1022 -------------------------- MJ2383_Lab_3.ipynb => OSeMOSYS.ipynb | 11 +- README.rst | 19 +- 3 files changed, 15 insertions(+), 1037 deletions(-) delete mode 100644 MJ2383_Lab_1.ipynb rename MJ2383_Lab_3.ipynb => OSeMOSYS.ipynb (99%) diff --git a/MJ2383_Lab_1.ipynb b/MJ2383_Lab_1.ipynb deleted file mode 100644 index 6a99ed2..0000000 --- a/MJ2383_Lab_1.ipynb +++ /dev/null @@ -1,1022 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Make sure to run this cell.\n", - "# here we import all the libraries we will use in this session\n", - "import numpy as np\n", - "import itertools\n", - "from plotly import express as px\n", - "import pandas as pd\n", - "import ipywidgets as widgets\n", - "from ipywidgets import interact, fixed\n", - "from checkers import checker_df, checker_lcoe, checker_parameters" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# MJ2383: Lab 1 - Computing Levelised Cost of Electricity\n", - "\n", - "In this lab, we are going to explore computation of an important metric called **levelised cost of electricity**, or LCOE for short.\n", - "\n", - "The lab is broken up into several stages each of which culminates in writing a piece of Python code or doing some analysis. Don't worry if you've never written any Python before or done any programming - it is actually as easy as (or easier than) writing down equations.\n", - "\n", - "in [stage 0](#Stage-0---Basic-Python-commands) we'll go through Python basics. If you've never used Python or programming before, you'll want to start here.\n", - "\n", - "In [stage 1](#Stage-1---Compute-LCOE), we'll write a Python function which computes the levelised cost of electricity from input data. We'll then test that this function produces the result we expect. You'll be able to check that your function works at the end of the stage.\n", - "\n", - "In [stage 2](#Stage-2---Explore-Parameter-Values), we'll then use the function you wrote to compute LCOE for one technology and explore what happens when you change the parameter values.\n", - "\n", - "in [stage 3](#Stage-3:-Comparing-technologies), we'll use the LCOE function again to compare technologies against one another, and develop screen curves that allow us to visualise the trade offs between technologies as a function of load-factor.\n", - "\n", - "## Housekeeping\n", - "\n", - "- This is a Jupyter notebook. A notebook contains cells. Each cell contains either descriptive text (like this) or Python code. You execute a cell by pressing `Shift + Enter`. Or, you can use the `Run` button in the menu bar at the top of the page.\n", - "\n", - "- Don't worry, you cannot break anything. If you think you've made a mistake, you can delete the content and try again. If you get really stuck, just reload the page and you'll be able to make a fresh start.\n", - "\n", - "- Also, each stage allows you to begin afresh, so you can just move on when you are ready.\n", - "\n", - "- If you like, work together in pairs sharing a screen.\n", - "\n", - "- The notebook has been designed to help support your learning by enabling you to explore, but also to challenge you. However, we are not trying to trick you. In most cases, the answers are in front of you.\n", - "\n", - "- If you have questions, please ask us!\n", - "- If you have any suggestions for how this could be improved, or find a bug or problem, please submit an issue\n", - " [here](https://github.com/KTH-dESA/MJ2383/issues/new/choose)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Stage 0 - Basic Python commands\n", - "In this section, we will go through an overview of the most basics in python. If you already have knowledge of python you can skip this stage and move to [stage 1](#Stage-1---Compute-LCOE).\n", - "\n", - "In Python we use the number symbol `#` to make a comment in the code. A comment is for clarification porpuses to the person reading the code, but it is ignored by the computer when you run the program, thus having no effect on your code output:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# this is a comment used to provide additional information" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In Python there exist many types of data, in here we are going to cover the most basic ones: integers, floats, strings, lists and arrays.\n", - "```\n", - "5 integer\n", - "2.6 float\n", - "'this is a string' str\n", - "[1, 2, 3, 4] list\n", - "np.array([3, 4, 6, 4]) numpy.ndarray\n", - "```\n", - "we can use the function `type` to check the type of a value. Pass different data types to the `type` function to see the different output:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "type(1) # change the data inside parenthesis" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Basic operators\n", - "You can actually use Python as a calculator to compute equations and store values into variables. This means that you can use basic mathematical operators:\n", - "```\n", - "+ addition\n", - "- subtraction\n", - "* multiplication\n", - "/ division\n", - "** exponent\n", - "// floor division\n", - "```" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "(3 + 5 * 10) / 4 - 10" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "3 ** 2" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You can also store the output of an operation into a variable and use such variable for further operations:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "x = 3 ** 2\n", - "y = 4 * x\n", - "print(x)\n", - "print(y)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Lists and arrays\n", - "Lists are variables containing a sequence of ints, floats, strings etc., even other lists.... Whatever you want to put in your list. We create a list by using square brackets `[]` and passing the values of the list separated by commas." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "my_list = [1, 2, 3, 4]\n", - "my_list" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "An array is a list of values, which allows to make numerical calculations very efficiently. We create an array by calling the `numpy` library function `np.array()` and passing a list as argument (inside the parentesis):" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "my_array = np.array([1, 2, 3, 4])\n", - "my_array" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Then we can use this array to make some elementwise computations or even pairwise computations between two arrays:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "my_array * 2" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "my_array * np.array([4, 5, 6, 7])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can even sum all of the values of the array by calling the function `sum()`" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "sum(my_array)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Stage 1 - Compute LCOE\n", - "\n", - "According to [OEE](http://www.open-electricity-economics.org/book/text/03.html) the formula to calculate LCOE is as follows:\n", - "\n", - "$LCOE = \\frac{C_{fix} + \\sum_{y=1}^Y DF_y \\cdot C_y}{\\sum_{y=1}^Y DF_y \\cdot G_y}$\n", - "\n", - "where LCOE is the levelized cost in EUR per kWh, $C_{fix}$ is the capital investment costs incurred for setting up the project, $C_y$ are operational costs incurred in year $y$, $Y$ is the technical lifetime in years, and $G_y$ is electricity generation in kWh. The costs are called levelized because they are “leveled” over all units of output. Levelized costs can be calculated for a specific power plant or for generic types of generation technologies.\n", - "\n", - "Discount factor ($DF$) is calculated for each year ($y$) of the plant's technical lifetime\n", - "\n", - "$DF_y = (1 + r)^{-y}$\n", - "\n", - "where $r$ is the discount rate" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here we can store the technical lifetime of our technology in a *variable* called ``technical_lifetime``" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "technical_lifetime = 20" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You can access the value stored in the *variable* at anytime, by typing its name and press `shift` + `enter`:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "technical_lifetime" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We then create an *array* of years using the value stored in ``technical_lifetime``. \n", - "\n", - "An *array* is like a list of values. The command ``np.arange()`` takes a number as an argument and creates an *array* of the same length containing the values from 0 to one less than the number. You can always check what are the parameters that a command (or function) accepts and what it returns as result. For this you can place the cursor over the function and hit `shift + tab` in your keyboard:\n", - "\n", - "![Documentation](img/docstring.gif \"Documentation\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "years = np.arange(technical_lifetime)\n", - "years" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we calculate $DF$ for each year in our plant's technical lifetime according to the formula we saw earlier:\n", - "$DF_y = (1 + r)^{-y}$; $r$ is the discount rate, and $y$ is the year.\n", - "\n", - "**Implement the DF formula in the cell below. Remember that to exponentiate in Python we use** `**`.\n", - "\n", - "**Q. What happens if you change the discount rate?**" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "discount_rate = 0.05\n", - "df = \"replace with formula\"\n", - "print(df) # we use the command print, to show the values stored inside the df variable" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Run this cell to check your answer\n", - "checker_df(value=df, \n", - " discount_rate=discount_rate, \n", - " years=years)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we've calculated $DF$, let's get some data for the other parameters:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "capital_costs = 1000.0 # €\n", - "operational_costs = np.repeat(500, technical_lifetime) # €\n", - "electricity_generation = np.repeat(8760., technical_lifetime) # kWh" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The code above creates variables, each containing an *array* of values. We automatically generate the arrays using `np.repeat(50, technical_lifetime)`. This creates an array of length `technical_lifetime` where each element in the array contains the value `50`.\n", - "\n", - "---\n", - "\n", - "Remember the formula? Let's check it again:\n", - "\n", - "\\\\(LCOE = \\frac{C_{fix} + \\sum_{y=1}^Y DF_y \\cdot C_y}{\\sum_{y=1}^Y DF_y \\cdot G_y}\\\\)\n", - "\n", - "Now, implement the LCOE formula in the cell below:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "lcoe = \"replace with formula\"\n", - "print(lcoe) # €/kWh" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "checker_lcoe(value=lcoe, \n", - " capital_costs=capital_costs, \n", - " operational_costs=operational_costs, \n", - " electricity_generation=electricity_generation, \n", - " discount_rate=discount_rate, \n", - " technical_lifetime=technical_lifetime)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "At the moment, this seems a pretty arbitrary result. We're just putting in nonsense data, and we are getting nonsense in return. In stage 2, we'll start working with real data, but first, we need to make this calculation re-useable.\n", - "\n", - "---\n", - "\n", - "In Python, if we want to reuse a piece of code, we'll create a \"function\". To do this, you use the ``def`` command like this:\n", - "\n", - "```python\n", - "def get_lcoe(capital_costs, operational_costs, electricity_generation, discount_rate, technical_lifetime):\n", - " # <>\n", - " return lcoe\n", - "```\n", - "\n", - "- In the above example `get_lcoe` is the name of the function\n", - "- The list of names in the round brackets `()` are arguments\n", - "- Don't forget the `:` at the end of the list of arguments\n", - "- Use four spaces to indent your code\n", - "- The argument passed to `return` in the last line is what the function will give back when you run it\n", - "\n", - "**Q. What should go in the function?**\n", - "\n", - "Have a go below:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def get_lcoe(capital_costs, operational_costs, electricity_generation, discount_rate, technical_lifetime):\n", - " # Write your code next, be sure to include any variable needed to calculate the lcoe value.\n", - " # Use the input variables of the function: \n", - " # (capital_costs, operational_costs, electricity_generation, discount_rate, technical_lifetime)\n", - " \n", - " # \n", - " lcoe = \"replace with formula\"\n", - " return lcoe" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Test your `lcoe` function by running the cell below." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# With the next line we make a call to the function you wrote passing some input values\n", - "actual = get_lcoe(capital_costs=5000, \n", - " operational_costs=np.array([100, 100, 100, 100]), \n", - " electricity_generation=np.array([8760, 8760, 8760, 8760]), \n", - " discount_rate=0.1, \n", - " technical_lifetime=4)\n", - "# We print the results\n", - "print(actual)\n", - "\n", - "# We run the checker with your calculation and the same input data, to see if the results is correct\n", - "checker_lcoe(value=actual, \n", - " capital_costs=5000, \n", - " operational_costs=np.array([100, 100, 100, 100]), \n", - " electricity_generation=np.array([8760, 8760, 8760, 8760]), \n", - " discount_rate=0.1, \n", - " technical_lifetime=4)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Stage 1 - Summary\n", - "\n", - "If you managed to get it to work, well done! You've written a Python function which computes LCOE. If not, don't worry, the answer is below! Make sure to write it in the function so the following steps of the Lab work.\n", - "\n", - "- In this stage, we introduced the LCOE equation\n", - "\\\\(LCOE = \\frac{C_{fix} + \\sum_{y=1}^Y DF_y \\cdot C_y}{\\sum_{y=1}^Y DF_y \\cdot G_y}\\\\)\n", - "and implemented it using a Python function.\n", - "- We also explored how the capital recovery factors weighs future years differently as a function of the discount rate.\n", - "- We learnt a lot of Python concepts including *variables*, *arrays*, and *arguments*.\n", - "- We learnt how to reuse Python code by writing a function.\n", - "\n", - "In the next stage, we'll extend our function to make it more useful and investigate the LCOE calculation itself.\n", - "\n", - "### Stage 1 - Answer LCOE Function\n", - "\n", - "As you can see, we just need to fill in the gaps. First build the array of years, calculate the array of DF and then add the lcoe calculation.\n", - "```python\n", - "def get_lcoe(capital_costs, operational_costs, electricity_generation, discount_rate, technical_lifetime):\n", - " year = np.arange(technical_lifetime)\n", - " df = (1 + discount_rate) ** - year\n", - " lcoe = (capital_costs + sum(df * operational_costs)) / sum(df * electricity_generation)\n", - " return lcoe\n", - "```" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Stage 2 - Explore Parameter Values" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We'll now make use of an LCOE function to explore the effect of different parameter values for one technology — a combined cycle gas turbine. In the following implementation of LCOE, we make the assumption that the yearly values for operational costs and electricity generation are the same in each year of the technology's lifetime." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here are the parameters for a combined cycle gas turbine (CCGT):\n", - "\n", - "Parameter | Unit |Value\n", - "---|---|---\n", - "Lifetime| |25\n", - "Discount rate | |\t\t\t8.0%\n", - "Station size | MW |\t750.0\n", - "Overnight cost | €/kW | 750.0\n", - "Fixed O&M cost | €/kW\t| 3.0\n", - "Efficiency | | 0.5\n", - "Fuel price | €/kWh| 0.03\n", - "Emission Factor\t| ton/kW-yr| 1.8\n", - "Load factor | | 0.75\n", - "\n", - "Here's the information we need to calculate the LCOE using the simple LCOE equation we developed earlier.\n", - "\n", - "Parameter | Unit |Value\n", - "---|---|---\n", - "Capital cost | € | ?\n", - "Electricity generation | kWh | ?\n", - "Annual fixed O&M cost |\t€\t| ?\n", - "Annual variable O&M cost |€| ?\n", - "Annual operational cost | € | ?\n", - "\n", - "**Q. What's the levelised cost of electricity for the CCGT plant?**\n", - "- work through the list of inputs methodically. \n", - "- Pay close attention to the units. \n", - "- Use Python variables to store information you reuse.\n", - "\n", - "### Hints\n", - "\n", - "- Remember there are approximately `365 * 24 = 8760` hours in a year.\n", - "- The load factor describes the proportion of those 8760 hours the plant is operational.\n", - "- Total operational costs are made up of fixed costs (not a function of activity) and variable costs (function of activity).\n", - "- The fixed costs are a function of capacity.\n", - "- The variable cost will be the product of the amount of fuel used and the fuel price.\n", - "- Fuel use can be computed from electricity generation and efficiency.\n", - "- Create any new variable if you need to." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Add your working HERE\n", - "\n", - "# 1. Populate some variables using the information from the table\n", - "\n", - "lifetime = \n", - "discount_rate = \n", - "capacity = # kW\n", - "overnight_cost = # €/kW the cost of a project if no interest was incurred during construction, as if the project was completed \"overnight.\"\n", - "fixed_om_cost = # €/kW\n", - "efficiency = \n", - "fuel_price = # €/kWh\n", - "load_factor = \n", - "\n", - "# 2. Calculate the intermediate values (you can use Python like a calculator)\n", - "# Use *+-/ for multiply, add, substract and divide.\n", - "\n", - "capital_costs = capacity * overnight_cost # in €\n", - "# electricity_generation = \n", - "# operational_costs = \n", - "\n", - "print(electricity_generation)\n", - "print(operational_costs)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Run this cell to check your answers. \n", - "# Note the input parameters needed by the function (remember that you can use `Shift + Tab` \n", - "# to see the documentation of the function). \n", - "# If you named your parameters differently, provide the right names to the function\n", - "checker_parameters(el_gen=electricity_generation, \n", - " op_costs=operational_costs, \n", - " capacity=capacity, \n", - " load_factor=load_factor, \n", - " fixed_om_cost=fixed_om_cost, \n", - " fuel_price=fuel_price, \n", - " efficiency=efficiency, \n", - " lifetime=lifetime)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# 3. Now use the earlier LCOE equation we developed to calculate the LCOE\n", - "actual = get_lcoe(capital_costs=capital_costs, \n", - " operational_costs=operational_costs, \n", - " electricity_generation=electricity_generation, \n", - " discount_rate=discount_rate, \n", - " technical_lifetime=lifetime)\n", - "print(actual)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "---\n", - "Now we extend our calculation of LCOE with the parameters used above. We implement the same calculation you did in the `get_lcoe_input`. Then we use this function inside a new function called `extended_lcoe` to calculate the input parameters and the `lcoe` value using our previous definded `get_lcoe` function. You do not need to change anythiing here, just check them below and make sure to run both cells as we will need them in the next steps." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def get_lcoe_input(station_size, overnight_cost, fuel_efficiency, technical_lifetime,\n", - " fuel_price, fixed_om_cost, load_factor, variable_om_cost):\n", - " \"\"\"Calculates the input parameters for the lcoe function\n", - " \n", - " Parameters\n", - " ----------\n", - " station_size : float\n", - " The capacity of the technology in kW\n", - " overnight_cost : float\n", - " The capital cost of the technology in €/kW\n", - " fuel_efficiency : float\n", - " The ratio describing quantity of fuel required per unit of activity\n", - " fuel_price : float\n", - " The price paid per unit of input fuel in €/kWh\n", - " fixed_om_cost : float\n", - " The fixed operation and maintenance cost of the technology in €/kW\n", - " load_factor : float\n", - " The percentage of the year in which the technology generates electricity in %.\n", - " variable_om_cost : float\n", - " The variable operation and maintenance cost of the technology in €/kW\n", - " \n", - " Returns\n", - " -------\n", - " dict\n", - " A dictionary containing:\n", - " capital_cost : float \n", - " The capital cost of the technology €\n", - " total_fixed_om_cost : float\n", - " Fixed costs €\n", - " annual_electricity_generation : np.ndarray\n", - " annual electricity production kWh\n", - " total_variable_om_cost : float\n", - " variable cost due to fuel use €\n", - " annual_operational_cost : np.ndarray\n", - " annual operational cost €\n", - " \n", - " \"\"\"\n", - " HOURS_IN_YEAR = 8760\n", - " \n", - " capital_cost = station_size * overnight_cost\n", - " total_fixed_om_cost = station_size * fixed_om_cost\n", - " annual_electricity_generation = np.repeat(station_size * HOURS_IN_YEAR * load_factor, technical_lifetime)\n", - "\n", - " total_variable_om_cost = ((annual_electricity_generation / fuel_efficiency) * fuel_price) + \\\n", - " (annual_electricity_generation * variable_om_cost)\n", - " annual_operational_cost = total_fixed_om_cost + total_variable_om_cost\n", - " \n", - " return {'capital_cost': capital_cost, 'total_fixed_om_cost': total_fixed_om_cost,\n", - " 'annual_electricity_generation': annual_electricity_generation, \n", - " 'total_variable_om_cost': total_variable_om_cost, 'annual_operational_cost': annual_operational_cost}" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def extended_lcoe(station_size, overnight_cost, fuel_efficiency, fuel_price, fixed_om_cost, var_om_cost, \n", - " load_factor, discount_rate, technical_lifetime):\n", - " \"\"\"Calculates levelised cost of electricity as a function of useful parameters\n", - " \n", - " Parameters\n", - " ----------\n", - " station_size : float\n", - " The capacity of the technology in kW\n", - " overnight_cost : float\n", - " The capital cost of the technology in €\n", - " fuel_efficiency : float\n", - " The ratio describing quantity of fuel required per unit of activity\n", - " fuel_price : float\n", - " The price paid per unit of input fuel in €/kWh\n", - " fixed_om_cost : float\n", - " The fixed operation and maintenance cost of the technology in €/kW\n", - " load_factor : float\n", - " The percentage of the year in which the technology generates electricity in %.\n", - " discount_rate : float\n", - " A decimal value less than 1\n", - " technical_lifetime : int\n", - " Technical lifetime of the technology in years\n", - " var_om_cost : float\n", - " Variable operational and maintenance cost\n", - " \n", - " Returns\n", - " -------\n", - " float\n", - " The levelised cost of electricity in €/kWh\n", - " \"\"\"\n", - " lcoe_params = get_lcoe_input(station_size, overnight_cost, fuel_efficiency, technical_lifetime,\n", - " fuel_price, fixed_om_cost, load_factor, var_om_cost)\n", - "\n", - " value = get_lcoe(lcoe_params['capital_cost'], lcoe_params['annual_operational_cost'], \n", - " lcoe_params['annual_electricity_generation'], discount_rate, \n", - " technical_lifetime)\n", - " return value" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "So to perform the calculation again using our new LCOE function, we get:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "ccgt_lcoe = extended_lcoe(station_size=capacity, \n", - " overnight_cost=overnight_cost, \n", - " fuel_efficiency=efficiency, \n", - " fuel_price=fuel_price, \n", - " fixed_om_cost=fixed_om_cost,\n", - " var_om_cost=0, \n", - " load_factor=load_factor, \n", - " discount_rate=discount_rate, \n", - " technical_lifetime=lifetime)\n", - "ccgt_lcoe" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Q. What are the key parameters that determine the LCOE of the CCGT plant?\n", - "\n", - "Run the next cell to create an interactive slider widget which allows you to play with the `extended_lcoe` function and answer the following questions:\n", - "\n", - "1. Which is the most influential input parameter?\n", - "2. Are all the responses linear? Is it easy to tell?\n", - "3. What happens if gas is expensive?\n", - "4. If the price of the gas turbine doubles from €750/kW to €1500/kW, what's the effect upon LCOE at different load factors?\n", - "5. How important is discount rate compared to the fuel price?" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "interact(extended_lcoe, \n", - " station_size=(100000,750000, 10000), \n", - " overnight_cost=(300, 2000), \n", - " fuel_efficiency=(0.3, 0.7, 0.01), \n", - " fuel_price=(0.00 , 0.20, 0.01), \n", - " fixed_om_cost=(1, 50, 0.001), \n", - " load_factor=(0.01, 1.0, 0.01), \n", - " discount_rate=(0.01, 0.30, 0.01), \n", - " technical_lifetime=(10, 60, 1), \n", - " var_om_cost=(0.0, 0.011, 0.001))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Stage 2 - Summary\n", - "\n", - "- In this stage, we calculated the levelised cost for a CCGT plant.\n", - "- Then, we explored the LCOE equation interactively to try to understand what parameters influence the LCOE of a technology.\n", - "\n", - "In the next stage, we compare LCOE across technologies." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Stage 3: Comparing technologies\n", - "\n", - "In the previous stages, we developed our understanding of the LCOE equation, and applied it to one technology. Now comes the *really* fun part, where we develop a comparison across different technologies." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can now make a more advanced function which holds the logic to calculate LCOE when we have a number of extra input parameters. Now, we can pass a *list* (elements separated by commas inside square brackets) of parameter values, and the function returns a list of results.\n", - "\n", - "You don't need to change anything in the following functions, just check them and run them." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def lcoe_params(station_sizes, overnight_costs, fixed_om_costs, load_factors, efficiencies, \n", - " fuel_prices, discount_rates, technical_lifetimes, technology, var_om_costs):\n", - " \"\"\"Calculate LCOE for cartesian product of all parameter lists\n", - " \"\"\"\n", - " results = [] # Create a list to hold the observations\n", - " \n", - " # Loop over cartesian product of parameter values\n", - " for (capacity, capex, fixed_om_cost, load_factor, efficiency, \n", - " fuel_price, discount_rate, technical_lifetime, variable_om_cost) in itertools.product(\n", - " station_sizes, overnight_costs, fixed_om_costs, load_factors, \n", - " efficiencies, fuel_prices, discount_rates, technical_lifetimes, var_om_costs):\n", - "\n", - " observation = {} # Create a dictionary to store the observation\n", - " \n", - " lcoe_params = get_lcoe_input(capacity, capex, efficiency, technical_lifetimes,\n", - " fuel_price, fixed_om_cost, load_factor, variable_om_cost)\n", - "\n", - " lcoe = extended_lcoe(capacity, capex, efficiency, fuel_price, fixed_om_cost, variable_om_cost,\n", - " load_factor, discount_rate, technical_lifetime)\n", - " \n", - " observation['Technology'] = technology\n", - " observation['StationSize'] = capacity\n", - " observation['OvernightCost'] = capex\n", - " observation['FixedOMCost'] = fixed_om_cost\n", - " observation['LoadFactor'] = load_factor\n", - " observation['Efficiency'] = efficiency\n", - " observation['FuelPrice'] = fuel_price\n", - " observation['DiscountRate'] = discount_rate\n", - " observation['Lifetime'] = technical_lifetime\n", - " observation['LCOE'] = lcoe\n", - " \n", - " year = np.arange(technical_lifetime)\n", - " df = (1 + discount_rate) ** - year\n", - " \n", - " observation['Fixed OM Cost'] = sum(df * lcoe_params['total_fixed_om_cost']) / \\\n", - " sum(df * lcoe_params['annual_electricity_generation'])\n", - " observation['Variable OM Cost'] = sum(df * lcoe_params['total_variable_om_cost']) / \\\n", - " sum(df * lcoe_params['annual_electricity_generation'])\n", - " observation['Capital Cost'] = lcoe_params['capital_cost'] / \\\n", - " sum(df * lcoe_params['annual_electricity_generation'])\n", - "\n", - " results.append(observation)\n", - " \n", - " return results" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "First, we'll use our new function to compare multiple technologies under their central operating characteristics, and explore how that changes under different discount rates. Run the folowing cell to pass all of the input values for each technology to the function. Give it a minute and you will se a plot below." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Here we create an array of load factors from 0.1 to 1.0 in steps of 0.01\n", - "load_factors = np.arange(0.1, 1.0, 0.01)\n", - "# We set the discount rate to a fixed value for the moment\n", - "discount_rate = [0.05, 0.1] # %\n", - "\n", - "# CCGT plant parameters\n", - "technical_lifetime = [25] # years\n", - "station_size = [750000] # kW\n", - "fuel_price = [0.03] # €/kWh\n", - "overnight_cost = [750] # €/kW\n", - "fixed_om_cost = [3.0] # €/kW\n", - "var_om_cost = [0.01]\n", - "fuel_efficiency = [0.5]\n", - "\n", - "ccgt_results = lcoe_params(\n", - " station_size, overnight_cost, fixed_om_cost, load_factors, fuel_efficiency, \n", - " fuel_price, discount_rate, technical_lifetime, 'CCGT', var_om_cost)\n", - "\n", - "# Coal plants\n", - "technical_lifetime = [35] # years\n", - "station_size = [1000000] # kW\n", - "fuel_price = [0.01] # €/kWh\n", - "overnight_cost = [2000] # €/kW\n", - "fixed_om_cost = [5.0] # €/kW\n", - "var_om_cost = [0.01]\n", - "fuel_efficiency = [0.4]\n", - "\n", - "coal_results = lcoe_params(\n", - " station_size, overnight_cost, fixed_om_cost, load_factors, fuel_efficiency, \n", - " fuel_price, discount_rate, technical_lifetime, 'Coal', var_om_cost)\n", - "\n", - "# Nuclear plants\n", - "technical_lifetime = [50] # years\n", - "station_size = [1200000] # kW\n", - "fuel_price = [0.005] # €/kWh\n", - "overnight_cost = [4500] # €/kW\n", - "fixed_om_cost = [2.0] # €/kW\n", - "var_om_cost = [0.01]\n", - "fuel_efficiency = [0.4]\n", - "\n", - "nuclear_results = lcoe_params(\n", - " station_size, overnight_cost, fixed_om_cost, load_factors, fuel_efficiency, \n", - " fuel_price, discount_rate, technical_lifetime, 'Nuclear', var_om_cost)\n", - "\n", - "# Concentrating solar power\n", - "technical_lifetime = [25] # years\n", - "station_size = [500000] # kW\n", - "fuel_price = [0.0] # €/kWh\n", - "overnight_cost = [3785] # €/kW\n", - "fixed_om_cost = [5.0] # €/kW\n", - "var_om_cost = [0.01]\n", - "fuel_efficiency = [1.0]\n", - "\n", - "csp_results = lcoe_params(\n", - " station_size, overnight_cost, fixed_om_cost, load_factors, fuel_efficiency, \n", - " fuel_price, discount_rate, technical_lifetime, 'CSP', var_om_cost)\n", - "\n", - "# Wind turbines\n", - "technical_lifetime = [25] # years\n", - "station_size = [200000] # kW\n", - "fuel_price = [0.0] # €/kWh\n", - "overnight_cost = [1200] # €/kW\n", - "fixed_om_cost = [2.0] # €/kW\n", - "var_om_cost = [0.0001]\n", - "fuel_efficiency = [1.0]\n", - "\n", - "wind_results = lcoe_params(\n", - " station_size, overnight_cost, fixed_om_cost, load_factors, fuel_efficiency, \n", - " fuel_price, discount_rate, technical_lifetime, 'Wind', var_om_cost)\n", - "\n", - "\n", - "plotting = pd.DataFrame(ccgt_results + coal_results + nuclear_results + csp_results + wind_results)\n", - "px.line(plotting, x='LoadFactor', y='LCOE', color='Technology', facet_col='DiscountRate',\n", - " labels={'LoadFactor': 'LoadFactor (%)', 'LCOE': 'LCOE (€/kW)'})" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "- What do you see when you compare technologies at different load factors?\n", - "- What does the LCOE graph *not* show you?\n", - "\n", - "Run the following cell to check another repreentation of the results:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "df = plotting.melt(id_vars=['Technology', 'LoadFactor', 'DiscountRate'], \n", - " value_vars=['Fixed OM Cost', 'Variable OM Cost', 'Capital Cost'])\n", - "\n", - "px.area(df, x='LoadFactor', y='value', color='variable', facet_col='Technology', facet_row='DiscountRate',\n", - " labels={'LoadFactor': 'LoadFactor (%)', 'value': 'LCOE (€/kW)'})" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "* What can you say about the different costs per technology?\n", - "* Does the wind technology really have the lowest levelised cost of electricity?\n", - "* What happens if the cost of natural gas doubles? What happens if coal doubles in price?\n", - "* What happens if concrete gets cheaper?" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "That is it for today's lab, we hope you enjoyed it and that you got a good idea of what the LCOE is and how to use it." - ] - } - ], - "metadata": { - "interpreter": { - "hash": "af8ebb4e2d22b06da4313823d442847307ae4d45e401ef0669a9fed88d20d2aa" - }, - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "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.8.13" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/MJ2383_Lab_3.ipynb b/OSeMOSYS.ipynb similarity index 99% rename from MJ2383_Lab_3.ipynb rename to OSeMOSYS.ipynb index ca2dc8c..5eeb0f5 100644 --- a/MJ2383_Lab_3.ipynb +++ b/OSeMOSYS.ipynb @@ -16,7 +16,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# MJ2383 Computer Lab 3\n", + "# LIFE Academy Exercise\n", "\n", "This lab aims to provide an inside view on what a supply curve is and how it can be generated while using OSeMOSYS. Reminder, a supply curve is a graphical representation of the law of supply. It slopes upward because the quantity supplied increases as price increases, with other things constant.\n", "\n", @@ -753,7 +753,7 @@ "metadata": { "celltoolbar": "Raw Cell Format", "kernelspec": { - "display_name": "Python 3", + "display_name": "Python 3.9.10 64-bit", "language": "python", "name": "python3" }, @@ -767,7 +767,12 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.8" + "version": "3.9.10" + }, + "vscode": { + "interpreter": { + "hash": "aee8b7b246df8f9039afb4144a1f6fd8d2ca17a180786b69acc140d282b71a49" + } } }, "nbformat": 4, diff --git a/README.rst b/README.rst index fede9b0..f7f605b 100644 --- a/README.rst +++ b/README.rst @@ -1,15 +1,10 @@ -MJ2383 ------- +LIFE Academy Workshop +--------------------- -Lab 1: +OSeMOSYS Exercise .. image:: https://mybinder.org/badge_logo.svg - :target: https://mybinder.org/v2/gh/KTH-dESA/MJ2383/main?filepath=MJ2383_Lab_1.ipynb - -Lab 3: - -.. image:: https://mybinder.org/badge_logo.svg - :target: https://mybinder.org/v2/gh/KTH-dESA/MJ2383/main?filepath=MJ2383_Lab_3.ipynb + :target: https://mybinder.org/v2/gh/KTH-dESA/LIFE/main?filepath=OSeMOSYS.ipynb Developing with Jupyter Notebooks ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -63,7 +58,7 @@ License href="http://purl.org/dc/dcmitype/InteractiveResource" property="dct:title" rel="dct:type"> - MJ2383 Computer Labs + LIFE Academy Workshop by Creative Commons Attribution 4.0 International License.
Based on a work at https://github.com/KTH-dESA/MJ2383 + href="https://github.com/KTH-dESA/LIFE" + rel="dct:source">https://github.com/KTH-dESA/LIFE