├── .bumpversion.cfg
├── .github
└── workflows
│ ├── CI.yml
│ ├── CI_conda.yml
│ └── python-publish.yml
├── .gitignore
├── CITATION.cff
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE.md
├── MANIFEST.in
├── README.md
├── doc
└── images
│ ├── cef_country_map.py
│ ├── cef_country_map.svg
│ ├── cefs_scatter.png
│ ├── elmada_logo.svg
│ ├── elmada_scheme.svg
│ ├── elmada_scheme_scribble.svg
│ ├── merit_order.svg
│ └── scheme_CEF_calculation.svg
├── elmada
├── __init__.py
├── cc_share.py
├── data
│ ├── raw
│ │ ├── destatis
│ │ │ └── energiepreisentwicklung-xlsx-5619001.xls
│ │ ├── sandbag
│ │ │ └── eua-price.csv
│ │ ├── smard
│ │ │ ├── Day-ahead_prices_2015.csv
│ │ │ ├── Day-ahead_prices_2018.csv
│ │ │ └── readme.txt
│ │ ├── tranberg
│ │ │ └── specific_emission_factors.csv
│ │ └── worldbank
│ │ │ ├── Data_Extract_From_World_Development_Indicators
│ │ │ ├── Data.csv
│ │ │ └── Metadata.csv
│ │ │ └── readme.txt
│ └── safe_cache
│ │ ├── 2017_AT_gen_entsoe.parquet
│ │ ├── 2017_AT_installedGen_entsoe.parquet
│ │ ├── 2017_BE_gen_entsoe.parquet
│ │ ├── 2017_BE_installedGen_entsoe.parquet
│ │ ├── 2017_BG_gen_entsoe.parquet
│ │ ├── 2017_CH_gen_entsoe.parquet
│ │ ├── 2017_CZ_gen_entsoe.parquet
│ │ ├── 2017_CZ_installedGen_entsoe.parquet
│ │ ├── 2017_DE_gen_entsoe.parquet
│ │ ├── 2017_DE_installedGen_entsoe.parquet
│ │ ├── 2017_DK_gen_entsoe.parquet
│ │ ├── 2017_DK_installedGen_entsoe.parquet
│ │ ├── 2017_EE_gen_entsoe.parquet
│ │ ├── 2017_ES_gen_entsoe.parquet
│ │ ├── 2017_ES_installedGen_entsoe.parquet
│ │ ├── 2017_FI_gen_entsoe.parquet
│ │ ├── 2017_FI_installedGen_entsoe.parquet
│ │ ├── 2017_FR_gen_entsoe.parquet
│ │ ├── 2017_FR_installedGen_entsoe.parquet
│ │ ├── 2017_GB_gen_entsoe.parquet
│ │ ├── 2017_GB_installedGen_entsoe.parquet
│ │ ├── 2017_GR_gen_entsoe.parquet
│ │ ├── 2017_GR_installedGen_entsoe.parquet
│ │ ├── 2017_HU_gen_entsoe.parquet
│ │ ├── 2017_HU_installedGen_entsoe.parquet
│ │ ├── 2017_IE_gen_entsoe.parquet
│ │ ├── 2017_IE_installedGen_entsoe.parquet
│ │ ├── 2017_IT_gen_entsoe.parquet
│ │ ├── 2017_IT_installedGen_entsoe.parquet
│ │ ├── 2017_LT_gen_entsoe.parquet
│ │ ├── 2017_LT_installedGen_entsoe.parquet
│ │ ├── 2017_LV_gen_entsoe.parquet
│ │ ├── 2017_ME_gen_entsoe.parquet
│ │ ├── 2017_MK_gen_entsoe.parquet
│ │ ├── 2017_NL_gen_entsoe.parquet
│ │ ├── 2017_NL_installedGen_entsoe.parquet
│ │ ├── 2017_NO_gen_entsoe.parquet
│ │ ├── 2017_PL_gen_entsoe.parquet
│ │ ├── 2017_PL_installedGen_entsoe.parquet
│ │ ├── 2017_PT_gen_entsoe.parquet
│ │ ├── 2017_PT_installedGen_entsoe.parquet
│ │ ├── 2017_RO_gen_entsoe.parquet
│ │ ├── 2017_RO_installedGen_entsoe.parquet
│ │ ├── 2017_RS_gen_entsoe.parquet
│ │ ├── 2017_RS_installedGen_entsoe.parquet
│ │ ├── 2017_SE_gen_entsoe.parquet
│ │ ├── 2017_SI_gen_entsoe.parquet
│ │ ├── 2017_SI_installedGen_entsoe.parquet
│ │ ├── 2017_SK_gen_entsoe.parquet
│ │ ├── 2018_AT_gen_entsoe.parquet
│ │ ├── 2018_AT_installedGen_entsoe.parquet
│ │ ├── 2018_BA_gen_entsoe.parquet
│ │ ├── 2018_BE_gen_entsoe.parquet
│ │ ├── 2018_BE_installedGen_entsoe.parquet
│ │ ├── 2018_BG_gen_entsoe.parquet
│ │ ├── 2018_CH_gen_entsoe.parquet
│ │ ├── 2018_CZ_gen_entsoe.parquet
│ │ ├── 2018_CZ_installedGen_entsoe.parquet
│ │ ├── 2018_DE_gen_entsoe.parquet
│ │ ├── 2018_DE_installedGen_entsoe.parquet
│ │ ├── 2018_DK_gen_entsoe.parquet
│ │ ├── 2018_DK_installedGen_entsoe.parquet
│ │ ├── 2018_ES_gen_entsoe.parquet
│ │ ├── 2018_ES_installedGen_entsoe.parquet
│ │ ├── 2018_FI_gen_entsoe.parquet
│ │ ├── 2018_FI_installedGen_entsoe.parquet
│ │ ├── 2018_FR_gen_entsoe.parquet
│ │ ├── 2018_FR_installedGen_entsoe.parquet
│ │ ├── 2018_GB_gen_entsoe.parquet
│ │ ├── 2018_GB_installedGen_entsoe.parquet
│ │ ├── 2018_GR_gen_entsoe.parquet
│ │ ├── 2018_GR_installedGen_entsoe.parquet
│ │ ├── 2018_HU_gen_entsoe.parquet
│ │ ├── 2018_HU_installedGen_entsoe.parquet
│ │ ├── 2018_IE_gen_entsoe.parquet
│ │ ├── 2018_IE_installedGen_entsoe.parquet
│ │ ├── 2018_IT_gen_entsoe.parquet
│ │ ├── 2018_IT_installedGen_entsoe.parquet
│ │ ├── 2018_LT_gen_entsoe.parquet
│ │ ├── 2018_LT_installedGen_entsoe.parquet
│ │ ├── 2018_LV_gen_entsoe.parquet
│ │ ├── 2018_MK_gen_entsoe.parquet
│ │ ├── 2018_NL_gen_entsoe.parquet
│ │ ├── 2018_NL_installedGen_entsoe.parquet
│ │ ├── 2018_NO_gen_entsoe.parquet
│ │ ├── 2018_PL_gen_entsoe.parquet
│ │ ├── 2018_PL_installedGen_entsoe.parquet
│ │ ├── 2018_PT_gen_entsoe.parquet
│ │ ├── 2018_PT_installedGen_entsoe.parquet
│ │ ├── 2018_RO_gen_entsoe.parquet
│ │ ├── 2018_RO_installedGen_entsoe.parquet
│ │ ├── 2018_RS_gen_entsoe.parquet
│ │ ├── 2018_RS_installedGen_entsoe.parquet
│ │ ├── 2018_SE_gen_entsoe.parquet
│ │ ├── 2018_SI_gen_entsoe.parquet
│ │ ├── 2018_SI_installedGen_entsoe.parquet
│ │ ├── 2018_SK_gen_entsoe.parquet
│ │ ├── 2019_AT_gen_entsoe.parquet
│ │ ├── 2019_AT_installedGen_entsoe.parquet
│ │ ├── 2019_BA_gen_entsoe.parquet
│ │ ├── 2019_BE_gen_entsoe.parquet
│ │ ├── 2019_BE_installedGen_entsoe.parquet
│ │ ├── 2019_BG_gen_entsoe.parquet
│ │ ├── 2019_CH_gen_entsoe.parquet
│ │ ├── 2019_CZ_gen_entsoe.parquet
│ │ ├── 2019_CZ_installedGen_entsoe.parquet
│ │ ├── 2019_DE_gen_entsoe.parquet
│ │ ├── 2019_DE_installedGen_entsoe.parquet
│ │ ├── 2019_DK_gen_entsoe.parquet
│ │ ├── 2019_DK_installedGen_entsoe.parquet
│ │ ├── 2019_EE_gen_entsoe.parquet
│ │ ├── 2019_ES_gen_entsoe.parquet
│ │ ├── 2019_ES_installedGen_entsoe.parquet
│ │ ├── 2019_FI_gen_entsoe.parquet
│ │ ├── 2019_FI_installedGen_entsoe.parquet
│ │ ├── 2019_FR_gen_entsoe.parquet
│ │ ├── 2019_FR_installedGen_entsoe.parquet
│ │ ├── 2019_GB_gen_entsoe.parquet
│ │ ├── 2019_GB_installedGen_entsoe.parquet
│ │ ├── 2019_GR_gen_entsoe.parquet
│ │ ├── 2019_GR_installedGen_entsoe.parquet
│ │ ├── 2019_HU_gen_entsoe.parquet
│ │ ├── 2019_HU_installedGen_entsoe.parquet
│ │ ├── 2019_IE_gen_entsoe.parquet
│ │ ├── 2019_IE_installedGen_entsoe.parquet
│ │ ├── 2019_IT_gen_entsoe.parquet
│ │ ├── 2019_IT_installedGen_entsoe.parquet
│ │ ├── 2019_LT_gen_entsoe.parquet
│ │ ├── 2019_LT_installedGen_entsoe.parquet
│ │ ├── 2019_LV_gen_entsoe.parquet
│ │ ├── 2019_ME_gen_entsoe.parquet
│ │ ├── 2019_MK_gen_entsoe.parquet
│ │ ├── 2019_NL_gen_entsoe.parquet
│ │ ├── 2019_NL_installedGen_entsoe.parquet
│ │ ├── 2019_NO_gen_entsoe.parquet
│ │ ├── 2019_PL_gen_entsoe.parquet
│ │ ├── 2019_PL_installedGen_entsoe.parquet
│ │ ├── 2019_PT_gen_entsoe.parquet
│ │ ├── 2019_PT_installedGen_entsoe.parquet
│ │ ├── 2019_RO_gen_entsoe.parquet
│ │ ├── 2019_RO_installedGen_entsoe.parquet
│ │ ├── 2019_RS_gen_entsoe.parquet
│ │ ├── 2019_RS_installedGen_entsoe.parquet
│ │ ├── 2019_SE_gen_entsoe.parquet
│ │ ├── 2019_SI_gen_entsoe.parquet
│ │ ├── 2019_SI_installedGen_entsoe.parquet
│ │ ├── 2019_SK_gen_entsoe.parquet
│ │ ├── 2020_AT_gen_entsoe.parquet
│ │ ├── 2020_AT_installedGen_entsoe.parquet
│ │ ├── 2020_BE_gen_entsoe.parquet
│ │ ├── 2020_BE_installedGen_entsoe.parquet
│ │ ├── 2020_CZ_gen_entsoe.parquet
│ │ ├── 2020_CZ_installedGen_entsoe.parquet
│ │ ├── 2020_DE_gen_entsoe.parquet
│ │ ├── 2020_DE_installedGen_entsoe.parquet
│ │ ├── 2020_DK_gen_entsoe.parquet
│ │ ├── 2020_DK_installedGen_entsoe.parquet
│ │ ├── 2020_ES_gen_entsoe.parquet
│ │ ├── 2020_ES_installedGen_entsoe.parquet
│ │ ├── 2020_FI_gen_entsoe.parquet
│ │ ├── 2020_FI_installedGen_entsoe.parquet
│ │ ├── 2020_FR_gen_entsoe.parquet
│ │ ├── 2020_FR_installedGen_entsoe.parquet
│ │ ├── 2020_GB_gen_entsoe.parquet
│ │ ├── 2020_GB_installedGen_entsoe.parquet
│ │ ├── 2020_GR_gen_entsoe.parquet
│ │ ├── 2020_GR_installedGen_entsoe.parquet
│ │ ├── 2020_HU_gen_entsoe.parquet
│ │ ├── 2020_HU_installedGen_entsoe.parquet
│ │ ├── 2020_IE_gen_entsoe.parquet
│ │ ├── 2020_IE_installedGen_entsoe.parquet
│ │ ├── 2020_IT_gen_entsoe.parquet
│ │ ├── 2020_IT_installedGen_entsoe.parquet
│ │ ├── 2020_LT_gen_entsoe.parquet
│ │ ├── 2020_LT_installedGen_entsoe.parquet
│ │ ├── 2020_NL_gen_entsoe.parquet
│ │ ├── 2020_NL_installedGen_entsoe.parquet
│ │ ├── 2020_PL_gen_entsoe.parquet
│ │ ├── 2020_PL_installedGen_entsoe.parquet
│ │ ├── 2020_PT_gen_entsoe.parquet
│ │ ├── 2020_PT_installedGen_entsoe.parquet
│ │ ├── 2020_RO_gen_entsoe.parquet
│ │ ├── 2020_RO_installedGen_entsoe.parquet
│ │ ├── 2020_RS_gen_entsoe.parquet
│ │ ├── 2020_RS_installedGen_entsoe.parquet
│ │ ├── 2020_SI_gen_entsoe.parquet
│ │ ├── 2020_SI_installedGen_entsoe.parquet
│ │ ├── GEO-database.parquet
│ │ ├── OPSD_conventional_power_plants_DE.csv
│ │ ├── QUANDL_eua_prices.parquet
│ │ ├── geo_list_coal.parquet
│ │ ├── geo_list_gas.parquet
│ │ ├── geo_list_nuclear.parquet
│ │ ├── share_ccgt_wiki.parquet
│ │ ├── transmission_efficiencies.parquet
│ │ └── units_of_geo_list.parquet
├── eu_pwl.py
├── exceptions.py
├── from_entsoe.py
├── from_geo_scraped.py
├── from_geo_via_morph.py
├── from_opsd.py
├── from_other.py
├── from_smard.py
├── helper.py
├── main.py
├── mappings.py
├── mode.py
├── paths.py
└── plots.py
├── environment.yml
├── paper
├── codemeta.json
├── paper.bib
└── paper.md
├── pyproject.toml
├── pytest.ini
├── setup.py
└── tests
├── __init__.py
├── common
├── CEF_hashes.csv
├── GEN_hashes.csv
├── __init__.py
├── averages.py
├── avgs.csv
├── geo_test_list.html
└── hasher.py
├── test_cc_share.py
├── test_data.py
├── test_eu_pwl.py
├── test_from_entsoe.py
├── test_from_geo_scraped.py
├── test_from_geo_via_morph.py
├── test_from_opsd.py
├── test_from_other.py
├── test_from_smard.py
├── test_helper.py
├── test_main.py
├── test_mode.py
└── test_plots.py
/.bumpversion.cfg:
--------------------------------------------------------------------------------
1 | [bumpversion]
2 | current_version = 0.1.1
3 | commit = True
4 | tag = True
5 |
6 | [bumpversion:file:setup.py]
7 |
8 | [bumpversion:file:elmada/__init__.py]
9 |
--------------------------------------------------------------------------------
/.github/workflows/CI.yml:
--------------------------------------------------------------------------------
1 | name: CI with pip
2 |
3 | on: push
4 |
5 | jobs:
6 | unit-tests:
7 | name: Python ${{ matrix.python-version }}, ${{ matrix.os }}
8 | runs-on: ${{ matrix.os }}
9 | strategy:
10 | fail-fast: false
11 | matrix:
12 | os: ["ubuntu-latest"] #, "windows-latest"]
13 | python-version: ["3.11"] # 3.7, 3.8,
14 | steps:
15 | - uses: actions/checkout@v2
16 | - name: Set up Python ${{ matrix.python-version }}
17 | uses: actions/setup-python@v2
18 | with:
19 | python-version: ${{ matrix.python-version }}
20 | - name: Install dependencies
21 | run: |
22 | python -m pip install --upgrade pip
23 | pip install -e .[dev]
24 | - name: Run pytest
25 | env:
26 | ENTSOE_API_KEY: ${{ secrets.ENTSOE_API_KEY }}
27 | MORPH_API_KEY: ${{ secrets.MORPH_API_KEY }}
28 | QUANDL_API_KEY: ${{ secrets.QUANDL_API_KEY }}
29 | shell: bash -l {0}
30 | run: |
31 | pytest -v -m="not apikey" .
32 |
--------------------------------------------------------------------------------
/.github/workflows/CI_conda.yml:
--------------------------------------------------------------------------------
1 | name: CI with conda
2 |
3 | on: push
4 |
5 | jobs:
6 | unit-tests:
7 | name: Python ${{ matrix.python-version }}, ${{ matrix.os }}
8 | runs-on: ${{ matrix.os }}
9 | strategy:
10 | fail-fast: false
11 | matrix:
12 | os: ["ubuntu-latest", "windows-latest"]
13 | python-version: ["3.9", "3.10", "3.11"]
14 | steps:
15 | - uses: actions/checkout@v2
16 | - name: Set up Python ${{ matrix.python-version }}
17 | uses: conda-incubator/setup-miniconda@v2
18 | with:
19 | miniconda-version: 'latest'
20 | environment-file: environment.yml
21 | auto-update-conda: true
22 | python-version: ${{ matrix.python-version }}
23 | auto-activate-base: false
24 | activate-environment: elmada
25 | - name: Run pytest and generate coverage report
26 | env:
27 | ENTSOE_API_KEY: ${{ secrets.ENTSOE_API_KEY }}
28 | MORPH_API_KEY: ${{ secrets.MORPH_API_KEY }}
29 | QUANDL_API_KEY: ${{ secrets.QUANDL_API_KEY }}
30 | shell: bash -l {0}
31 | run: |
32 | pytest -v --cov=elmada --cov-report=xml -m="not apikey" .
33 | - name: Upload coverage report to Codecov
34 | uses: codecov/codecov-action@v1
35 | with:
36 | # token: ${{ secrets.CODECOV_TOKEN }}
37 | fail_ci_if_error: false
38 | verbose: true
39 |
--------------------------------------------------------------------------------
/.github/workflows/python-publish.yml:
--------------------------------------------------------------------------------
1 | # This workflow will upload a Python Package using Twine when a release is created
2 | # For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries
3 |
4 | name: Upload Python Package
5 |
6 | on:
7 | release:
8 | types: [published]
9 |
10 | jobs:
11 | deploy:
12 |
13 | runs-on: ubuntu-latest
14 |
15 | steps:
16 | - uses: actions/checkout@v2
17 | - name: Set up Python
18 | uses: actions/setup-python@v2
19 | with:
20 | python-version: '3.x'
21 | - name: Install dependencies
22 | run: |
23 | python -m pip install --upgrade pip
24 | pip install build
25 | - name: Build package
26 | run: python -m build
27 | - name: Publish package on Test-PyPi
28 | uses: pypa/gh-action-pypi-publish@v1.4.2
29 | with:
30 | user: __token__
31 | password: ${{ secrets.TEST_PYPI_API_TOKEN }}
32 | repository_url: https://test.pypi.org/legacy/
33 | - name: Publish package on PyPi
34 | uses: pypa/gh-action-pypi-publish@v1.4.2
35 | with:
36 | user: __token__
37 | password: ${{ secrets.PYPI_API_TOKEN }}
38 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # symlink to cache directory
2 | cache
3 |
4 | # jupyter notebooks
5 | notebooks/
6 |
7 | # Pycharm settings
8 | .idea/
9 |
10 | # Visual Studio Code settings
11 | .vscode/
12 | *.code
13 |
14 | # Spyder project settings
15 | .spyderproject
16 | .spyproject
17 |
18 | # Byte-compiled / optimized / DLL files
19 | __pycache__/
20 | *.py[cod]
21 | *$py.class
22 |
23 | # C extensions
24 | *.so
25 |
26 | # Distribution / packaging
27 | .Python
28 | build/
29 | develop-eggs/
30 | dist/
31 | downloads/
32 | eggs/
33 | .eggs/
34 | lib/
35 | lib64/
36 | parts/
37 | sdist/
38 | var/
39 | wheels/
40 | share/python-wheels/
41 | *.egg-info/
42 | .installed.cfg
43 | *.egg
44 | MANIFEST
45 |
46 | # PyInstaller
47 | # Usually these files are written by a python script from a template
48 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
49 | *.manifest
50 | *.spec
51 |
52 | # Installer logs
53 | pip-log.txt
54 | pip-delete-this-directory.txt
55 |
56 | # Unit test / coverage reports
57 | htmlcov/
58 | .tox/
59 | .nox/
60 | .coverage
61 | .coverage.*
62 | .cache
63 | nosetests.xml
64 | coverage.xml
65 | *.cover
66 | .hypothesis/
67 | .pytest_cache/
68 |
69 | # Translations
70 | *.mo
71 | *.pot
72 |
73 | # Django stuff:
74 | *.log
75 | local_settings.py
76 | db.sqlite3
77 |
78 | # Flask stuff:
79 | instance/
80 | .webassets-cache
81 |
82 | # Scrapy stuff:
83 | .scrapy
84 |
85 | # Sphinx documentation
86 | docs/_build/
87 |
88 | # PyBuilder
89 | target/
90 |
91 | # Jupyter Notebook
92 | .ipynb_checkpoints
93 |
94 | # IPython
95 | profile_default/
96 | ipython_config.py
97 |
98 | # pyenv
99 | .python-version
100 |
101 | # celery beat schedule file
102 | celerybeat-schedule
103 |
104 | # SageMath parsed files
105 | *.sage.py
106 |
107 | # Environments
108 | .env
109 | .venv
110 | env/
111 | venv/
112 | ENV/
113 | env.bak/
114 | venv.bak/
115 |
116 | # Rope project settings
117 | .ropeproject
118 |
119 | # mkdocs documentation
120 | /site
121 |
122 | # mypy
123 | .mypy_cache/
124 | .dmypy.json
125 | dmypy.json
126 |
127 | # Pyre type checker
128 | .pyre/
129 |
--------------------------------------------------------------------------------
/CITATION.cff:
--------------------------------------------------------------------------------
1 | cff-version: 1.2.0
2 | message: "If you use this software, please cite it as below."
3 | authors:
4 | - family-names: "Fleschutz"
5 | given-names: "Markus"
6 | orcid: "https://orcid.org/0000-0002-8516-9635"
7 | - family-names: "Murphy"
8 | given-names: "Michael D."
9 | orcid: "https://orcid.org/0000-0002-4269-2581"
10 | title: "elmada: Dynamic electricity carbon emission factors and prices for Europe"
11 | version: 0.1.0
12 | doi: 10.5281/zenodo.5566694
13 | date-released: 2021-10-13
14 | url: "https://github.com/DrafProject/elmada"
15 | preferred-citation:
16 | type: article
17 | authors:
18 | - family-names: "Fleschutz"
19 | given-names: "Markus"
20 | orcid: "https://orcid.org/0000-0002-8516-9635"
21 | - family-names: "Murphy"
22 | given-names: "Michael D."
23 | orcid: "https://orcid.org/0000-0002-4269-2581"
24 | doi: "10.21105/joss.03625"
25 | journal: "Journal of Open Source Software"
26 | month: 10
27 | start: 3625
28 | title: "elmada: Dynamic electricity carbon emission factors and prices for Europe"
29 | issue: 66
30 | volume: 6
31 | year: 2021
32 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 |
2 | # Contributor Covenant Code of Conduct
3 |
4 | ## Our Pledge
5 |
6 | We as members, contributors, and leaders pledge to make participation in our
7 | community a harassment-free experience for everyone, regardless of age, body
8 | size, visible or invisible disability, ethnicity, sex characteristics, gender
9 | identity and expression, level of experience, education, socio-economic status,
10 | nationality, personal appearance, race, caste, color, religion, or sexual identity
11 | and orientation.
12 |
13 | We pledge to act and interact in ways that contribute to an open, welcoming,
14 | diverse, inclusive, and healthy community.
15 |
16 | ## Our Standards
17 |
18 | Examples of behavior that contributes to a positive environment for our
19 | community include:
20 |
21 | * Demonstrating empathy and kindness toward other people
22 | * Being respectful of differing opinions, viewpoints, and experiences
23 | * Giving and gracefully accepting constructive feedback
24 | * Accepting responsibility and apologizing to those affected by our mistakes,
25 | and learning from the experience
26 | * Focusing on what is best not just for us as individuals, but for the
27 | overall community
28 |
29 | Examples of unacceptable behavior include:
30 |
31 | * The use of sexualized language or imagery, and sexual attention or
32 | advances of any kind
33 | * Trolling, insulting or derogatory comments, and personal or political attacks
34 | * Public or private harassment
35 | * Publishing others' private information, such as a physical or email
36 | address, without their explicit permission
37 | * Other conduct which could reasonably be considered inappropriate in a
38 | professional setting
39 |
40 | ## Enforcement Responsibilities
41 |
42 | Community leaders are responsible for clarifying and enforcing our standards of
43 | acceptable behavior and will take appropriate and fair corrective action in
44 | response to any behavior that they deem inappropriate, threatening, offensive,
45 | or harmful.
46 |
47 | Community leaders have the right and responsibility to remove, edit, or reject
48 | comments, commits, code, wiki edits, issues, and other contributions that are
49 | not aligned to this Code of Conduct, and will communicate reasons for moderation
50 | decisions when appropriate.
51 |
52 | ## Scope
53 |
54 | This Code of Conduct applies within all community spaces, and also applies when
55 | an individual is officially representing the community in public spaces.
56 | Examples of representing our community include using an official e-mail address,
57 | posting via an official social media account, or acting as an appointed
58 | representative at an online or offline event.
59 |
60 | ## Enforcement
61 |
62 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
63 | reported to the community leaders responsible for enforcement at
64 | [INSERT CONTACT METHOD].
65 | All complaints will be reviewed and investigated promptly and fairly.
66 |
67 | All community leaders are obligated to respect the privacy and security of the
68 | reporter of any incident.
69 |
70 | ## Enforcement Guidelines
71 |
72 | Community leaders will follow these Community Impact Guidelines in determining
73 | the consequences for any action they deem in violation of this Code of Conduct:
74 |
75 | ### 1. Correction
76 |
77 | **Community Impact**: Use of inappropriate language or other behavior deemed
78 | unprofessional or unwelcome in the community.
79 |
80 | **Consequence**: A private, written warning from community leaders, providing
81 | clarity around the nature of the violation and an explanation of why the
82 | behavior was inappropriate. A public apology may be requested.
83 |
84 | ### 2. Warning
85 |
86 | **Community Impact**: A violation through a single incident or series
87 | of actions.
88 |
89 | **Consequence**: A warning with consequences for continued behavior. No
90 | interaction with the people involved, including unsolicited interaction with
91 | those enforcing the Code of Conduct, for a specified period of time. This
92 | includes avoiding interactions in community spaces as well as external channels
93 | like social media. Violating these terms may lead to a temporary or
94 | permanent ban.
95 |
96 | ### 3. Temporary Ban
97 |
98 | **Community Impact**: A serious violation of community standards, including
99 | sustained inappropriate behavior.
100 |
101 | **Consequence**: A temporary ban from any sort of interaction or public
102 | communication with the community for a specified period of time. No public or
103 | private interaction with the people involved, including unsolicited interaction
104 | with those enforcing the Code of Conduct, is allowed during this period.
105 | Violating these terms may lead to a permanent ban.
106 |
107 | ### 4. Permanent Ban
108 |
109 | **Community Impact**: Demonstrating a pattern of violation of community
110 | standards, including sustained inappropriate behavior, harassment of an
111 | individual, or aggression toward or disparagement of classes of individuals.
112 |
113 | **Consequence**: A permanent ban from any sort of public interaction within
114 | the community.
115 |
116 | ## Attribution
117 |
118 | This Code of Conduct is adapted from the [Contributor Covenant][homepage],
119 | version 2.0, available at
120 | [https://www.contributor-covenant.org/version/2/0/code_of_conduct.html][v2.0].
121 |
122 | Community Impact Guidelines were inspired by
123 | [Mozilla's code of conduct enforcement ladder][Mozilla CoC].
124 |
125 | For answers to common questions about this code of conduct, see the FAQ at
126 | [https://www.contributor-covenant.org/faq][FAQ]. Translations are available
127 | at [https://www.contributor-covenant.org/translations][translations].
128 |
129 | [homepage]: https://www.contributor-covenant.org
130 | [v2.0]: https://www.contributor-covenant.org/version/2/0/code_of_conduct.html
131 | [Mozilla CoC]: https://github.com/mozilla/diversity
132 | [FAQ]: https://www.contributor-covenant.org/faq
133 | [translations]: https://www.contributor-covenant.org/translations
134 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # How to contribute
2 |
3 | To guarantee the further evolution of elmada in the long term, we depend on the support of volunteer developers.
4 |
5 | Some of the resources to look at if you're interested in contributing:
6 | * [Join us on Gitter to chat!](https://gitter.im/DrafProject/elmada)
7 |
8 | ## Licensing
9 |
10 | By contributing to elmada, e.g. through opening a pull request or submitting a patch, you represent that your contributions are your own original work and that you have the right to license them, and you agree that your contributions are licensed under the LGPL 3 license.
11 |
12 | ## Submitting bug reports
13 |
14 | [Open an issue on GitHub](https://github.com/DrafProject/elmada/issues/new) to report bugs or other problems.
15 |
16 | ## Submitting changes
17 |
18 | To contribute changes:
19 |
20 | 1. Fork the project on GitHub
21 | 1. Create a feature branch to work on in your fork (``git checkout -b new-fix-or-feature``)
22 | 1. Commit your changes to the feature branch after running black to format your code
23 | 1. Push the branch to GitHub (``git push origin new-fix-or-feature``)
24 | 1. On GitHub, create a new [pull request](https://github.com/DrafProject/elmada/pull/new/master) from the feature branch
25 |
26 | ### Pull requests
27 |
28 | Before submitting a pull request, check whether you have:
29 |
30 | * Added or updated documentation for your changes
31 | * Added tests if you implemented new functionality
32 |
33 | When opening a pull request, please provide a clear summary of your changes!
34 |
35 | ### Commit messages
36 |
37 | Please try to write clear commit messages. One-line messages are fine for small changes, but bigger changes should look like this:
38 |
39 | A brief summary of the commit
40 |
41 | A paragraph or bullet-point list describing what changed and its impact,
42 | covering as many lines as needed.
43 |
44 | ## Testing
45 |
46 | We have existing test coverage for the key functionality of elmada.
47 |
48 | All tests are in the ``elmada/tests`` directory and use [pytest](https://docs.pytest.org/en/latest/).
49 |
50 | Our test coverage is not perfect. An easy way to contribute code is to work on better tests.
51 |
52 | ## Coding conventions
53 |
54 | Start reading our code and you'll get the hang of it.
55 |
56 | We mostly follow the official [Style Guide for Python Code (PEP8)](https://www.python.org/dev/peps/pep-0008/).
57 |
58 | We have chosen to use the uncompromising code formatter, [`black`](https://github.com/psf/black/).
59 | If run from the root directory of this repo, `pyproject.toml` should ensure the line lengths are restricted to 100.
60 | The philosophy behind using black is to have uniform style throughout the project dictated by code.
61 | Since `black` is designed to minimise diffs, and make patches more human readable, this also makes code reviews more efficient.
62 |
63 | ## Attribution
64 |
65 | The layout and content of this document is based on the contribution guidelines of the projects [OpenGovernment](https://github.com/opengovernment/opengovernment/blob/master/CONTRIBUTING.md) and [Calliope](https://github.com/calliope-project/calliope/blob/master/CONTRIBUTING.md).
66 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | GNU LESSER GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 |
9 | This version of the GNU Lesser General Public License incorporates
10 | the terms and conditions of version 3 of the GNU General Public
11 | License, supplemented by the additional permissions listed below.
12 |
13 | 0. Additional Definitions.
14 |
15 | As used herein, "this License" refers to version 3 of the GNU Lesser
16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU
17 | General Public License.
18 |
19 | "The Library" refers to a covered work governed by this License,
20 | other than an Application or a Combined Work as defined below.
21 |
22 | An "Application" is any work that makes use of an interface provided
23 | by the Library, but which is not otherwise BASEd on the Library.
24 | Defining a subclass of a class defined by the Library is deemed a mode
25 | of using an interface provided by the Library.
26 |
27 | A "Combined Work" is a work produced by combining or linking an
28 | Application with the Library. The particular version of the Library
29 | with which the Combined Work was made is also called the "Linked
30 | Version".
31 |
32 | The "Minimal Corresponding Source" for a Combined Work means the
33 | Corresponding Source for the Combined Work, excluding any source code
34 | for portions of the Combined Work that, considered in isolation, are
35 | BASEd on the Application, and not on the Linked Version.
36 |
37 | The "Corresponding Application Code" for a Combined Work means the
38 | object code and/or source code for the Application, including any data
39 | and utility programs needed for reproducing the Combined Work from the
40 | Application, but excluding the System Libraries of the Combined Work.
41 |
42 | 1. Exception to Section 3 of the GNU GPL.
43 |
44 | You may convey a covered work under sections 3 and 4 of this License
45 | without being bound by section 3 of the GNU GPL.
46 |
47 | 2. Conveying Modified Versions.
48 |
49 | If you modify a copy of the Library, and, in your modifications, a
50 | facility refers to a function or data to be supplied by an Application
51 | that uses the facility (other than as an argument passed when the
52 | facility is invoked), then you may convey a copy of the modified
53 | version:
54 |
55 | a) under this License, provided that you make a good faith effort to
56 | ensure that, in the event an Application does not supply the
57 | function or data, the facility still operates, and performs
58 | whatever part of its purpose remains meaningful, or
59 |
60 | b) under the GNU GPL, with none of the additional permissions of
61 | this License applicable to that copy.
62 |
63 | 3. Object Code Incorporating Material from Library Header Files.
64 |
65 | The object code form of an Application may incorporate material from
66 | a header file that is part of the Library. You may convey such object
67 | code under terms of your choice, provided that, if the incorporated
68 | material is not limited to numerical parameters, data structure
69 | layouts and accessors, or small macros, inline functions and templates
70 | (ten or fewer lines in length), you do both of the following:
71 |
72 | a) Give prominent notice with each copy of the object code that the
73 | Library is used in it and that the Library and its use are
74 | covered by this License.
75 |
76 | b) Accompany the object code with a copy of the GNU GPL and this license
77 | document.
78 |
79 | 4. Combined Works.
80 |
81 | You may convey a Combined Work under terms of your choice that,
82 | taken together, effectively do not restrict modification of the
83 | portions of the Library contained in the Combined Work and reverse
84 | engineering for debugging such modifications, if you also do each of
85 | the following:
86 |
87 | a) Give prominent notice with each copy of the Combined Work that
88 | the Library is used in it and that the Library and its use are
89 | covered by this License.
90 |
91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license
92 | document.
93 |
94 | c) For a Combined Work that displays copyright notices during
95 | execution, include the copyright notice for the Library among
96 | these notices, as well as a reference directing the user to the
97 | copies of the GNU GPL and this license document.
98 |
99 | d) Do one of the following:
100 |
101 | 0) Convey the Minimal Corresponding Source under the terms of this
102 | License, and the Corresponding Application Code in a form
103 | suitable for, and under terms that permit, the user to
104 | recombine or relink the Application with a modified version of
105 | the Linked Version to produce a modified Combined Work, in the
106 | manner specified by section 6 of the GNU GPL for conveying
107 | Corresponding Source.
108 |
109 | 1) Use a suitable shared library mechanism for linking with the
110 | Library. A suitable mechanism is one that (a) uses at run time
111 | a copy of the Library already present on the user's computer
112 | system, and (b) will operate properly with a modified version
113 | of the Library that is interface-compatible with the Linked
114 | Version.
115 |
116 | e) Provide Installation Information, but only if you would otherwise
117 | be required to provide such information under section 6 of the
118 | GNU GPL, and only to the extent that such information is
119 | necessary to install and execute a modified version of the
120 | Combined Work produced by recombining or relinking the
121 | Application with a modified version of the Linked Version. (If
122 | you use option 4d0, the Installation Information must accompany
123 | the Minimal Corresponding Source and Corresponding Application
124 | Code. If you use option 4d1, you must provide the Installation
125 | Information in the manner specified by section 6 of the GNU GPL
126 | for conveying Corresponding Source.)
127 |
128 | 5. Combined Libraries.
129 |
130 | You may place library facilities that are a work BASEd on the
131 | Library side by side in a single library together with other library
132 | facilities that are not Applications and are not covered by this
133 | License, and convey such a combined library under terms of your
134 | choice, if you do both of the following:
135 |
136 | a) Accompany the combined library with a copy of the same work BASEd
137 | on the Library, uncombined with any other library facilities,
138 | conveyed under the terms of this License.
139 |
140 | b) Give prominent notice with the combined library that part of it
141 | is a work BASEd on the Library, and explaining where to find the
142 | accompanying uncombined form of the same work.
143 |
144 | 6. Revised Versions of the GNU Lesser General Public License.
145 |
146 | The Free Software Foundation may publish revised and/or new versions
147 | of the GNU Lesser General Public License from time to time. Such new
148 | versions will be similar in spirit to the present version, but may
149 | differ in detail to address new problems or concerns.
150 |
151 | Each version is given a distinguishing version number. If the
152 | Library as you received it specifies that a certain numbered version
153 | of the GNU Lesser General Public License "or any later version"
154 | applies to it, you have the option of following the terms and
155 | conditions either of that published version or of any later version
156 | published by the Free Software Foundation. If the Library as you
157 | received it does not specify a version number of the GNU Lesser
158 | General Public License, you may choose any version of the GNU Lesser
159 | General Public License ever published by the Free Software Foundation.
160 |
161 | If the Library as you received it specifies that a proxy can decide
162 | whether future versions of the GNU Lesser General Public License shall
163 | apply, that proxy's public statement of acceptance of any version is
164 | permanent authorization for you to choose that version for the
165 | Library.
166 |
--------------------------------------------------------------------------------
/MANIFEST.in:
--------------------------------------------------------------------------------
1 | graft doc/images
2 | graft elmada
3 | include LICENSE.md README.md
4 |
--------------------------------------------------------------------------------
/doc/images/cef_country_map.py:
--------------------------------------------------------------------------------
1 | import elmada
2 |
3 | fig = elmada.plots.cef_country_map(year=2020, method="XEF_EP")
4 | fp = elmada.paths.BASE_DIR.parent / "doc/images/cef_country_map.svg"
5 | fig.write_image(str(fp))
6 |
--------------------------------------------------------------------------------
/doc/images/cefs_scatter.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/doc/images/cefs_scatter.png
--------------------------------------------------------------------------------
/doc/images/elmada_logo.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/elmada/__init__.py:
--------------------------------------------------------------------------------
1 | __title__ = "elmada"
2 | __summary__ = "Dynamic electricity carbon emission factors and prices for Europe"
3 | __uri__ = "https://github.com/DrafProject/elmada"
4 |
5 | __version__ = "0.1.1"
6 |
7 | __author__ = "Markus Fleschutz"
8 | __email__ = "mfleschutz@gmail.com"
9 |
10 | __license__ = "LGPLv3"
11 | __copyright__ = f"Copyright (C) 2021 {__author__}"
12 |
13 | # isort: off
14 |
15 | from .mode import get_mode, set_mode
16 | from .helper import set_api_keys, make_symlink_to_cache
17 |
18 | # isort: on
19 |
20 | from . import (
21 | cc_share,
22 | eu_pwl,
23 | from_entsoe,
24 | from_geo_scraped,
25 | from_geo_via_morph,
26 | from_opsd,
27 | from_other,
28 | from_smard,
29 | helper,
30 | paths,
31 | plots,
32 | )
33 | from .main import (
34 | get_el_national_generation,
35 | get_emissions,
36 | get_merit_order,
37 | get_prices,
38 | get_residual_load,
39 | )
40 |
--------------------------------------------------------------------------------
/elmada/data/raw/destatis/energiepreisentwicklung-xlsx-5619001.xls:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/raw/destatis/energiepreisentwicklung-xlsx-5619001.xls
--------------------------------------------------------------------------------
/elmada/data/raw/smard/readme.txt:
--------------------------------------------------------------------------------
1 | Note from Markus Fleschutz:
2 |
3 | * data downloaded on 2021-06-10
4 | * from https://www.smard.de/en/downloadcenter/download-market-data#!?downloadAttributes=%7B%22selectedCategory%22:3,%22selectedSubCategory%22:8,%22selectedRegion%22:%22DE%22,%22from%22:1420066800000,%22to%22:1451602799999,%22selectedFileType%22:%22CSV%22%7D
5 | * License: CC BY 4.0
--------------------------------------------------------------------------------
/elmada/data/raw/tranberg/specific_emission_factors.csv:
--------------------------------------------------------------------------------
1 | # Specific emission factors per fuel type and country
2 | # Manually scraped from the supplementary material of Tranberg.2019 (https://doi.org/10.1016/j.esr.2019.100367)
3 |
4 | energy_type,AT,BE,BG,CZ,DE,DK,EE,ES,EU28,FI,FR,GB,GR,HU,IE,IT,LT,LV,ME,NL,NO,PL,PT,RO,RS,SE,SI,SK
5 | high-voltage_mix,119,185,606,729,649,425,1030,331,422,259,38.8,800,976,397,509,463,545,516,422,614,9.38,998,354,392,848,17.4,432,214
6 | wind,0.149,0.156,0.149,0.166,0.165,0.126,0.165,0.122,0.142,0.156,0.133,0.142,0.121,0.114,0.116,0.161,0.110,0.159,0.142,0.133,0.120,0.140,0.117,0.192,0.142,0.141,0.142,0.142
7 | nuclear,10.3,10.1,10.1,10.1,9.37,10.3,10.3,10.2,10.3,10.5,10.6,10.3,10.3,10.1,10.3,10.3,10.3,10.3,10.3,10.1,10.3,10.3,10.3,12.3,10.3,10.3,10.1,10.1
8 | geothermal,0.00664,0.00664,0.00664,0.00664,0.00664,0.00664,0.00664,0.00664,0.00664,0.00664,0.00664,0.00664,0.00664,0.00664,0.00664,0.00664,0.00664,0.00664,0.00664,0.00664,0.00664,0.00664,0.00664,0.00664,0.00664,0.00664,0.00664,0.00664
9 | biomass_cogeneration,50.4,50.4,53.4,50.4,50.4,50.4,53.4,50.4,50.5,50.4,50.4,50.4,50.5,50.4,50.4,50.4,53.4,53.4,50.5,50.4,50.5,50.4,50.4,50.4,50.5,50.4,50.4,50.4
10 | hydropower_pumped_storage,445,372,894,1140,958,611,611,539,611,611,70.8,611,1410,611,845,608,1030,611,611,611,35.2,1410,582,622,1210,611,610,678
11 | hydropower_reservoir,0.445,8.13,8.13,44.8,44.8,8.13,8.13,44.8,8.13,44.8,0.445,8.13,8.13,8.13,8.13,0.445,8.13,8.13,8.13,8.13,0.445,8.13,44.8,8.13,0.445,44.8,8.13,44.8
12 | hydropower_run-of-river,0.0253,0.0253,0.0253,0.0253,0.0253,0.0253,0.0253,0.0253,0.0253,0.0253,0.0253,0.0253,0.0253,0.0253,0.0253,0.0253,0.0253,0.0253,0.0253,0.0253,0.0253,0.0253,0.0253,0.0253,0.0253,0.0253,0.0253,0.0253
13 | coal,984,1120,1180,1180,1160,1160,1300,1210,1160,1080,1090,1140,1300,1400,1070,1150,1170,1160,1160,1030,1160,1160,1140,1140,1340,1170,1190,1160
14 | coal_cogeneration,1220,1210,1250,1710,1160,1050,1210,1210,1210,1100,1210,1210,1560,1230,1210,1260,1210,1210,1210,996,1490,1160,1210,1230,1230,1370,1240,1530
15 | gas,613,471,745,696,533,513,513,491,513,837,587,521,681,749,461,531,513,513,513,464,406,513,440,615,513,513,1090,694
16 | gas_cogeneration,527,501,932,835,347,452,419,167,471,528,668,471,167,645,167,493,625,596,471,449,520,540,471,682,805,551,432,649
17 | oil,1150,911,1660,1060,875,1240,1180,864,1010,446,951,1320,990,1130,917,1060,1010,1010,1010,1010,1010,1010,832,997,1010,852,1380,958
18 | oil_cogeneration,957,852,962,1520,678,963,871,933,933,949,768,933,1070,871,933,902,1530,933,933,1070,933,878,609,1250,933,835,871,1390
19 | low-voltage-mix,319,236,669,786,643,390,918,362,440,241,51.2,802,961,484,585,430,725,777,735,607,27.5,1030,396,468,937,39.3,444,455
20 | solar,0.00580,0.00502,0.00423,0.00642,0.00448,0.00349,0.00349,0.00234,0.00349,0.00349,0.00370,0.00349,0.00415,0.00349,0.00349,0.00166,0.00591,0.00349,0.00349,0.00591,0.00349,0.00349,0.00185,0.00478,0.00445,0.00565,0.00453,0.00493
--------------------------------------------------------------------------------
/elmada/data/raw/worldbank/Data_Extract_From_World_Development_Indicators/Metadata.csv:
--------------------------------------------------------------------------------
1 | Code,License Type,Indicator Name,Long definition,Source,Topic,Periodicity,Aggregation method,Statistical concept and methodology,Development relevance,Limitations and exceptions,General comments,License URL
2 | EG.ELC.LOSS.ZS,Use and distribution of these data are subject to IEA terms and conditions.,Electric power transmission and distribution losses (% of output),"Electric power transmission and distribution losses include losses in transmission between sources of supply and points of distribution and in the distribution to consumers, including pilferage.","IEA Statistics © OECD/IEA 2018 (http://www.iea.org/stats/index.asp), subject to https://www.iea.org/t&c/termsandconditions/",Environment: Energy production & use,Annual,Weighted average,Data on electric power production and consumption are collected from national energy agencies by the International Energy Agency (IEA) and adjusted by the IEA to meet international definitions. Electric power transmission and distribution losses percentage of output is the share of electric power transmission and distribution losses to electricity production which is the total number of GWh generated by power plants separated into electricity plants and CHP plants.,"An economy's production and consumption of electricity are basic indicators of its size and level of development. Although a few countries export electric power, most production is for domestic consumption. Expanding the supply of electricity to meet the growing demand of increasingly urbanized and industrialized economies without incurring unacceptable social, economic, and environmental costs is one of the great challenges facing developing countries.
3 |
4 | Modern societies are becoming increasing dependent on reliable and secure electricity supplies to underpin economic growth and community prosperity. This reliance is set to grow as more efficient and less carbon intensive forms of power are developed and deployed to help decarbonize economies. Maintaining reliable and secure electricity services while seeking to rapidly decarbonize power systems is a key challenge for countries throughout the world.
5 |
6 | In developing economies growth in energy use is closely related to growth in the modern sectors - industry, motorized transport, and urban areas - but energy use also reflects climatic, geographic, and economic factors (such as the relative price of energy). Energy use has been growing rapidly in low- and middle-income economies, but high-income economies still use almost five times as much energy on a per capita basis.
7 |
8 | Governments in many countries are increasingly aware of the urgent need to make better use of the world's energy resources. Improved energy efficiency is often the most economic and readily available means of improving energy security and reducing greenhouse gas emissions.","Electricity consumption is equivalent to production less power plants' own use and transmission, distribution, and transformation losses less exports plus imports. It includes consumption by auxiliary stations, losses in transformers that are considered integral parts of those stations, and electricity produced by pumping installations. Where data are available, it covers electricity generated by primary sources of energy - coal, oil, gas, nuclear, hydro, geothermal, wind, tide and wave, and combustible renewables. Neither production nor consumption data capture the reliability of supplies, including breakdowns, load factors, and frequency of outages.",Restricted use: Please contact the International Energy Agency for third-party use of these data.,http://www.iea.org/t&c/termsandconditions
9 |
--------------------------------------------------------------------------------
/elmada/data/raw/worldbank/readme.txt:
--------------------------------------------------------------------------------
1 | Note from Markus Fleschutz:
2 | data downloaded on 2020-05-19 from https://databank.worldbank.org/reports.aspx?source=2&series=EG.ELC.LOSS.ZS by filtering out European countries.
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2017_AT_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2017_AT_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2017_AT_installedGen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2017_AT_installedGen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2017_BE_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2017_BE_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2017_BE_installedGen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2017_BE_installedGen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2017_BG_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2017_BG_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2017_CH_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2017_CH_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2017_CZ_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2017_CZ_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2017_CZ_installedGen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2017_CZ_installedGen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2017_DE_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2017_DE_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2017_DE_installedGen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2017_DE_installedGen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2017_DK_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2017_DK_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2017_DK_installedGen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2017_DK_installedGen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2017_EE_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2017_EE_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2017_ES_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2017_ES_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2017_ES_installedGen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2017_ES_installedGen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2017_FI_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2017_FI_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2017_FI_installedGen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2017_FI_installedGen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2017_FR_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2017_FR_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2017_FR_installedGen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2017_FR_installedGen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2017_GB_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2017_GB_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2017_GB_installedGen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2017_GB_installedGen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2017_GR_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2017_GR_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2017_GR_installedGen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2017_GR_installedGen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2017_HU_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2017_HU_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2017_HU_installedGen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2017_HU_installedGen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2017_IE_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2017_IE_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2017_IE_installedGen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2017_IE_installedGen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2017_IT_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2017_IT_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2017_IT_installedGen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2017_IT_installedGen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2017_LT_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2017_LT_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2017_LT_installedGen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2017_LT_installedGen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2017_LV_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2017_LV_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2017_ME_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2017_ME_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2017_MK_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2017_MK_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2017_NL_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2017_NL_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2017_NL_installedGen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2017_NL_installedGen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2017_NO_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2017_NO_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2017_PL_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2017_PL_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2017_PL_installedGen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2017_PL_installedGen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2017_PT_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2017_PT_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2017_PT_installedGen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2017_PT_installedGen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2017_RO_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2017_RO_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2017_RO_installedGen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2017_RO_installedGen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2017_RS_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2017_RS_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2017_RS_installedGen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2017_RS_installedGen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2017_SE_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2017_SE_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2017_SI_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2017_SI_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2017_SI_installedGen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2017_SI_installedGen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2017_SK_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2017_SK_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2018_AT_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2018_AT_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2018_AT_installedGen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2018_AT_installedGen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2018_BA_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2018_BA_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2018_BE_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2018_BE_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2018_BE_installedGen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2018_BE_installedGen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2018_BG_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2018_BG_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2018_CH_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2018_CH_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2018_CZ_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2018_CZ_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2018_CZ_installedGen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2018_CZ_installedGen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2018_DE_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2018_DE_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2018_DE_installedGen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2018_DE_installedGen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2018_DK_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2018_DK_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2018_DK_installedGen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2018_DK_installedGen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2018_ES_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2018_ES_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2018_ES_installedGen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2018_ES_installedGen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2018_FI_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2018_FI_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2018_FI_installedGen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2018_FI_installedGen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2018_FR_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2018_FR_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2018_FR_installedGen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2018_FR_installedGen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2018_GB_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2018_GB_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2018_GB_installedGen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2018_GB_installedGen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2018_GR_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2018_GR_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2018_GR_installedGen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2018_GR_installedGen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2018_HU_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2018_HU_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2018_HU_installedGen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2018_HU_installedGen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2018_IE_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2018_IE_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2018_IE_installedGen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2018_IE_installedGen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2018_IT_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2018_IT_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2018_IT_installedGen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2018_IT_installedGen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2018_LT_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2018_LT_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2018_LT_installedGen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2018_LT_installedGen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2018_LV_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2018_LV_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2018_MK_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2018_MK_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2018_NL_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2018_NL_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2018_NL_installedGen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2018_NL_installedGen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2018_NO_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2018_NO_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2018_PL_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2018_PL_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2018_PL_installedGen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2018_PL_installedGen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2018_PT_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2018_PT_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2018_PT_installedGen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2018_PT_installedGen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2018_RO_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2018_RO_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2018_RO_installedGen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2018_RO_installedGen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2018_RS_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2018_RS_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2018_RS_installedGen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2018_RS_installedGen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2018_SE_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2018_SE_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2018_SI_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2018_SI_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2018_SI_installedGen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2018_SI_installedGen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2018_SK_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2018_SK_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2019_AT_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2019_AT_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2019_AT_installedGen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2019_AT_installedGen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2019_BA_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2019_BA_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2019_BE_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2019_BE_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2019_BE_installedGen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2019_BE_installedGen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2019_BG_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2019_BG_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2019_CH_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2019_CH_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2019_CZ_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2019_CZ_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2019_CZ_installedGen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2019_CZ_installedGen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2019_DE_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2019_DE_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2019_DE_installedGen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2019_DE_installedGen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2019_DK_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2019_DK_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2019_DK_installedGen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2019_DK_installedGen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2019_EE_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2019_EE_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2019_ES_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2019_ES_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2019_ES_installedGen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2019_ES_installedGen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2019_FI_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2019_FI_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2019_FI_installedGen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2019_FI_installedGen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2019_FR_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2019_FR_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2019_FR_installedGen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2019_FR_installedGen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2019_GB_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2019_GB_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2019_GB_installedGen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2019_GB_installedGen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2019_GR_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2019_GR_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2019_GR_installedGen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2019_GR_installedGen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2019_HU_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2019_HU_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2019_HU_installedGen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2019_HU_installedGen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2019_IE_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2019_IE_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2019_IE_installedGen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2019_IE_installedGen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2019_IT_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2019_IT_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2019_IT_installedGen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2019_IT_installedGen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2019_LT_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2019_LT_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2019_LT_installedGen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2019_LT_installedGen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2019_LV_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2019_LV_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2019_ME_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2019_ME_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2019_MK_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2019_MK_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2019_NL_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2019_NL_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2019_NL_installedGen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2019_NL_installedGen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2019_NO_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2019_NO_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2019_PL_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2019_PL_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2019_PL_installedGen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2019_PL_installedGen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2019_PT_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2019_PT_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2019_PT_installedGen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2019_PT_installedGen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2019_RO_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2019_RO_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2019_RO_installedGen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2019_RO_installedGen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2019_RS_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2019_RS_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2019_RS_installedGen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2019_RS_installedGen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2019_SE_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2019_SE_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2019_SI_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2019_SI_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2019_SI_installedGen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2019_SI_installedGen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2019_SK_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2019_SK_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2020_AT_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2020_AT_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2020_AT_installedGen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2020_AT_installedGen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2020_BE_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2020_BE_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2020_BE_installedGen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2020_BE_installedGen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2020_CZ_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2020_CZ_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2020_CZ_installedGen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2020_CZ_installedGen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2020_DE_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2020_DE_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2020_DE_installedGen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2020_DE_installedGen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2020_DK_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2020_DK_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2020_DK_installedGen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2020_DK_installedGen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2020_ES_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2020_ES_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2020_ES_installedGen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2020_ES_installedGen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2020_FI_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2020_FI_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2020_FI_installedGen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2020_FI_installedGen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2020_FR_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2020_FR_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2020_FR_installedGen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2020_FR_installedGen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2020_GB_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2020_GB_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2020_GB_installedGen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2020_GB_installedGen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2020_GR_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2020_GR_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2020_GR_installedGen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2020_GR_installedGen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2020_HU_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2020_HU_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2020_HU_installedGen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2020_HU_installedGen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2020_IE_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2020_IE_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2020_IE_installedGen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2020_IE_installedGen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2020_IT_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2020_IT_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2020_IT_installedGen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2020_IT_installedGen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2020_LT_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2020_LT_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2020_LT_installedGen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2020_LT_installedGen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2020_NL_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2020_NL_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2020_NL_installedGen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2020_NL_installedGen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2020_PL_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2020_PL_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2020_PL_installedGen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2020_PL_installedGen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2020_PT_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2020_PT_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2020_PT_installedGen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2020_PT_installedGen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2020_RO_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2020_RO_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2020_RO_installedGen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2020_RO_installedGen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2020_RS_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2020_RS_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2020_RS_installedGen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2020_RS_installedGen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2020_SI_gen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2020_SI_gen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/2020_SI_installedGen_entsoe.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/2020_SI_installedGen_entsoe.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/GEO-database.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/GEO-database.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/QUANDL_eua_prices.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/QUANDL_eua_prices.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/geo_list_coal.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/geo_list_coal.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/geo_list_gas.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/geo_list_gas.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/geo_list_nuclear.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/geo_list_nuclear.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/share_ccgt_wiki.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/share_ccgt_wiki.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/transmission_efficiencies.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/transmission_efficiencies.parquet
--------------------------------------------------------------------------------
/elmada/data/safe_cache/units_of_geo_list.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/elmada/data/safe_cache/units_of_geo_list.parquet
--------------------------------------------------------------------------------
/elmada/eu_pwl.py:
--------------------------------------------------------------------------------
1 | import logging
2 | from functools import lru_cache
3 | from typing import Any, Callable, Dict, Iterable, List, Optional, Tuple, Union
4 |
5 | import pandas as pd
6 |
7 | from elmada import cc_share, from_entsoe, from_geo_scraped, from_opsd, from_other
8 | from elmada import mappings as mp
9 |
10 | logger = logging.getLogger(__name__)
11 | logger.setLevel(level=logging.WARN)
12 |
13 |
14 | def prep_prices(year=2019, freq="60min", country="DE", **kwargs) -> pd.Series:
15 | return prep_CEFs(year=year, freq=freq, country=country, **kwargs)["marginal_cost"]
16 |
17 |
18 | def prep_CEFs(
19 | year: int = 2019,
20 | freq: str = "60min",
21 | country: str = "DE",
22 | validation_mode: bool = False,
23 | mo_P: Optional[pd.DataFrame] = None,
24 | **mo_kwargs,
25 | ) -> pd.DataFrame:
26 | """Prepares XEFs for European countries with piece-wise-linear approximation method"""
27 |
28 | if mo_P is None:
29 | mo_P = merit_order(year=year, country=country, validation_mode=validation_mode, **mo_kwargs)
30 | return from_opsd.get_CEFs_from_merit_order(mo_P=mo_P, year=year, freq=freq, country=country)
31 |
32 |
33 | def merit_order(
34 | year: int = 2019,
35 | country: str = "DE",
36 | approx_method: str = "regr",
37 | validation_mode: bool = False,
38 | overwrite_carbon_tax: Optional[float] = None,
39 | pp_size_method: str = "from_geo_scraped",
40 | ) -> pd.DataFrame:
41 | """Return a merit-order list. Virtual power plants are constructed through discretization."""
42 |
43 | mo_f = merit_order_per_fuel(
44 | year=year, country=country, approx_method=approx_method, validation_mode=validation_mode
45 | )
46 |
47 | df = discretize_merit_order_per_fuel(mo_f, pp_size_method=pp_size_method, country=country)
48 |
49 | df["x_k"] = df["fuel_draf"].map(from_other.get_fuel_prices(year=year, country=country))
50 |
51 | DATA_QUASCH = from_other.get_emissions_per_fuel_quaschning()
52 | marginal_emissions_of_gen = df["fuel_draf"].map(DATA_QUASCH) / df["used_eff"]
53 | df["fuel_cost"] = df["x_k"] / df["used_eff"]
54 |
55 | carbon_price = (
56 | from_other.get_ETS_price(year) if overwrite_carbon_tax is None else overwrite_carbon_tax
57 | )
58 | df["GHG_cost"] = marginal_emissions_of_gen * carbon_price
59 | df["marginal_emissions"] = marginal_emissions_of_gen
60 | df["marginal_cost"] = df["fuel_cost"] + df["GHG_cost"]
61 |
62 | df = df.sort_values("marginal_cost").reset_index(drop=True)
63 | df["cumsum_capa"] = df["capa"].cumsum()
64 | df = df.dropna()
65 | return df.copy()
66 |
67 |
68 | def merit_order_per_fuel(
69 | year: int = 2019,
70 | country: str = "DE",
71 | approx_method: str = "regr",
72 | validation_mode: bool = False,
73 | ) -> pd.DataFrame:
74 | """Returns a non-discretized merit order on fuel-type level."""
75 | eff_min, eff_max, __, __ = approximate_min_max_values(method=approx_method)
76 |
77 | source = "power_plant_list" if validation_mode else "entsoe"
78 |
79 | df = pd.DataFrame(
80 | dict(
81 | eff_min=eff_min,
82 | eff_max=eff_max,
83 | fuel_price=from_other.get_fuel_prices(year=year, country=country),
84 | emissions_for_gen=from_other.get_emissions_per_fuel_quaschning(), # in t_CO2eq / MWh_el
85 | capa=prep_installed_generation_capacity(year=year, country=country, source=source),
86 | )
87 | )
88 |
89 | logger.info(f"Available fuels for {year}, {country}: {list(df.index)}")
90 |
91 | df["eff_mean"] = (df["eff_min"] + df["eff_max"]) / 2
92 | df["fuel_cost"] = df["fuel_price"] / df["eff_mean"]
93 | df["GHG_cost"] = df["emissions_for_gen"] / df["eff_mean"]
94 | df["marginal_cost"] = df["fuel_cost"] + df["GHG_cost"]
95 | df = df.sort_values("marginal_cost")
96 | df["cumsum_capa"] = df["capa"].cumsum()
97 | return df.copy()
98 |
99 |
100 | @lru_cache(maxsize=2)
101 | def approximate_min_max_values(method="regr") -> Tuple[Dict, Dict, Dict, Dict]:
102 | """`method` must be either 'interp' for interpolation or 'regr' for linear regression."""
103 | mo = get_clean_merit_order_for_min_max_approximation(sort_by_fuel=True)
104 |
105 | if method == "interp":
106 | grouper = mo.groupby("fuel_draf")["used_eff"]
107 | eff_min = grouper.min().to_dict()
108 | eff_max = grouper.max().to_dict()
109 |
110 | grouper = mo.groupby("fuel_draf")["cumsum_capa"]
111 | cumsum_capa_LHS = grouper.min().to_dict()
112 | cumsum_capa_RHS = grouper.max().to_dict()
113 |
114 | elif method == "regr":
115 | from scipy import stats
116 |
117 | eff_min = {}
118 | eff_max = {}
119 | cumsum_capa_LHS = {}
120 | cumsum_capa_RHS = {}
121 |
122 | for fuel in mo["fuel_draf"].unique():
123 | mo_ = mo[mo["fuel_draf"] == fuel]
124 | slope, intercept, r_value, p_value, std_err = stats.linregress(
125 | mo_["cumsum_capa"], mo_["used_eff"]
126 | )
127 |
128 | cumsum_capa_LHS[fuel] = mo_["cumsum_capa"].min()
129 | cumsum_capa_RHS[fuel] = mo_["cumsum_capa"].max()
130 |
131 | eff_min[fuel] = intercept + slope * cumsum_capa_RHS[fuel]
132 | eff_max[fuel] = intercept + slope * cumsum_capa_LHS[fuel]
133 |
134 | else:
135 | raise ValueError(f"Method must be either 'interp' or 'regr'. Given:'{method}'")
136 |
137 | return eff_min, eff_max, cumsum_capa_RHS, cumsum_capa_LHS
138 |
139 |
140 | def get_clean_merit_order_for_min_max_approximation(sort_by_fuel=False) -> pd.DataFrame:
141 | """Return a clean German merit order of the generation technologies."""
142 |
143 | # The opsd-file is used here here!
144 | mo = from_opsd.merit_order(year=2019)
145 |
146 | if sort_by_fuel:
147 | groups = []
148 | for fuel, group in mo.groupby("fuel_draf", sort=False):
149 | groups.append(group.sort_values(by="used_eff", ascending=False))
150 |
151 | mo = pd.concat(groups, axis=0, ignore_index=True)
152 |
153 | mo["cumsum_capa"] = mo["capa"].cumsum()
154 |
155 | return mo
156 |
157 |
158 | @lru_cache(maxsize=3)
159 | def prep_installed_generation_capacity(year=2019, country="DE", source="entsoe") -> pd.Series:
160 | if source == "power_plant_list":
161 | ser = (
162 | from_opsd.merit_order(year=year)[["fuel_draf", "capa"]]
163 | .groupby("fuel_draf")
164 | .sum()["capa"]
165 | )
166 | return ser
167 |
168 | elif source == "entsoe":
169 | df = from_entsoe.load_installed_generation_capacity(year=year, country=country)
170 | df = from_entsoe.aggregate_to_standard_techs(df)
171 | ser = df.T.iloc[:, 0]
172 |
173 | ser = apply_share_cc_assumption(ser, country=country)
174 |
175 | logger.info(f"Available fuels for {year}, {country}: {list(ser.index)}")
176 |
177 | ser = pd.Series(index=mp.PWL_FUELS, data=ser)
178 | ser.fillna(0, inplace=True)
179 | return ser
180 |
181 | # elif source == "opsd":
182 | # ser = from_opsd.get_installed_generation_capacity(year=year, country=country)
183 | # ser = pd.Series(index=mp.PWL_FUELS, data=ser)
184 | # ser.fillna(0, inplace=True)
185 | # ser = apply_share_cc_assumption(ser, country=country)
186 | # return ser
187 |
188 | else:
189 | raise ValueError("Source must be either 'entsoe' or 'power_plant_list'")
190 |
191 |
192 | def apply_share_cc_assumption(ser, country: str) -> pd.Series:
193 | if "gas" in ser:
194 | share_cc = get_share_cc(country=country)
195 | ser["gas_cc"] = ser["gas"] * share_cc
196 | ser["gas"] = ser["gas"] * (1 - share_cc)
197 |
198 | return ser
199 |
200 |
201 | def get_share_cc(country: str) -> float:
202 | try:
203 | return cc_share.get_ccgt_shares_from_cascade().loc[country, "selected"]
204 | except KeyError:
205 | logger.warning("Unable to apply cascade method, German values returned")
206 | return cc_share.get_ccgt_DE()
207 |
208 |
209 | def discretize_merit_order_per_fuel(
210 | mo_f: pd.DataFrame, country: str, pp_size_method: str = "from_geo_scraped"
211 | ) -> pd.DataFrame:
212 | concat_list = []
213 | # if country=="DE":
214 | # pp_size_method = "from_Germany"
215 | for fuel, row in mo_f.iterrows():
216 | pp_size = get_pp_size(pp_size_method=pp_size_method, country=country, fuel=fuel)
217 | logger.info(f"pp_size_method={pp_size_method}")
218 | number_of_powerplants = int(row["capa"] // pp_size)
219 | capa_of_last_powerplant = row["capa"] % pp_size
220 | df = pd.DataFrame({"capa": [pp_size] * number_of_powerplants + [capa_of_last_powerplant]})
221 | cumsum_capa_in_f = df["capa"].cumsum()
222 | df["used_eff"] = row["eff_max"] - (cumsum_capa_in_f / row["capa"]) * (
223 | row["eff_max"] - row["eff_min"]
224 | )
225 | df["fuel_draf"] = fuel
226 | concat_list.append(df)
227 | df = pd.concat(concat_list, ignore_index=True)
228 | return df
229 |
230 |
231 | def get_pp_size(pp_size_method: str, country: str, fuel: str) -> int:
232 | try:
233 | if pp_size_method == "from_geo_scraped":
234 | return from_geo_scraped.get_pp_sizes_for_pwl().loc[country, fuel]
235 | # elif pp_size_method == "from_Germany":
236 | # return get_pp_sizes_from_germany()[fuel]
237 | # elif pp_size_method == "from_geo":
238 | # return from_geo_via_morph.get_pp_sizes().loc[country, fuel]
239 | # elif pp_size_method == "from_ccgt":
240 | # return cc_share.get_pp_sizes().loc[country, fuel]
241 | else:
242 | raise ValueError(f"Invalid pp_size_method given: {pp_size_method}")
243 | except KeyError:
244 | logger.warning("Unable to apply pp-size, German values returned")
245 | return get_pp_sizes_from_germany()[fuel]
246 |
247 |
248 | @lru_cache(maxsize=1)
249 | def get_pp_sizes_from_germany() -> pd.Series:
250 | return from_opsd.merit_order().groupby("fuel_draf").capa.mean()
251 |
--------------------------------------------------------------------------------
/elmada/exceptions.py:
--------------------------------------------------------------------------------
1 | class NoDataError(Exception):
2 | pass
3 |
--------------------------------------------------------------------------------
/elmada/from_geo_scraped.py:
--------------------------------------------------------------------------------
1 | import pandas as pd
2 | import requests
3 | from bs4 import BeautifulSoup
4 |
5 | from elmada import from_geo_via_morph
6 | from elmada import helper as hp
7 | from elmada import mappings as mp
8 | from elmada import paths
9 |
10 |
11 | def get_pp_sizes_for_pwl() -> pd.DataFrame:
12 | df = get_pp_sizes()
13 | df = df.fillna(df.mean()).astype(int)
14 | return df
15 |
16 |
17 | def get_pp_sizes() -> pd.DataFrame:
18 | geo = get_units_of_geo_list()
19 |
20 | grouper = geo.groupby(["cy", "fuel"])["capa"]
21 | pp_sizes = grouper.mean().unstack()
22 | count = grouper.count().unstack()
23 | pp_sizes = pp_sizes.where(count >= 2)
24 | return pp_sizes[mp.PWL_FUELS]
25 |
26 |
27 | def get_units_of_geo_list(cache: bool = True) -> pd.DataFrame:
28 | fp = paths.mode_dependent_cache_dir() / "units_of_geo_list.parquet"
29 |
30 | if fp.exists() and cache:
31 | df = hp.read(fp)
32 |
33 | else:
34 | df = _query_geo_power_plant_data()
35 | if cache:
36 | hp.write(df, fp)
37 |
38 | df = df.rename(
39 | columns={
40 | "Capacity (MWe)": "capa",
41 | "Unit Efficiency (%)": "eff",
42 | "Date Commissioned (yyyy-mm-dd)": "commissioned",
43 | "Unit #": "unit_no",
44 | },
45 | )
46 |
47 | interesting_cols = ["cy", "fuel", "geoid", "capa", "eff", "commissioned", "unit_no"]
48 | df = df.loc[df.capa != "", interesting_cols]
49 |
50 | df["capa"] = df["capa"].astype(float).astype(int)
51 | return df.reset_index(drop=True)
52 |
53 |
54 | def _query_geo_power_plant_data():
55 | geo = from_geo_via_morph.get_geo_list()
56 | max_count = len(geo)
57 | print(f"Download {max_count} items:", end="")
58 | concat_list = []
59 | for _, ser in geo.iterrows():
60 | print(".", end="")
61 | geo_id = ser["id"]
62 | print(geo_id, end=", ")
63 | df = get_df_from_geo_id(geo_id)
64 | df["cy"] = ser["cy"]
65 | df["fuel"] = ser["fuel"]
66 | df["geoid"] = geo_id
67 | concat_list.append(df)
68 | df = pd.concat(concat_list)
69 | return df
70 |
71 |
72 | def get_df_from_geo_id(geo_id: int) -> pd.DataFrame:
73 | url = f"http://globalenergyobservatory.org/geoid/{geo_id}"
74 | page = requests.get(url).text
75 | soup = BeautifulSoup(page, "lxml")
76 | selector = soup.find("div", {"id": "UnitDescription_Block"})
77 |
78 | table = selector.find("table")
79 |
80 | headers = [cell.get_text().strip() for cell in table.findAll("th")]
81 | ncols = len(headers)
82 | rows = table.findAll("tr")
83 |
84 | df = pd.DataFrame(columns=headers)
85 | for i, row in enumerate(rows):
86 | cells = row.find_all("td")
87 | if len(cells) == ncols:
88 | df.loc[i, :] = [cell.find("input").get("value") for cell in cells]
89 | return df
90 |
--------------------------------------------------------------------------------
/elmada/from_geo_via_morph.py:
--------------------------------------------------------------------------------
1 | """Handles power plant list from Global Energy Observatory via morph."""
2 |
3 | import collections
4 | import logging
5 | import sqlite3
6 | from functools import lru_cache
7 | from pathlib import Path
8 | from typing import Any, Callable, Dict, Iterable, List, Optional, Set, Tuple, Union
9 |
10 | import numpy as np
11 | import pandas as pd
12 | import requests
13 |
14 | from elmada import helper as hp
15 | from elmada import mappings as mp
16 | from elmada import paths
17 |
18 | logger = logging.getLogger(__name__)
19 | logger.setLevel(logging.WARN)
20 |
21 | DB_FILE_NAME = Path("GEO-database.suffix")
22 |
23 | CC_WEIGHTING = {
24 | "Thermal and CCGT": 1,
25 | "Power and Heat Combined Cycle Gas Turbine": 1,
26 | "Combined Cycle Gas Engine (CCGE)": 1,
27 | "Combined Cycle Gas Turbine": 1,
28 | "OCGT and CCGT": 0.49,
29 | "Sub-critical Thermal": 0,
30 | "Power and Heat Open Cycle Gas Turbine": 0,
31 | "Open Cycle Gas Turbine": 0,
32 | "Heat and Power Steam Turbine": 0,
33 | "Super-critical Thermal": 0,
34 | "Gas Engines": 0,
35 | None: 0,
36 | }
37 |
38 | COLS = {
39 | "Name": "name",
40 | "Type": "fuel",
41 | "Country": "country",
42 | "Type_of_Plant_rng1": "plant_type",
43 | "Type_of_Fuel_rng1_Primary": "primary_fuel",
44 | "Design_Capacity_MWe_nbr": "capa",
45 | "GEO_Assigned_Identification_Number": "id",
46 | "Status_of_Plant_itf": "status",
47 | }
48 |
49 | NON_OPERATIONAL_STATUSES = [
50 | "Built and In Test Stage",
51 | "Decommissioned",
52 | "Demolished",
53 | "Design and Planning Stage",
54 | "Final Bid and Approval Stage",
55 | "Mothballed Full",
56 | "Mothballed Partial",
57 | "Shutdown" "Under Construction",
58 | ]
59 |
60 |
61 | def get_ccgt_shares() -> pd.DataFrame:
62 | geo = get_geo_list()
63 | valid_countries = get_valid_countries()
64 | df = pd.DataFrame(index=valid_countries.keys())
65 |
66 | for cy_short, cy_long in valid_countries.items():
67 | is_gas = geo["fuel"].isin(["gas", "gas_cc"])
68 | is_country = geo["country"] == cy_long
69 | geo_sub = geo[is_gas & is_country]
70 | df.loc[cy_short, "cy_long"] = cy_long
71 | df.loc[cy_short, "cc_capa"] = geo_sub["capa"] @ geo_sub["cc_weight"]
72 | df.loc[cy_short, "total_capa"] = geo_sub["capa"].sum()
73 |
74 | df["share_cc"] = df["cc_capa"] / df["total_capa"]
75 | return df
76 |
77 |
78 | def get_valid_countries():
79 | df = get_geo_list()
80 | avail = set(df["country"].unique())
81 | d = {short: long for short, long in mp.COUNTRIES_FOR_ANALYSIS.items() if long in avail}
82 | return collections.OrderedDict(sorted(d.items()))
83 |
84 |
85 | @lru_cache(maxsize=1)
86 | def get_geo_list():
87 | df = get_geo_full_list()
88 | df = filter_relevant_plants(df)
89 | df = filter_operating_plants(df)
90 | df = set_lignite_plants(df)
91 | df = set_gasCC_plants(df)
92 |
93 | df["fuel"] = df["fuel"].str.lower()
94 |
95 | reverse_ana = {v: k for k, v in mp.COUNTRIES_FOR_ANALYSIS.items()}
96 | df.insert(loc=3, column="cy", value=df["country"].map(reverse_ana))
97 |
98 | return df[df.capa.notnull()]
99 |
100 |
101 | def set_gasCC_plants(df: pd.DataFrame) -> pd.DataFrame:
102 | is_gas = df["fuel"] == "Gas"
103 |
104 | df["cc_weight"] = np.nan
105 | df.loc[is_gas, "cc_weight"] = df.loc[is_gas, "plant_type"].map(CC_WEIGHTING)
106 | # is_cc = df["cc_weight"][is_gas].apply(round).astype("bool")
107 | is_cc = df["cc_weight"] >= 0.5
108 | df.loc[is_gas & is_cc, "fuel"] = "Gas_cc"
109 | return df
110 |
111 |
112 | def set_lignite_plants(df: pd.DataFrame) -> pd.DataFrame:
113 | is_coal = df["fuel"] == "Coal"
114 | is_lignite = df["primary_fuel"].str.contains("lignite", case=False)
115 | is_peat = df["primary_fuel"].str.contains("peat", case=False)
116 | df.loc[is_coal & (is_lignite ^ is_peat), "fuel"] = "Lignite"
117 | return df
118 |
119 |
120 | def filter_operating_plants(df: pd.DataFrame) -> pd.DataFrame:
121 | no_status = df.status.isna()
122 | contains_shutdown = df.name.str.contains("Shutdown")
123 | df.loc[no_status & contains_shutdown, "status"] = "Shutdown"
124 | is_operating = ~df["status"].isin(NON_OPERATIONAL_STATUSES)
125 | return df[is_operating]
126 |
127 |
128 | def filter_relevant_plants(df: pd.DataFrame) -> pd.DataFrame:
129 | is_ana = df["Country"].isin(mp.COUNTRIES_FOR_ANALYSIS.values())
130 | is_conv = df["Type"].isin(["Gas", "Coal", "Nuclear", "Oil"])
131 | return df.loc[is_ana & is_conv, COLS.keys()].rename(columns=COLS)
132 |
133 |
134 | def get_geo_full_list(cache: bool = True) -> pd.DataFrame:
135 |
136 | fp = paths.mode_dependent_cache_dir() / DB_FILE_NAME.with_suffix(".parquet")
137 |
138 | if cache and fp.exists():
139 | df = hp.read(fp)
140 |
141 | else:
142 | fp_db = paths.CACHE_DIR / DB_FILE_NAME.with_suffix(".db")
143 |
144 | if not fp_db.exists():
145 | download_database(fp=fp_db)
146 |
147 | conn = sqlite3.connect(fp_db)
148 | df = pd.read_sql_query("SELECT * FROM powerplants", conn)
149 | conn.close()
150 | if cache:
151 | hp.write(df, fp)
152 |
153 | return df
154 |
155 |
156 | def download_database(fp: Path) -> None:
157 | url_base = "https://morph.io/coroa/global_energy_observatory_power_plants/"
158 | morph_api_key = hp.get_api_key("morph")
159 | url_ending = f"data.sqlite?key={morph_api_key}"
160 | url = url_base + url_ending
161 |
162 | response = requests.get(url)
163 | logger.info(f"{fp.name} downloaded from GEO via Morph.")
164 | fp.write_bytes(response.content)
165 |
--------------------------------------------------------------------------------
/elmada/from_opsd.py:
--------------------------------------------------------------------------------
1 | """Reads power plant list from Open Power System Data (OPSD)."""
2 |
3 | import logging
4 | from pathlib import Path
5 | from typing import Any, Callable, Dict, Iterable, List, Optional, Tuple, Union
6 |
7 | import numpy as np
8 | import pandas as pd
9 | import requests
10 | from scipy import stats
11 |
12 | import elmada
13 | from elmada import from_entsoe, from_other
14 | from elmada import mappings as mp
15 | from elmada import paths
16 |
17 | logger = logging.getLogger(__name__)
18 | logger.setLevel(logging.WARN)
19 |
20 |
21 | def prep_prices(year=2019, freq="60min", country="DE", **mo_kwargs) -> pd.Series:
22 | """Convenience function to get the marginal costs for each timesteps."""
23 | return prep_CEFs(year=year, freq=freq, country=country, **mo_kwargs)["marginal_cost"]
24 |
25 |
26 | def prep_CEFs(year=2019, freq="60min", country="DE", mo_P=None, **mo_kwargs) -> pd.DataFrame:
27 | """Prepares German CEFs from the power plant list from OPSD"""
28 | assert country == "DE", "this function only works for Germany"
29 | if mo_P is None:
30 | mo_P = merit_order(year=year, **mo_kwargs)
31 | return get_CEFs_from_merit_order(mo_P=mo_P, year=year, freq=freq, country=country)
32 |
33 |
34 | def get_CEFs_from_merit_order(
35 | mo_P: pd.DataFrame, year: int, freq: str, country: str, resi_T: Optional[pd.Series] = None
36 | ) -> pd.DataFrame:
37 | if resi_T is None:
38 | resi_T = from_entsoe.prep_residual_load(year=year, freq=freq, country=country)
39 | _warn_if_not_enough_capa(mo_P, resi_T)
40 | total_load_T = from_entsoe.load_el_national_generation(
41 | year=year, freq=freq, country=country
42 | ).sum(axis=1)
43 | cols = ["cumsum_capa", "marginal_emissions", "capa", "fuel_draf", "used_eff", "marginal_cost"]
44 | len_mo = len(mo_P)
45 | mo_P_arr = mo_P[cols].values
46 | transm_eff = from_other.get_transmission_efficiency(country=country)
47 |
48 | def get_data(resi_value: float, what: str = "marginal_emissions"):
49 | output_col_index = cols.index(what)
50 | for row in range(len_mo):
51 | if mo_P_arr[row, 0] > resi_value:
52 | return mo_P_arr[row, output_col_index]
53 | return mo_P_arr[-1, output_col_index]
54 |
55 | def get_emissions_for_xef(resi_value: float) -> float:
56 | # If the residual load is not positive there are no emissions:
57 | if resi_value <= 0.0:
58 | return 0.0
59 |
60 | emissions = 0.0
61 | for row in range(len_mo):
62 | emissions += mo_P_arr[row, 1] * mo_P_arr[row, 2]
63 |
64 | # If the cumsum_capa is above resi_value subtract the emissions of the additional
65 | # capacity and return the quotient of emissions and resi_value:
66 | if mo_P_arr[row, 0] > resi_value:
67 | emissions -= mo_P_arr[row, 1] * (mo_P_arr[row, 0] - resi_value)
68 | return emissions
69 |
70 | # If the following code is executed there is not enough generation capacity:
71 | return emissions
72 |
73 | df = pd.DataFrame(
74 | {
75 | "residual_load": resi_T,
76 | "total_load": total_load_T,
77 | "marginal_fuel": resi_T.apply(get_data, what="fuel_draf"),
78 | "efficiency": resi_T.apply(get_data, what="used_eff"),
79 | "marginal_cost": resi_T.apply(get_data, what="marginal_cost"),
80 | "MEFs": resi_T.apply(get_data, what="marginal_emissions") / transm_eff,
81 | "XEFs": (resi_T.apply(get_emissions_for_xef)) / total_load_T / transm_eff,
82 | }
83 | )
84 |
85 | df["XEFs"] *= 1000 # convert from t/MWh to kg/MWh or g/kWh
86 | df["MEFs"] *= 1000 # convert from t/MWh to kg/MWh or g/kWh
87 | return df
88 |
89 |
90 | def merit_order(
91 | year=2019,
92 | efficiency_per_plant: bool = True,
93 | emission_data_source: str = "quaschning",
94 | overwrite_carbon_tax: Optional[float] = None,
95 | **preprocess_kwargs,
96 | ) -> pd.DataFrame:
97 | """Prepares the merit order from the German power plant list."""
98 |
99 | df = get_current_active_power_plants(year)
100 | df = _rename_to_draf_fuels(df)
101 | df = _preprocess_efficiencies(
102 | df, efficiency_per_plant=efficiency_per_plant, **preprocess_kwargs
103 | )
104 | df = _add_marginal_emissions_for_gen(df, emission_data_source)
105 |
106 | carbon_price = (
107 | from_other.get_ETS_price(year) if overwrite_carbon_tax is None else overwrite_carbon_tax
108 | )
109 | df["x_k"] = df["fuel_draf"].map(from_other.get_fuel_prices(year=year, country="DE"))
110 | df["fuel_cost"] = df["x_k"] / df["used_eff"]
111 | df["GHG_cost"] = df["marginal_emissions_for_gen"] * carbon_price
112 | df["marginal_emissions"] = df["marginal_emissions_for_gen"]
113 | df["marginal_cost"] = df["fuel_cost"] + df["GHG_cost"]
114 |
115 | # to be consistent with the PWL merit_order:
116 | df = df.rename(columns={"capacity_net_bnetza": "capa"})
117 |
118 | df = df.sort_values("marginal_cost").reset_index(drop=True)
119 | df["cumsum_capa"] = df["capa"].cumsum()
120 | return df
121 |
122 |
123 | def get_summary(year=2019) -> pd.DataFrame:
124 | ca = get_current_active_power_plants(year)
125 | grouper = ca.groupby(["fuel", "technology"])
126 | d = dict(
127 | counts=grouper.count().id,
128 | efficiency=grouper["efficiency_estimate"].mean(),
129 | sum_capa=grouper["capacity_net_bnetza"].sum(),
130 | )
131 | return pd.concat(d.values(), axis=1, keys=d.keys())
132 |
133 |
134 | def get_summary_of_opsd_raw() -> pd.DataFrame:
135 | df = read_opsd_powerplant_list(which="DE")
136 | df = df.rename(columns={"capacity_net_bnetza": "capa"})
137 | df = df.groupby("energy_source_level_2").agg({"id": "size", "capa": "sum"})
138 | df = df.sort_values("capa", ascending=False)
139 | df["capa_rel"] = df["capa"] / df["capa"].sum()
140 | return df
141 |
142 |
143 | def get_current_active_power_plants(year=2019) -> pd.DataFrame:
144 | """Returns a Dataframe with all active power plants for a specific year."""
145 | df = read_opsd_powerplant_list(which="DE")
146 |
147 | used_fuels = mp.OPSD_TO_DRAF.keys()
148 | other_fuels = set(df["fuel"]) - set(used_fuels)
149 | is_german = df["country_code"] == "DE"
150 | after_commissioned = (df["commissioned"].notnull() & (df["commissioned"] < year)) | df[
151 | "commissioned"
152 | ].isna()
153 | before_shutdown = (df["shutdown"].notnull() & (df["shutdown"] > year)) | df["shutdown"].isna()
154 | currently_active = after_commissioned & before_shutdown
155 | in_used_fuels = df["fuel"].isin(used_fuels)
156 | cond = is_german & currently_active & in_used_fuels
157 | ca = df[cond]
158 |
159 | logger.info(
160 | f"{(cond.sum() / len(df)):.2%} of data rows were used "
161 | f"({(~in_used_fuels).sum() / len(df):.2%} are not in used fuels). "
162 | f"Other (discarded) fuels are {other_fuels}."
163 | )
164 |
165 | # interesting_cols = ["id", "name_bnetza", "status", "fuel", "technology", "type", "efficiency_estimate", "capacity_net_bnetza"]
166 | return ca
167 |
168 |
169 | def read_opsd_powerplant_list(which: str = "DE") -> pd.DataFrame:
170 | assert which in ("DE", "EU"), f"`{which}` is no valid value for `which`."
171 |
172 | fp = paths.mode_dependent_cache_dir() / f"OPSD_conventional_power_plants_{which}.csv"
173 |
174 | if not fp.exists():
175 | download_powerplant_list(which=which, fp=fp)
176 | df = pd.read_csv(fp)
177 |
178 | if elmada.get_mode() == "live":
179 | df = df.rename(columns={"energy_source": "fuel", "country": "country_code"})
180 |
181 | return df
182 |
183 |
184 | def download_powerplant_list(which: str, fp: Path) -> None:
185 | url = (
186 | f"https://data.open-power-system-data.org/conventional_power_plants/latest/"
187 | f"conventional_power_plants_{which}.csv"
188 | )
189 | response = requests.get(url, allow_redirects=True)
190 | fp.write_bytes(response.content)
191 | logger.info(f"{fp.name} downloaded from OPSD.")
192 |
193 |
194 | def _rename_to_draf_fuels(
195 | df: pd.DataFrame, minimum_efficiency_for_gas_cc: float = 0.5
196 | ) -> pd.DataFrame:
197 | def my_rename(row):
198 | if (
199 | (row["fuel"] == "Natural gas")
200 | and (row["technology"] == "Combined cycle")
201 | and (row["efficiency_estimate"] >= minimum_efficiency_for_gas_cc)
202 | ):
203 | return "gas_cc"
204 | else:
205 | return mp.OPSD_TO_DRAF[row["fuel"]]
206 |
207 | df["fuel_draf"] = df.apply(my_rename, axis=1)
208 | return df
209 |
210 |
211 | def _preprocess_efficiencies(
212 | df: pd.DataFrame,
213 | efficiency_per_plant: bool = True,
214 | fill_missing_efficiencies: bool = True,
215 | fill_zscore_outlier: bool = True,
216 | zscore_threshold: float = 3,
217 | ensure_minimum_efficiency: bool = True,
218 | minimum_efficiency: float = 0.3,
219 | ) -> pd.DataFrame:
220 | filler_dic = df.groupby(by="fuel_draf")["efficiency_estimate"].mean().to_dict()
221 | df["filler"] = df["fuel_draf"].map(filler_dic)
222 |
223 | if fill_missing_efficiencies:
224 | number_of_nans = df["efficiency_estimate"].isna().sum()
225 | logger.info(f"{number_of_nans} nans in efficiencies of merit order filled")
226 | df["efficiency_estimate"].fillna(df["filler"], inplace=True)
227 |
228 | if fill_zscore_outlier:
229 | for fuel in df["fuel_draf"].unique():
230 | ser = df["efficiency_estimate"]
231 | ser = ser[ser.notnull()]
232 | cond_zscore = np.abs(stats.zscore(ser)) > zscore_threshold
233 | cond_fuel = df["fuel_draf"] == fuel
234 | number_of_nans = len(df[cond_zscore & cond_fuel])
235 | logger.info("f{number_of_nans} nans filled in fuel {fuel}.")
236 | df.loc[(cond_zscore & cond_fuel), "efficiency_estimate"] = filler_dic[fuel]
237 |
238 | df["eta_k"] = df["fuel_draf"].map(from_other.get_baumgaertner_data()["eta_k"])
239 | df["used_eff"] = df["efficiency_estimate"] if efficiency_per_plant else df["eta_k"]
240 |
241 | if ensure_minimum_efficiency:
242 | df.loc[df["used_eff"] < minimum_efficiency, "used_eff"] = minimum_efficiency
243 | logger.info(f"Minimum_efficiency set to {minimum_efficiency}.")
244 |
245 | return df
246 |
247 |
248 | def _add_marginal_emissions_for_gen(df: pd.DataFrame, emission_data_source: str) -> pd.DataFrame:
249 |
250 | if emission_data_source == "baumgaertner":
251 | DATA_BAUMG = from_other.get_baumgaertner_data()
252 | df["mu_co2_k"] = df["fuel_draf"].map(DATA_BAUMG["mu_co2_k"])
253 | df["H_u_k"] = df["fuel_draf"].map(DATA_BAUMG["H_u_k"])
254 | df["marginal_emissions_for_gen"] = df["mu_co2_k"] / (df["H_u_k"] * df["used_eff"])
255 |
256 | elif emission_data_source == "quaschning":
257 | # in the quaschning data the mu_co2_k and the H_u_k are already included
258 | DATA_QUASCH = from_other.get_emissions_per_fuel_quaschning()
259 | df["marginal_emissions_for_gen"] = df["fuel_draf"].map(DATA_QUASCH) / df["used_eff"]
260 |
261 | else:
262 | raise ValueError("Emission_data_source must be either 'baumgaertner' or 'quaschning'.")
263 |
264 | return df
265 |
266 |
267 | def _warn_if_not_enough_capa(mo_P: pd.DataFrame, resi_ser: pd.DataFrame) -> None:
268 | mo_max = mo_P["cumsum_capa"].max() / 1e3
269 | min_resi = resi_ser.min() / 1e3
270 | if mo_max < min_resi:
271 | logger.warning(
272 | f"Generation capacity ({mo_max:.2f} GW) is lower than "
273 | f"minimum residual load ({min_resi:.2f} GW)."
274 | )
275 |
--------------------------------------------------------------------------------
/elmada/from_other.py:
--------------------------------------------------------------------------------
1 | import logging
2 | from io import StringIO
3 | from typing import Any, Callable, Dict, Iterable, List, Optional, Tuple, Union
4 |
5 | import numpy as np
6 | import pandas as pd
7 |
8 | from elmada import get_mode
9 | from elmada import helper as hp
10 | from elmada import mappings as mp
11 | from elmada import paths
12 |
13 | logger = logging.getLogger(__name__)
14 | logger.setLevel(logging.WARN)
15 |
16 |
17 | def get_ETS_price(year: int) -> float:
18 | prices = get_ETS_prices()
19 | try:
20 | return prices[year]
21 | except KeyError:
22 | max(prices.keys())
23 | backup_value = prices[max(prices.keys())]
24 | logger.warning(
25 | f"No data for ETS price for {year} "
26 | f"==> Default value of latest available year with {backup_value:.2f} €/t is given."
27 | )
28 | return backup_value
29 |
30 |
31 | def get_ETS_prices() -> Dict:
32 | """Returns carbon emission prices of the EU emissions trading system (ETS) for different years.
33 |
34 | Source:
35 | * EEX (https://www.eex.com/en/market-data/environmental-markets/auction-market/european-emission-allowances-auction)
36 | via Sandbag (https://sandbag.org.uk/carbon-price-viewer/)
37 | * ICE via Quandl
38 | """
39 | mode = get_mode()
40 | if mode == "live":
41 | return get_ice_eua_prices()
42 |
43 | elif mode == "safe":
44 | return get_sandbag_eua_prices()
45 | else:
46 | raise RuntimeError()
47 |
48 |
49 | def get_ice_eua_prices(cache: bool = True) -> Dict:
50 | """Get EUA prices from ICE [1] via Quandl [2].
51 |
52 | Sources:
53 | [1] https://www.theice.com/
54 | [2] https://www.quandl.com/data/CHRIS/ICE_C1-ECX-EUA-Futures-Continuous-Contract-1-C1-Front-Month
55 | """
56 | fp = paths.mode_dependent_cache_dir() / "QUANDL_eua_prices.parquet"
57 |
58 | if cache and fp.exists():
59 | ser = hp.read(fp)
60 | else:
61 | import quandl
62 |
63 | quandl.ApiConfig.api_key = hp.get_api_key("quandl")
64 | df = quandl.get("CHRIS/ICE_C1")
65 | ser = df["Settle"].resample("y").mean().rename("Price")
66 | ser.index = ser.index.year
67 | if cache:
68 | hp.write(ser, fp)
69 |
70 | return ser.to_dict()
71 |
72 |
73 | def get_sandbag_eua_prices() -> Dict:
74 | """Get ETS EUA prices via Sandbag"""
75 | fp = paths.DATA_DIR / "sandbag/eua-price.csv"
76 |
77 | # # fix bad csv-syntax
78 | s = fp.read_text(encoding="utf8").replace('",', '";')
79 |
80 | io = StringIO(s)
81 | df = pd.read_csv(io, sep=";", decimal=",", index_col=0, skiprows=2)
82 |
83 | df.index = pd.to_datetime(df.index)
84 | df = df.resample("YE").mean()
85 | df = df.set_index(df.index.year, drop=True)
86 | return df.squeeze().to_dict()
87 |
88 |
89 | def _get_light_oil_conversion_factor(source: int = 2):
90 | """Get the calorific value for light oil in MWh/Hektoliter from different sources.
91 |
92 | Sources:
93 | [1]: Heizöl Total (https://www.heizoel.total.de/rund-um-heizoel/aktuelles-tipps/heizoelkauf-beratung/heizwert-und-brennwert-von-heizol/)
94 | raw value: 9.8 kWh/l
95 |
96 | [2]: Mineralöl Franken (https://www.mineraloel-franken.de/fileadmin/redaktion/downloads/Produktspezifikation_Heizoel_EL_Total.pdf)
97 | raw values: 42.6 MJ/kg for light oil with a density of 860 kg/m³.
98 | """
99 | if source == 1:
100 | return 0.980
101 |
102 | if source == 2:
103 | return (
104 | 42.6 # MJ/kg
105 | * 860 # kg/m³ ==> MJ/m³
106 | / 3600 # MJ/MWh ==> MWh/m³
107 | / 10 # Hektoliter/m³ ==> MWh/Hektoliter
108 | )
109 |
110 |
111 | def _get_light_oil_price(year: int = 2019):
112 | """Get light oil price in €/MWh.
113 |
114 | Source: StatistischeBundesamt.2020 (https://www.destatis.de/DE/Themen/Wirtschaft/Preise/Publikationen/Energiepreise/energiepreisentwicklung-pdf-5619001.html)
115 | """
116 | data = pd.Series(
117 | {
118 | 2005: 42.42,
119 | 2006: 47.58,
120 | 2007: 46.83,
121 | 2008: 61.76,
122 | 2009: 40.81,
123 | 2010: 52.31,
124 | 2011: 66.51,
125 | 2012: 72.94,
126 | 2013: 67.96,
127 | 2014: 61.88,
128 | 2015: 46.19,
129 | 2016: 38.40,
130 | 2017: 45.05,
131 | 2018: 55.27,
132 | 2019: 53.69,
133 | }
134 | )
135 | return (
136 | data[year] / _get_light_oil_conversion_factor() # €/Hektoliter # MWh/Hektoliter ==> €/MWh
137 | )
138 |
139 |
140 | def _get_lignite_price(year: int = 2019, base_price: float = 6.3) -> float:
141 | """Get lignite price in €/MWh. Calculated as product of a destatis index and an baseprice for
142 | the year 2015.
143 |
144 | Source:
145 | indices: StatistischeBundesamt.2020 (https://www.destatis.de/DE/Themen/Wirtschaft/Preise/Publikationen/Energiepreise/energiepreisentwicklung-pdf-5619001.html)
146 | base_price: Konstantin.2017 (https://doi.org/10.1007/978-3-662-49823-1)
147 | """
148 | fp = paths.DATA_DIR / "destatis/energiepreisentwicklung-xlsx-5619001.xls"
149 | xl = pd.ExcelFile(fp)
150 | df = xl.parse(
151 | sheet_name=7, skiprows=28, header=None, skipfooter=0, index_col=0, na_values="-"
152 | ).dropna(axis=0, how="all")[13]
153 |
154 | df.index = df.index.str.slice(0, 5).astype(int)
155 | return df[year] * base_price / 100
156 |
157 |
158 | def _get_coal_price(year: int = 2019, base_price: float = 10.12) -> float:
159 | """Get coal price in €/MWh. Calculated as product of a destatis index and an baseprice for
160 | the year 2015.
161 |
162 | Source:
163 | indices: StatistischeBundesamt.2020 (https://www.destatis.de/DE/Themen/Wirtschaft/Preise/Publikationen/Energiepreise/energiepreisentwicklung-pdf-5619001.html)
164 | base_price: Konstantin.2017 (https://doi.org/10.1007/978-3-662-49823-1)
165 | """
166 | fp = paths.DATA_DIR / "destatis/energiepreisentwicklung-xlsx-5619001.xls"
167 | xl = pd.ExcelFile(fp)
168 | df = xl.parse(
169 | sheet_name=7, skiprows=7, header=None, skipfooter=21, index_col=0, na_values="-"
170 | ).dropna(axis=0, how="all")[13]
171 |
172 | df.index = df.index.str.slice(0, 5).astype(int)
173 | return df[year] * base_price / 100
174 |
175 |
176 | def _get_gas_price(year: int = 2019, country: str = "DE") -> float:
177 | """Get gas price in €/MWh. including excise taxes, without value-added tax.
178 | Data for most European countries in the years 2008 - 2019.
179 |
180 | Source: StatistischeBundesamt.2020 (https://www.destatis.de/DE/Themen/Wirtschaft/Preise/Publikationen/Energiepreise/energiepreisentwicklung-pdf-5619001.html)
181 | """
182 | fp = paths.DATA_DIR / "destatis/energiepreisentwicklung-xlsx-5619001.xls"
183 | xl = pd.ExcelFile(fp)
184 | nblocks = 4
185 | blocksize = 26
186 | block_list = []
187 | for i in range(nblocks):
188 | block_list.append(
189 | xl.parse(
190 | sheet_name=11,
191 | skiprows=4 + i * blocksize,
192 | skipfooter=blocksize * (nblocks - 1 - i) + 2,
193 | index_col=0,
194 | na_values="-",
195 | ).dropna(axis=0, how="all")
196 | )
197 | df = pd.concat(block_list, sort=False, join="outer", axis=1).dropna(axis=1, how="all")
198 | df["year"] = df.index.str.slice(5).astype(int)
199 | df = df.groupby("year").mean() # get mean between year-halfs
200 |
201 | df = df / 100 * 1000 # convert cent/kWh into €/MWh
202 |
203 | default_value = df.mean().mean()
204 | warn_msg = (
205 | f"No data for gas price for {year},{country} ==> "
206 | f"Default value {default_value:.2f} €/MWh is given."
207 | )
208 | try:
209 | price = df.loc[year, mp.EU_de_for_gas_price[country]]
210 | except KeyError:
211 | logger.warning(warn_msg)
212 | return default_value
213 |
214 | if np.isnan(price):
215 | logger.warning(warn_msg)
216 | return default_value
217 |
218 | else:
219 | return price
220 |
221 |
222 | def get_fuel_prices(year: int = 2019, country: str = "DE") -> Dict:
223 | """Get x_k: Fuel price [€ / MWh]
224 |
225 | Source:
226 | Nuclear: Konstantin.2017 (https://doi.org/10.1007/978-3-662-49823-1)
227 | Rest:
228 | StatistischeBundesamt.2020 (https://www.destatis.de/DE/Themen/Wirtschaft/Preise/Publikationen/Energiepreise/energiepreisentwicklung-pdf-5619001.html)
229 | + Konstantin.2017 (https://doi.org/10.1007/978-3-662-49823-1)
230 | """
231 | gas_price = _get_gas_price(year=year, country=country)
232 | try:
233 | return dict(
234 | oil=_get_light_oil_price(year=year),
235 | gas_cc=gas_price,
236 | gas=gas_price,
237 | coal=_get_coal_price(year=year),
238 | lignite=_get_lignite_price(year=year),
239 | nuclear=4.18,
240 | )
241 | except KeyError: # if values for future years are missing
242 | return get_fuel_prices(year=2019)
243 |
244 |
245 | def get_baumgaertner_data() -> Dict:
246 | """Nomentclature
247 |
248 | Per Fuel-type k
249 | - x_k: Fuel price [€ / MWh]
250 | - mu_co2_k: Carbon emission intensity [t_CO2eq / t_k]
251 | - H_u_k: Calorific value [MWh / t_k]
252 | - eta_k: Efficiency [MWh_el / MWh]
253 |
254 | Source: Baumgärtner.2019 (https://doi.org/10.1016/j.apenergy.2019.04.029)
255 | fuel prices: Konstantin.2017 (https://doi.org/10.1007/978-3-662-49823-1)
256 | """
257 | return dict(
258 | x_k=dict(oil=38.63, gas_cc=26.71, gas=29.81, coal=10.12, lignite=6.3, nuclear=4.18),
259 | mu_co2_k=dict(oil=3.15, gas_cc=1.93, gas=1.93, coal=2.6, lignite=1.39, nuclear=0.0),
260 | H_u_k=dict(
261 | oil=42.6 / 3.6,
262 | gas_cc=38.27 / 3.6,
263 | gas=38.27 / 3.6,
264 | coal=26.47 / 3.6,
265 | lignite=14.99 / 3.6,
266 | nuclear=41667.0,
267 | ),
268 | eta_k=dict(oil=0.373, gas_cc=0.50, gas=0.349, coal=0.377, lignite=0.356, nuclear=0.32),
269 | )
270 |
271 |
272 | def get_emissions_per_fuel_quaschning() -> Dict:
273 | """Carbon emissions per fuel type [t_CO2eq / MWh]
274 |
275 | Source: Quaschning.2015 (https://www.volker-quaschning.de/datserv/CO2-spez/index_e.php)"""
276 | return dict(oil=0.28, gas_cc=0.25, gas=0.25, coal=0.34, lignite=0.36, nuclear=0.0)
277 |
278 |
279 | def prepare_transmission_losses() -> pd.DataFrame:
280 | """Source: WorldBank.2020 (https://databank.worldbank.org/reports.aspx?source=2&series=EG.ELC.LOSS.ZS)"""
281 | fp = paths.DATA_DIR / "worldbank/Data_Extract_From_World_Development_Indicators/Data.csv"
282 | df = pd.read_csv(fp, nrows=58, na_values="..", index_col=2).iloc[:, 3:]
283 | df = df.rename(columns={k: k[:4] for k in df.keys()})
284 | df["mean"] = df.loc[:, "2010":"2014"].mean(1)
285 | return df
286 |
287 |
288 | def get_transmission_efficiency_series(fillna: bool = True, cache: bool = True) -> pd.Series:
289 | """Returns a Series with transmission efficiencies and long country names in index."""
290 |
291 | fp = paths.mode_dependent_cache_dir() / "transmission_efficiencies.parquet"
292 |
293 | if cache and fp.exists():
294 | ser = hp.read(fp)
295 |
296 | else:
297 | df = prepare_transmission_losses()
298 | ser = df["mean"]
299 | if cache:
300 | hp.write(ser, fp)
301 |
302 | if fillna:
303 | na_countries = list(ser[ser.isna()].index)
304 | filler = ser.mean()
305 | ser = ser.fillna(filler)
306 | logger.info(f"No data for transmission efficiency for {na_countries}")
307 |
308 | return 1 - (ser / 100)
309 |
310 |
311 | def get_transmission_efficiency(country: str) -> float:
312 | """Returns scalar value of transmission efficiency."""
313 | ser = get_transmission_efficiency_series()
314 |
315 | try:
316 | cy_long = mp.EUROPE_COUNTRIES[country]
317 | return ser[cy_long]
318 |
319 | except KeyError:
320 | default = ser.mean()
321 | logger.warning(
322 | f"No transmission efficiency data for {country}. European mean of {default} given."
323 | )
324 | return default
325 |
--------------------------------------------------------------------------------
/elmada/from_smard.py:
--------------------------------------------------------------------------------
1 | import logging
2 | import warnings
3 |
4 | import pandas as pd
5 |
6 | from elmada import helper as hp
7 | from elmada import paths
8 |
9 | logger = logging.getLogger(__name__)
10 | logger.setLevel(level=logging.WARN)
11 |
12 |
13 | def prep_dayahead_prices(
14 | year: int, freq: str = "60min", country: str = "DE", cache: bool = True
15 | ) -> pd.Series:
16 |
17 | assert year in range(2000, 2100), f"{year} is not a valid year"
18 | assert country == "DE", "Smard is only used for Germany!"
19 |
20 | fp = paths.CACHE_DIR / f"{year}_{freq}_{country}_dayahead_c_el_smard.parquet"
21 |
22 | if cache and fp.exists():
23 | ser = hp.read(fp)
24 |
25 | else:
26 | fp_raw = paths.DATA_DIR / f"smard/Day-ahead_prices_{year}.csv"
27 | df = pd.read_csv(
28 | fp_raw,
29 | sep=";",
30 | usecols=["Germany/Luxembourg[€/MWh]", "Germany/Austria/Luxembourg[€/MWh]"],
31 | na_values="-",
32 | )
33 | df = df.reset_index(drop=True)
34 | df = df.fillna(df.mean())
35 |
36 | # add a merged column
37 | df["DE_merged"] = 0
38 |
39 | if year < 2018:
40 | df["DE_merged"] = df["Germany/Austria/Luxembourg[€/MWh]"]
41 |
42 | elif year == 2018:
43 |
44 | warnings.filterwarnings(
45 | "ignore",
46 | message="A value is trying to be set on a copy of a slice from a DataFrame",
47 | )
48 | df.loc[:6552, "DE_merged"] = df.loc[:6552, "Germany/Austria/Luxembourg[€/MWh]"]
49 | df.loc[6552:, "DE_merged"] = df.loc[6552:, "Germany/Luxembourg[€/MWh]"]
50 |
51 | elif year > 2018:
52 | df["DE_merged"] = df["Germany/Luxembourg[€/MWh]"]
53 |
54 | ser = df["DE_merged"]
55 | if cache:
56 | hp.write(ser, fp)
57 |
58 | start_freq = hp.estimate_freq(ser)
59 |
60 | ser = hp.resample(ser, year=year, start_freq=start_freq, target_freq=freq)
61 | if start_freq != freq:
62 | logger.warning(f"Dayahead_prices were resampled from {start_freq} to {freq}")
63 |
64 | hp.warn_if_incorrect_index_length(ser, year, freq)
65 | return ser
66 |
--------------------------------------------------------------------------------
/elmada/main.py:
--------------------------------------------------------------------------------
1 | import logging
2 | from typing import Any, Callable, Dict, Iterable, List, Optional, Tuple, Union
3 |
4 | import pandas as pd
5 |
6 | import elmada
7 | from elmada import helper as hp
8 | from elmada import paths
9 |
10 | logger = logging.getLogger(__name__)
11 | logger.setLevel(level=logging.WARN)
12 |
13 |
14 | def get_emissions(
15 | year: int,
16 | freq: str = "60min",
17 | country: str = "DE",
18 | method: str = "XEF_PP",
19 | cache: bool = True,
20 | use_datetime: bool = False,
21 | **mo_kwargs,
22 | ) -> Union[pd.Series, pd.DataFrame]:
23 | """Returns dynamic carbon emisson factors in gCO2eq/kWh_el and optional data.
24 |
25 | Args:
26 | year: Year
27 | freq: Frequency, e.g. '60min' or '15min'
28 | country: alpha-2 country code, e.g. 'DE'
29 | method: A method string that consists of two parts joined by an underscore. The first
30 | part is the type of emission factor. Use 'XEF' for grid mix emission factors and
31 | 'MEF' for marginal emission factors. The second part determines the calculation method
32 | or data sources. 'EP' stands for ENTSO-E, 'PP' for power plant method, 'PWL' for piecewise
33 | linear method, 'PWLv' for the piecewise linear method in validation mode.
34 | For PP, PWL, and PWLv, the first can be omitted ('_PP', '_PWL', '_PWLv') to return
35 | a DataFrame including the columns 'residual_load', 'total_load', 'marginal_fuel',
36 | 'efficiency', 'marginal_cost', 'MEFs', 'XEFs'.
37 | The following combinations are possible:
38 |
39 | | Method | Description |
40 | | -------- | ---------------------------------------- |
41 | | XEF_EP | Series: XEFs using ENTSO-E data |
42 | | XEF_PP | Series: XEFs using PP method |
43 | | XEF_PWL | Series: XEFs using PWL method |
44 | | XEF_PWLv | Series: XEFs using PWLv method |
45 | | MEF_PP | Series: MEFs from PP method |
46 | | MEF_PWL | Series: MEFs using PWL method |
47 | | MEF_PWLv | Series: MEFs using PWLv method |
48 | | _PP | Dataframe: extended data for PP method |
49 | | _PWL | Dataframe: extended data for PWL method |
50 | | _PWLv | Dataframe: extended data for PWLv method |
51 |
52 | cache: If cache is used.
53 | use_datetime: If True, the index is a timezone agnostic datetime. If False, the index is
54 | 0, 1, 2, etc.
55 | **mo_kwargs: Keyword arguments for merit order creation such as 'overwrite_carbon_tax',
56 | 'efficiency_per_plant', 'emission_data_source'.
57 | """
58 | first_method_part, last_method_part = method.split("_")
59 |
60 | fp = paths.CACHE_DIR / f"{year}_{country}_{freq}_CEFs_{last_method_part}.parquet"
61 |
62 | if cache and fp.exists() and not mo_kwargs:
63 | df = hp.read(fp, squeeze=False)
64 | else:
65 | df = _make_emissions(
66 | year=year, freq=freq, country=country, method=last_method_part, **mo_kwargs
67 | )
68 | if cache:
69 | hp.write(df, fp)
70 |
71 | if use_datetime:
72 | df.index = hp.make_datetimeindex(year=year, freq=freq)
73 |
74 | if first_method_part in ("XEF", "MEF"):
75 | return df[first_method_part + "s"]
76 | else:
77 | return df
78 |
79 |
80 | def _make_emissions(year, freq, country, method, **mo_kwargs) -> pd.DataFrame:
81 | config = dict(year=year, freq=freq, country=country)
82 |
83 | if method == "EP":
84 | return elmada.from_entsoe.prep_XEFs(**config)
85 | # elif method == "SM":
86 | # return elmada.from_smard.prep_XEFs(**config)
87 | elif method == "PP":
88 | return elmada.from_opsd.prep_CEFs(**config, **mo_kwargs)
89 | elif method in ["PWL", "PWLv"]:
90 | is_vmode = bool(method == "PWLv")
91 | return elmada.eu_pwl.prep_CEFs(validation_mode=is_vmode, **config, **mo_kwargs)
92 | else:
93 | raise ValueError(f"Method {method} not implemented.")
94 |
95 |
96 | def get_prices(
97 | year: int,
98 | freq: str = "60min",
99 | country: str = "DE",
100 | method: str = "hist_EP",
101 | cache: bool = True,
102 | **mo_kwargs,
103 | ) -> pd.Series:
104 | """Returns the day-ahead spot-market electricity prices in €/MWh.
105 |
106 | Args:
107 | year: Year
108 | freq: Frequency, e.g. '60min' or '15min'
109 | country: alpha-2 country code, e.g. 'DE'
110 | method: 'PP' stands for power plant method, 'PWL' stands for piecewise
111 | linear method, 'PWLv' stands for the piecewise linear method in validation mode.
112 |
113 | | Method | Description |
114 | | ------- | --------------------------------------------------- |
115 | | PP | Using PP method |
116 | | PWL | Using PWL method |
117 | | PWLv | Using PWLv method |
118 | | hist_EP | Using historic ENTSO-E data |
119 | | hist_SM | Using historic Smard data only for DE, (2015, 2018 |
120 |
121 | cache: If data is cached
122 |
123 | Known data issues:
124 | - (2015, DE, entsoe)-prices: data missing until Jan 6th
125 | - (2018, DE, entsoe)-prices: data missing from Sep 30th
126 | """
127 |
128 | # >> Workaround due to missing data for DE
129 | if year in [2015, 2018] and country == "DE" and method == "hist_EP":
130 | method = "hist_SM"
131 | logger.warning(
132 | f"The requested entsoe-data ({year}, {country}) is not complete. "
133 | f"Smard-data is given to you instead."
134 | )
135 | # <<
136 |
137 | config = dict(year=year, freq=freq, country=country, cache=cache)
138 |
139 | if method == "hist_EP":
140 | return elmada.from_entsoe.prep_dayahead_prices(**config)
141 | elif method == "hist_SM":
142 | return elmada.from_smard.prep_dayahead_prices(**config)
143 | elif method in ["PP", "PWL", "PWLv"]:
144 | df = get_emissions(method=f"_{method}", **config, **mo_kwargs)
145 | return df["marginal_cost"]
146 | else:
147 | raise ValueError(f"Method '{method}' not implemented.")
148 |
149 |
150 | def get_merit_order(
151 | year: int, country: str = "DE", method: str = "PP", **mo_kwargs
152 | ) -> pd.DataFrame:
153 | """Returns the merit order as DataFrame.
154 |
155 | Args:
156 | year: Year
157 | country: alpha-2 country code, e.g. 'DE'
158 | method: One of 'PP' (power plant method), 'PWL' (piecewise
159 | linear method), 'PWLv' (piecewise linear method in validation mode).
160 | **mo_kwargs: keyword arguments for merit order function depending on `method`.
161 | """
162 | if method == "PP":
163 | assert country == "DE", f"PP-method only works for Germany and not for {country}"
164 | return elmada.from_opsd.merit_order(year=year, **mo_kwargs)
165 | elif method == "PWL":
166 | return elmada.eu_pwl.merit_order(
167 | year=year, country=country, validation_mode=False, **mo_kwargs
168 | )
169 | elif method == "PWLv":
170 | return elmada.eu_pwl.merit_order(
171 | year=year, country=country, validation_mode=True, **mo_kwargs
172 | )
173 | else:
174 | raise ValueError("`method` needs to be one of ['PP', 'PWL', 'PWLv'].")
175 |
176 |
177 | def get_residual_load(year: int, freq: str = "60min", country: str = "DE", **kwargs) -> pd.Series:
178 | return elmada.from_entsoe.prep_residual_load(year=year, freq=freq, country=country, **kwargs)
179 |
180 |
181 | def get_el_national_generation(year: int, freq: str = "60min", country: str = "DE") -> pd.DataFrame:
182 | return elmada.from_entsoe.load_el_national_generation(year=year, freq=freq, country=country)
183 |
--------------------------------------------------------------------------------
/elmada/mappings.py:
--------------------------------------------------------------------------------
1 | """Various mappings including countries, fuel types, colors.
2 |
3 | ISO 3166-1 alpha-2 country codes are used, see https://en.wikipedia.org/wiki/ISO_3166-1
4 |
5 | # TODO: there is a python module for ISO 3166-1, see https://github.com/deactivated/python-iso3166
6 |
7 | """
8 |
9 | import collections
10 | from typing import Any, Callable, Dict, Iterable, List, Optional, Set, Tuple, Union
11 |
12 | # EU 27
13 | EU = {
14 | "AT": "Austria",
15 | "BE": "Belgium",
16 | "BG": "Bulgaria",
17 | "CY": "Cyprus",
18 | "CZ": "Czech Republic",
19 | "DE": "Germany",
20 | "DK": "Denmark",
21 | "EE": "Estonia",
22 | "ES": "Spain",
23 | "FI": "Finland",
24 | "FR": "France",
25 | "GR": "Greece",
26 | "HR": "Croatia",
27 | "HU": "Hungary",
28 | "IE": "Ireland",
29 | "IT": "Italy",
30 | "LT": "Lithuania",
31 | "LU": "Luxembourg",
32 | "LV": "Latvia",
33 | "MT": "Malta",
34 | "NL": "Netherlands",
35 | "PL": "Poland",
36 | "PT": "Portugal",
37 | "RO": "Romania",
38 | "SE": "Sweden",
39 | "SI": "Slovenia",
40 | "SK": "Slovakia",
41 | }
42 |
43 | # Non-EU27 European countries
44 | OTHER = {
45 | "AD": "Andorra",
46 | "AL": "Albania",
47 | "AM": "Armenia",
48 | "AZ": "Azerbaijan",
49 | "BA": "Bosnia and Herzegovina",
50 | "BY": "Belarus",
51 | "CH": "Switzerland",
52 | "FO": "Faeroe Islands",
53 | "GB": "United Kingdom",
54 | "GE": "Georgia",
55 | "GI": "Gibraltar",
56 | "IS": "Iceland",
57 | "KZ": "Kazakhstan",
58 | "LI": "Liechtenstein",
59 | "MC": "Monaco",
60 | "MD": "Moldova",
61 | "ME": "Montenegro",
62 | "MK": "Macedonia",
63 | "NO": "Norway",
64 | "RS": "Serbia",
65 | "RU": "Russian Federation",
66 | "SM": "San Marino",
67 | "TR": "Turkey",
68 | "UA": "Ukraine",
69 | "VA": "Vatican City State",
70 | "XK": "Kosovo",
71 | }
72 |
73 | # Countries that are excluded form the analysis
74 | EXCLUDED = {
75 | "AD": "No generation data available from ENTSO-E",
76 | "AM": "No generation data available from ENTSO-E",
77 | "AZ": "No generation data available from ENTSO-E",
78 | "CY": "No generation data available from ENTSO-E",
79 | "FO": "No generation data available from ENTSO-E",
80 | "GE": "No generation data available from ENTSO-E",
81 | "GI": "No generation data available from ENTSO-E",
82 | "IS": "No generation data available from ENTSO-E",
83 | "KZ": "No generation data available from ENTSO-E",
84 | "LI": "No generation data available from ENTSO-E",
85 | "MC": "No generation data available from ENTSO-E",
86 | "MD": "No generation data available from ENTSO-E",
87 | "SM": "No generation data available from ENTSO-E",
88 | "VA": "No generation data available from ENTSO-E",
89 | "XK": "No generation data available from ENTSO-E",
90 | "LU": "No generation data available from ENTSO-E",
91 | "RU": "No generation data available from ENTSO-E",
92 | "TR": "No generation data available from ENTSO-E",
93 | "MT": "No generation data available from ENTSO-E",
94 | "HR": "No generation data available from ENTSO-E",
95 | "UA": "No generation data available from ENTSO-E",
96 | "AL": "No generation data available from ENTSO-E",
97 | "BY": "No generation data available from ENTSO-E",
98 | "BA": "Only coal as conventional generation data from ENTSO-E.",
99 | "CH": "Only nuclear as conventional generation data from ENTSO-E.",
100 | "SE": "Only nuclear as conventional generation data from ENTSO-E.",
101 | "NO": "Only gas as conventional generation data from ENTSO-E.",
102 | "LV": "Only gas as conventional generation data from ENTSO-E.",
103 | "MK": "Only lignite as conventional generation data from ENTSO-E.",
104 | "ME": "Only lignite as conventional generation data from ENTSO-E.",
105 | "EE": "Only other_conv as conventional generation data from ENTSO-E.",
106 | "BG": "No installed capacity for 2019 from ENTSO-E",
107 | "SK": "No installed capacity for 2019 from ENTSO-E",
108 | }
109 |
110 | EUROPE_COUNTRIES = {**EU, **OTHER}
111 |
112 | EUROPE_COUNTRIES_LONG_TO_SHORT = {long: short for short, long in EUROPE_COUNTRIES.items()}
113 |
114 | d = {k: v for k, v in EUROPE_COUNTRIES.items() if k not in EXCLUDED}
115 | COUNTRIES_FOR_ANALYSIS = collections.OrderedDict(sorted(d.items()))
116 |
117 | NO_GEN_DAT = {k for k, v in EXCLUDED.items() if v == "No generation data available from ENTSO-E"}
118 | d = {k: v for k, v in EUROPE_COUNTRIES.items() if k not in NO_GEN_DAT}
119 | EUROPE30 = collections.OrderedDict(sorted(d.items()))
120 |
121 | # Modified entsoe-py mapping to match whole countries
122 | NEIGHBOURS_CY = {
123 | "AT": ["CH", "CZ", "DE", "HU", "IT", "SI"],
124 | "BA": ["HR", "ME", "RS"],
125 | "BE": ["NL", "DE", "FR", "GB"],
126 | "BG": ["GR", "MK", "RO", "RS", "TR"],
127 | "CH": ["AT", "DE", "FR", "IT"],
128 | "CZ": ["AT", "DE", "PL", "SK"],
129 | "DE": ["AT", "BE", "CH", "CZ", "DK", "FR", "IT", "NL", "PL", "SE", "SI"],
130 | "DK": ["DE", "NO", "SE"],
131 | "EE": ["FI", "LV", "RU"],
132 | "ES": ["FR", "PT"],
133 | "FI": ["EE", "NO", "RU", "SE", "SE"],
134 | "FR": ["BE", "CH", "DE", "ES", "GB", "IT"],
135 | "GB": ["BE", "FR", "IE", "NL"],
136 | "GR": ["AL", "BG", "IT", "MK", "TR"],
137 | "HU": ["AT", "HR", "RO", "RS", "SK", "UA"],
138 | "IE": ["GB"],
139 | "IT": ["AT", "FR", "DE", "GR", "MT", "ME", "SI", "CH"],
140 | "LT": ["BY", "LV", "PL", "RU", "SE"],
141 | "LV": ["EE", "LT", "RU"],
142 | "ME": ["AL", "BA", "RS"],
143 | "MK": ["BG", "GR", "RS"],
144 | "MT": ["IT"],
145 | "NL": ["BE", "DE", "DE", "GB", "NO"],
146 | "NO": ["SE", "FI", "DK", "NL"],
147 | "PL": ["CZ", "DE", "DE", "LT", "SE", "SK", "UA"],
148 | "PT": ["ES"],
149 | "RO": ["BG", "HU", "RS", "UA"],
150 | "RS": ["AL", "BA", "BG", "HR", "HU", "ME", "MK", "RO"],
151 | "SE": ["FI", "NO", "DE", "DK", "LT", "PL"],
152 | "SI": ["AT", "DE", "HR", "IT"],
153 | "SK": ["CZ", "HU", "PL", "UA"],
154 | }
155 |
156 |
157 | EU_de_for_gas_price = {
158 | "AT": "Österreich",
159 | "BE": "Belgien",
160 | "BG": "Bulgarien",
161 | "CZ": "Tschechiensche\nRepublik",
162 | "DE": "Deutschland",
163 | "DK": "Dänemark",
164 | "EE": "Estland",
165 | "ES": "Spanien",
166 | "FI": "Finnland",
167 | "FR": "Frankreich",
168 | "GB": "Vereinigtes\nKönigreich",
169 | "GE": "Georgien",
170 | "GR": "Griechenland",
171 | "HR": "Kroatien",
172 | "HU": "Ungarn",
173 | "IE": "Irland",
174 | "IT": "Italien",
175 | "LT": "Litauen",
176 | "LU": "Luxemburg",
177 | "LV": "Lettland",
178 | "MK": "Mazedonien",
179 | "NL": "Niederlande",
180 | "PO": "Polen",
181 | "PT": "Portugal",
182 | "RO": "Rumänien",
183 | "SE": "Schweden",
184 | "SI": "Slowenien",
185 | "SK": "Slowakei",
186 | "TR": "Türkei",
187 | "UA": "Ukraine",
188 | }
189 |
190 | TECH_COLORS = {
191 | "load": "grey",
192 | "biomass": "olive",
193 | "hydro": "lightseagreen",
194 | "solar": "gold",
195 | "wind_onshore": "royalblue",
196 | "wind_offshore": "navy",
197 | "other_RES": "lightgreen",
198 | "nuclear": "deeppink",
199 | "lignite": "chocolate",
200 | "coal": "saddlebrown",
201 | "pumped_hydro": "mediumseagreen",
202 | "gas": "darkgreen",
203 | "gas_cc": "lightgreen",
204 | "oil": "darkgrey",
205 | "other_conv": "lightgray",
206 | "nan": "red",
207 | }
208 |
209 | ORDERED_ENTSOE_FUELS = [
210 | "Solar",
211 | "Wind Offshore",
212 | "Wind Onshore",
213 | "Other renewable",
214 | "Geothermal",
215 | "Waste",
216 | "Biomass",
217 | "Hydro Run-of-river and poundage",
218 | "Hydro Water Reservoir",
219 | "Marine",
220 | "Nuclear",
221 | "Fossil Brown coal/Lignite",
222 | "Fossil Hard coal",
223 | "Fossil Gas",
224 | "Fossil Oil",
225 | "Other_conventional",
226 | "Other",
227 | "Fossil Oil shale",
228 | "Fossil Coal-derived gas",
229 | "Fossil Peat",
230 | "Hydro Pumped Storage",
231 | ]
232 |
233 |
234 | FUEL_AGGREGATION = {
235 | "Other": "other_conv",
236 | "other_conventional": "other_conv",
237 | "Fossil Coal-derived gas": "other_conv",
238 | "Fossil Oil shale": "other_conv",
239 | "Fossil Peat": "other_conv",
240 | "Waste": "other_RES",
241 | "Geothermal": "other_RES",
242 | "Hydro Water Reservoir": "other_RES",
243 | "Marine": "other_RES",
244 | "Other renewable": "other_RES",
245 | "other renewables": "other_RES",
246 | }
247 |
248 | FUEL_RENAME = {
249 | "Biomass": "biomass",
250 | "Fossil Brown coal/Lignite": "lignite",
251 | "Fossil Oil": "oil",
252 | "Fossil Gas": "gas",
253 | "Fossil Hard coal": "coal",
254 | "Hydro Pumped Storage": "pumped_hydro",
255 | "Hydro Run-of-river and poundage": "hydro",
256 | "Nuclear": "nuclear",
257 | "Solar": "solar",
258 | "Wind Offshore": "wind_offshore",
259 | "Wind Onshore": "wind_onshore",
260 | }
261 |
262 | OPSD_TO_DRAF = {
263 | "Hard coal": "coal",
264 | "Lignite": "lignite",
265 | "Natural gas": "gas",
266 | "Nuclear": "nuclear",
267 | "Oil": "oil",
268 | }
269 |
270 | # Fuels after aggregation
271 | RES = ["hydro", "biomass", "solar", "wind_offshore", "wind_onshore", "other_RES"]
272 | CONV = ["nuclear", "lignite", "coal", "pumped_hydro", "gas", "oil", "other_conv"]
273 | DRAF_FUELS = RES + CONV
274 |
275 | # Fuels for calculation of residual load (simplified merit order simulation)
276 | CONV2 = ["nuclear", "lignite", "coal", "gas", "oil"]
277 | RES2 = [f for f in DRAF_FUELS if f not in CONV2]
278 |
279 | # Fuels for simplified merit oder simulation
280 | PWL_FUELS = ["nuclear", "lignite", "coal", "gas_cc", "gas", "oil"]
281 |
282 |
283 | def get_tech_colors(technologies: Iterable[str]) -> List[str]:
284 | """Returns list of colours for given list of technologies."""
285 | return [TECH_COLORS[i] for i in technologies]
286 |
--------------------------------------------------------------------------------
/elmada/mode.py:
--------------------------------------------------------------------------------
1 | class ConfigUtil:
2 | mode = "safe"
3 |
4 |
5 | def set_mode(mode: str):
6 | """Set data mode either to 'safe' or to 'live'."""
7 | assert mode in ["safe", "live"]
8 | ConfigUtil.mode = mode
9 |
10 |
11 | def get_mode() -> str:
12 | return ConfigUtil.mode
13 |
14 |
15 | def is_safe_mode() -> bool:
16 | return get_mode() == "safe"
17 |
--------------------------------------------------------------------------------
/elmada/paths.py:
--------------------------------------------------------------------------------
1 | from pathlib import Path
2 | from typing import Optional
3 |
4 | from appdirs import user_cache_dir, user_config_dir
5 |
6 | from elmada.mode import is_safe_mode
7 | from elmada.mappings import COUNTRIES_FOR_ANALYSIS
8 |
9 |
10 | def _get_cache_directory() -> Path:
11 | """Returns the path to cache directory and creates it, if not yet existing."""
12 | fp = Path(user_cache_dir(appname="elmada", appauthor="DrafProject"))
13 | fp.mkdir(parents=True, exist_ok=True)
14 | return fp
15 |
16 |
17 | def _get_config_directory() -> Path:
18 | """Returns the path to config directory and creates it, if not yet existing."""
19 | fp = Path(user_config_dir(appname="elmada", appauthor="DrafProject")) / "api_keys"
20 | fp.mkdir(parents=True, exist_ok=True)
21 | return fp
22 |
23 |
24 | BASE_DIR = Path(__file__).resolve().parent
25 | CACHE_DIR = _get_cache_directory()
26 | KEYS_DIR = _get_config_directory()
27 | DATA_DIR = BASE_DIR / "data/raw"
28 | SAFE_CACHE_DIR = BASE_DIR / "data/safe_cache"
29 |
30 |
31 | def mode_dependent_cache_dir(year: Optional[int] = None, country: Optional[str] = None):
32 | is_safe_year = year in range(2017, 2021) or year is None
33 | is_safe_country = country in COUNTRIES_FOR_ANALYSIS or country is None
34 | return SAFE_CACHE_DIR if (is_safe_mode() and is_safe_year and is_safe_country) else CACHE_DIR
35 |
--------------------------------------------------------------------------------
/elmada/plots.py:
--------------------------------------------------------------------------------
1 | from typing import Any, Callable, Dict, Iterable, List, Optional, Tuple, Union
2 |
3 | import matplotlib.pyplot as plt
4 | import numpy as np
5 | import pandas as pd
6 | from matplotlib.patches import Patch
7 |
8 | from elmada import helper as hp
9 | from elmada import mappings as mp
10 | from elmada.main import get_emissions, get_merit_order, get_residual_load
11 |
12 |
13 | def merit_order(
14 | year=2019,
15 | country="DE",
16 | method="PWL",
17 | ylim_left: Optional[float] = None,
18 | ylim_right: Optional[float] = None,
19 | ax: Optional["plt.axes"] = None,
20 | mo_P: Optional[pd.DataFrame] = None,
21 | include_histo: bool = False,
22 | include_legend: bool = True,
23 | legend_fontsize: float = 9,
24 | **mo_kwargs,
25 | ) -> Tuple[plt.Figure, plt.Axes, plt.Axes]:
26 | """Plot the merit order with the given method."""
27 |
28 | plt.rcParams.update({"mathtext.default": "regular"})
29 | plt.rc("text", usetex=False)
30 | plt.rc("font", family="serif")
31 |
32 | if mo_P is None:
33 | mo_P = get_merit_order(year=year, country=country, method=method, **mo_kwargs)
34 |
35 | mo_P = mo_P.copy() # avoids modifying lru_cache
36 | # the unit of mo_P.marginal_emissions is kg
37 | mo_P.cumsum_capa /= 1000 # from MW to GW
38 | mo_P.capa /= 1000 # from MW to GW
39 |
40 | if ax is not None:
41 | fig = ax.get_figure()
42 | else:
43 | fig, ax = plt.subplots(1, figsize=(7, 3))
44 |
45 | ax_right = ax.twinx()
46 |
47 | color_left = "darkblue"
48 | color_right = "black"
49 |
50 | ax.step(
51 | x=hp.add_row_for_steps(mo_P.cumsum_capa),
52 | y=hp.add_row_for_steps(mo_P.marginal_cost),
53 | where="pre",
54 | label=r"pwl $c_p^m$",
55 | drawstyle="steps",
56 | color=color_left,
57 | linewidth=1.5,
58 | )
59 |
60 | ax_right.step(
61 | x=hp.add_row_for_steps(mo_P.cumsum_capa),
62 | y=hp.add_row_for_steps(mo_P.marginal_emissions),
63 | where="pre",
64 | label=r"pwl $\varepsilon_p$",
65 | drawstyle="steps",
66 | color=color_right,
67 | linewidth=0.5,
68 | )
69 |
70 | ax.set_xlabel(r"Net power [GW$_{el}$]")
71 | ax.set_ylabel(r"Marginal cost $c_p^m$" + "\n" + r"[€/$MWh_{el}$]")
72 | ax_right.set_ylabel(
73 | r"Marginal emissions $\varepsilon_p$" + "\n" + r"[$t_{CO_{2}eq}$/$MWh_{el}$]"
74 | )
75 |
76 | plt.xlim((0, mo_P.cumsum_capa.max()))
77 | ax.set_ylim(bottom=0, top=ylim_left)
78 | ax_right.set_ylim(bottom=0, top=ylim_right)
79 |
80 | ax.yaxis.label.set_color(color_left)
81 | ax.tick_params(axis="y", colors=color_left)
82 | ax.spines["left"].set_color(color_left)
83 |
84 | ax_right.yaxis.label.set_color(color_right)
85 | ax_right.tick_params(axis="y", colors=color_right)
86 | ax_right.spines["right"].set_color(color_right)
87 |
88 | alpha_value_for_fuels = _make_colored_background_for_fuel_types(ax, mo_P)
89 |
90 | if include_legend:
91 | _add_legend(mo_P, legend_fontsize, alpha_value_for_fuels)
92 |
93 | if include_histo:
94 | _add_histo(year, country, ax_right)
95 |
96 | plt.tight_layout()
97 |
98 | return fig, ax, ax_right
99 |
100 |
101 | def _add_histo(year, country, ax_right):
102 | resi = get_residual_load(year=year, country=country) / 1000
103 | n, bins = np.histogram(resi, bins=20, density=True)
104 | ylim = ax_right.get_ylim()[1]
105 | max_loc = 0.15
106 | y = n * max_loc * (ylim / n.max())
107 | ax_right.fill_between(bins[1:], y, step="post", color="black", alpha=0.5)
108 |
109 |
110 | def _add_legend(mo_P, legend_fontsize, alpha_value_for_fuels):
111 | fuels = mo_P.fuel_draf.unique()
112 | cdict = mp.TECH_COLORS
113 | legend_elements = [
114 | Patch(facecolor=cdict[f], edgecolor=cdict[f], alpha=alpha_value_for_fuels, label=f)
115 | for f in fuels
116 | ]
117 | leg = plt.legend(
118 | handles=legend_elements,
119 | bbox_to_anchor=(0.0, 1.01, 1.0, 0.102),
120 | loc="lower left",
121 | ncol=6,
122 | mode="expand",
123 | borderaxespad=0.0,
124 | fontsize=legend_fontsize,
125 | framealpha=1.0,
126 | handletextpad=0.5,
127 | edgecolor="inherit",
128 | )
129 | leg.get_frame().set_linewidth(0.5)
130 |
131 |
132 | def _make_colored_background_for_fuel_types(ax, mo_P):
133 | alpha_value_for_fuels = 0.7
134 | for i, row in mo_P.iterrows():
135 | ax.axvspan(
136 | row["cumsum_capa"] - row["capa"],
137 | row["cumsum_capa"],
138 | facecolor=mp.TECH_COLORS[row["fuel_draf"]],
139 | linewidth=0,
140 | alpha=alpha_value_for_fuels,
141 | ymin=0,
142 | ymax=1,
143 | )
144 | return alpha_value_for_fuels
145 |
146 |
147 | def cefs_scatter_plotly(
148 | year: int = 2019, freq: str = "60min", country: str = "DE", method: str = "MEF_PWL", **mo_kwargs
149 | ):
150 | """Optional interactive scatter plot. Works only if Plotly is installed."""
151 | import plotly.express as px
152 |
153 | first_part, second_part = method.split("_")
154 |
155 | df = get_emissions(year=year, freq=freq, country=country, method=f"_{second_part}", **mo_kwargs)
156 | return px.scatter(
157 | df, y=f"{first_part}s", color="marginal_fuel", color_discrete_map=mp.TECH_COLORS
158 | )
159 |
160 |
161 | def cefs_scatter(
162 | year=2019,
163 | freq: str = "60min",
164 | country: str = "DE",
165 | method: str = "MEF_PWL",
166 | figsize: Tuple = (8, 3),
167 | **mo_kwargs,
168 | ) -> None:
169 | """Plot the CEFs as scatter plot with colors refering to marginal power plant type."""
170 | _, second_part = method.split("_")
171 |
172 | df = get_emissions(year=year, freq=freq, country=country, method=f"_{second_part}", **mo_kwargs)
173 |
174 | df = df.reset_index().set_index(["index", "marginal_fuel"])["MEFs"].unstack()
175 | df.index.name = None
176 | df.plot(
177 | marker="o",
178 | markersize=2.5,
179 | figsize=figsize,
180 | linewidth=0,
181 | color=mp.get_tech_colors(df.keys()),
182 | )
183 |
184 | ax = plt.gca()
185 | ax.set(ylabel="Carbon emission factors\n[$g_{CO_{2}eq}$ / $kWh_{el}$]")
186 | ax.legend(ncol=6, loc="lower center", bbox_to_anchor=(0.5, 1), frameon=True)
187 |
188 |
189 | def _small(s: str):
190 | return f"{s}"
191 |
192 |
193 | def cef_country_map(year: int = 2019, method: str = "XEF_PWL", scope: str = "Europe30"):
194 | """Returns a choropleth figure. Works only if Plotly is installed.
195 |
196 | Args:
197 | year: Year for data
198 | method: One of XEF_EP, XEF_PWLv, MEF_PWL
199 | scope: One of Europe20, Europe30
200 | """
201 |
202 | from iso3166 import countries
203 | import plotly.express as px
204 |
205 | d = mp.EUROPE30 if scope == "Europe30" else mp.COUNTRIES_FOR_ANALYSIS
206 | df = pd.DataFrame([(k, v) for k, v in d.items()], columns=["iso_alpha2", "country"])
207 | df["iso_alpha3"] = df.iso_alpha2.apply(lambda x: countries.get(x).alpha3)
208 | df[method] = df.iso_alpha2.apply(
209 | lambda x: get_emissions(year=year, country=x, method=method).mean()
210 | )
211 | small_adder = _small("(unsupported countries in light blue)")
212 | fig = px.choropleth(
213 | df,
214 | locations="iso_alpha3",
215 | color=method,
216 | hover_name="country",
217 | scope="europe",
218 | color_continuous_scale="OrRd",
219 | labels={method: f"{method}
gCO2eq/kWh"},
220 | title=f"{year} mean {method} of supported countries
{small_adder}",
221 | width=700,
222 | )
223 | fig.update_layout(
224 | margin=dict(l=0, r=0, t=0, b=0),
225 | title={"y": 0.12, "x": 0.5, "xanchor": "center", "yanchor": "top"},
226 | coloraxis_colorbar=dict(xpad=0, x=0.95),
227 | )
228 | return fig
229 |
--------------------------------------------------------------------------------
/environment.yml:
--------------------------------------------------------------------------------
1 | name: elmada
2 | channels:
3 | - conda-forge
4 | - anaconda
5 | dependencies: # NOTE: here '=' (single)
6 | - appdirs
7 | - black
8 | - bs4
9 | - entsoe-py=0.2.10
10 | - ipython
11 | - iso3166
12 | - isort
13 | - lxml
14 | - matplotlib
15 | - mypy
16 | - numpy
17 | - pandas
18 | - pip
19 | - plotly
20 | - pyarrow
21 | - pytest
22 | - pytest-cov
23 | - pytest-mock
24 | - python>=3.7
25 | - quandl
26 | - requests
27 | - scipy
28 | - xlrd
29 | # - notebook
30 | - pip: # NOTE: here '==' (double)
31 | - pytest-responsemock
32 | - '--editable=.[dev]' # installs an editable full version of elmada
--------------------------------------------------------------------------------
/paper/codemeta.json:
--------------------------------------------------------------------------------
1 | {
2 | "@context": "https://raw.githubusercontent.com/codemeta/codemeta/master/codemeta.jsonld",
3 | "@type": "Code",
4 | "author": [
5 | {
6 | "@id": "https://orcid.org/0000-0002-8516-9635",
7 | "@type": "Person",
8 | "name": "Markus Fleschutz",
9 | "affiliation": "Department of Process, Energy and Transport Engineering, Munster Technological University"
10 | },
11 | {
12 | "@id": "https://orcid.org/0000-0002-4269-2581",
13 | "@type": "Person",
14 | "name": "Michael D. Murphy",
15 | "affiliation": "Department of Process, Energy and Transport Engineering, Munster Technological University"
16 | }
17 | ],
18 | "identifier": "http://doi.org/10.5281/zenodo.5566694",
19 | "codeRepository": "https://github.com/DrafProject/elmada",
20 | "datePublished": "2021-07-23",
21 | "dateModified": "2021-07-23",
22 | "dateCreated": "2021-07-23",
23 | "description": "Dynamic electricity carbon emission factors and prices for Europe",
24 | "keywords": "energy market data, energy systems, carbon emission factors, marginal emissions, demand response",
25 | "license": "LGPLv3",
26 | "title": "elmada",
27 | "version": "v0.1.0"
28 | }
--------------------------------------------------------------------------------
/paper/paper.bib:
--------------------------------------------------------------------------------
1 | @article{Fleschutz2021,
2 | title = {The effect of price-based demand response on carbon emissions in European electricity markets: The importance of adequate carbon prices},
3 | author = {Markus Fleschutz and Markus Bohlayer and Marco Braun and Gregor Henze and Michael D. Murphy},
4 | journal = {Applied Energy},
5 | volume = {295},
6 | pages = {117040},
7 | year = {2021},
8 | issn = {0306-2619},
9 | doi = {10.1016/j.apenergy.2021.117040},
10 | }
11 |
12 | @online{ENTSOE,
13 | author = {{European Network of Transmission System Operators for Electricity (ENTSO-E)}},
14 | title = {{ENTSOE Transparency Platform}},
15 | year = 2021,
16 | url = {https://transparency.entsoe.eu},
17 | urldate = {2021-07-10},
18 | }
19 |
20 | @online{ElmadaGitHub,
21 | title = {Elmada},
22 | author = {Fleschutz, Markus},
23 | year = 2021,
24 | url = {https://github.com/DrafProject/elmada},
25 | urldate = {2021-07-23}
26 | }
27 |
28 | @misc{ElmadaZenodo,
29 | title = {Elmada},
30 | author = {Fleschutz, Markus},
31 | year = 2021,
32 | doi = {10.5281/zenodo.5566694}
33 | }
34 |
35 | @online{ElecMapApi,
36 | year = {2021},
37 | author = {{Tomorrow}},
38 | title = {{electricityMap API}},
39 | urldate = {2021-07-10},
40 | url = {https://api.electricitymap.org/}
41 | }
42 |
43 | @online{WattTime,
44 | year = 2021,
45 | author = {{WattTime}},
46 | title = {Automated Emissions Reduction},
47 | urldate = {2021-07-10},
48 | url = {https://www.watttime.org/aer/}
49 | }
50 |
51 |
52 | @article{Prina2020,
53 | doi = {10.1016/j.rser.2020.109917},
54 | url = {https://doi.org/10.1016/j.rser.2020.109917},
55 | year = {2020},
56 | publisher = {Elsevier {BV}},
57 | volume = {129},
58 | pages = {109917},
59 | author = {Matteo Giacomo Prina and Giampaolo Manzolini and David Moser and Benedetto Nastasi and Wolfram Sparber},
60 | title = {Classification and challenges of bottom-up energy system models - A review},
61 | journal = {Renewable and Sustainable Energy Reviews}
62 | }
63 |
64 | @article{Hawkes2010,
65 | doi = {10.1016/j.enpol.2010.05.053},
66 | author = {Hawkes, A. D.},
67 | year = {2010},
68 | title = {Estimating marginal {CO2} emissions rates for national electricity systems},
69 | keywords = {CO2;electricity;marginal},
70 | pages = {5977--5987},
71 | volume = {38},
72 | number = {10},
73 | issn = {03014215},
74 | journal = {Energy Policy}
75 | }
76 |
77 | @article{Tranberg2019,
78 | author = {Tranberg, Bo and Corradi, Olivier and Lajoie, Bruno and Gibon, Thomas and Staffell, Iain and Andresen, Gorm Bruun},
79 | year = {2019},
80 | title = {{Real-time carbon accounting method for the European electricity markets}},
81 | keywords = {Carbon accounting;Carbon emission;Carbon intensity;Flow tracing},
82 | pages = {100367},
83 | volume = {26},
84 | issn = {2211467X},
85 | journal = {{Energy Strategy Reviews}},
86 | doi = {10.1016/j.esr.2019.100367},
87 | }
--------------------------------------------------------------------------------
/paper/paper.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: 'elmada: Dynamic electricity carbon emission factors and prices for Europe'
3 | tags:
4 | - energy
5 | - electricity market
6 | - carbon emissions
7 | - marginal emissions
8 | - python
9 | authors:
10 | - name: Markus Fleschutz
11 | orcid: 0000-0002-8516-9635
12 | affiliation: 1, 2
13 | - name: Michael D. Murphy
14 | orcid: 0000-0002-4269-2581
15 | affiliation: 1
16 | affiliations:
17 | - name: Department of Process, Energy and Transport Engineering, Munster Technological University
18 | index: 1
19 | - name: Institute of Refrigeration, Air-Conditioning, and Environmental Engineering, Karlsruhe University of Applied Sciences
20 | index: 2
21 | date: 23 July 2021
22 | bibliography: paper.bib
23 | ---
24 |
25 | # Summary
26 |
27 | The expansion of intermittent renewable energy sources such as solar and wind requires increased operational flexibility in electricity systems.
28 | Energy system models at the scale of individual decentral energy hubs can help decision-makers of energy hubs such as city quarters or industrial sites evaluate the cost and carbon emission saving potentials of their flexibility.
29 | For national scale models, the carbon emissions of the electricity supply system are endogenously determined.
30 | However, low-level models (at the scale of decentral energy hubs) need this information as input.
31 | Since specific carbon emissions of national electricity supply systems fluctuate hourly, the usage of dynamic (i.e. at least hourly resolved) carbon emission factors (CEFs) is essential [@Prina2020].
32 |
33 | `elmada` is an easy-to-use open-source Python package designed to provide dynamic electricity CEFs and prices for European countries.
34 | The target group includes modelers of distributed energy hubs who need electricity market data.
35 | This is where the name **elmada** comes from: **el**ectricity **ma**rket **da**ta.
36 | `elmada` is developed in the open on GitHub [@ElmadaGitHub].
37 | Each release is archived on Zenodo [@ElmadaZenodo].
38 |
39 | # Statement of Need
40 |
41 | Dynamic CEFs are important for the environmental assessment of electricity supply in not fully decarbonized energy systems.
42 | To the best of the authors' knowledge, `elmada` is the first free and open-source Python interface for dynamic CEFs in Europe.
43 | This makes `elmada` an important complement to existing commercial services.
44 |
45 | At the moment, there are two main commercial services that provide an Application Programming Interface (API) for historical dynamic CEFs: the `electricityMap` API [@ElecMapApi] and the Automated Emissions Reduction from WattTime [@WattTime].
46 | The `electricityMap` is maintained by Tomorrow, a startup based in Denmark, and WattTime is a nonprofit organization in the USA.
47 | However, both focus on real-time CEFs as incentive signals for demand response answering the question "How clean is my electricity right now?".
48 | We elaborate more on `electricityMap` here, as they originate in Europe, which is also the focus of `elmada`.
49 | The services of WattTime are broadly similar.
50 |
51 | `electricityMap` is a software project that visualizes the carbon emission intensity linked to the generation and consumption of electricity on a global choropleth map.
52 | Additionally, the `electricityMap` API provides historical, real-time (current hour), forecast, and since recently also marginal data. The calculation methods consider international energy exchanges and the fact that the list of data sources is curated by Tomorrow (the company behind it) makes it save-to-use as a live incentive signal e.g. for carbon-based demand response applications.
53 | However, the use of `electricityMap` API requires a data-dependent payment even for the historic data, so it is not free of charge.
54 |
55 | There are two types of dynamic CEFs:
56 |
57 | * grid-mix emission factors (XEFs), which represent the emission intensity based on the current generation mix of the electricity system,
58 | * and marginal emission factors (MEFs), which quantify the emission intensity of the generators likely to react to a marginal system change.
59 |
60 | Currently, there is no multi-national solution for modelers of decentral energy hubs searching for free historical hourly CEFs (in particular MEFs).
61 | This gap often leads to the usage of yearly average CEFs, which are potentially misleading [@Hawkes2010].
62 | We close this gap by providing the conveniently installable Python package `elmada` that calculates XEFs and MEFs in hourly (or higher) resolution for 30 European countries for free.
63 | `elmada` provides modelers a no-regret (free) entry point to European dynamic CEFs leaving it open to the modeler to later switch to a paid service that generate more accurate CEFs, e.g. through advanced methods such as the consideration of cross-border energy flows through flow tracing [@Tranberg2019].
64 |
65 | # Functionality
66 |
67 | `elmada` calculates both types of dynamic CEFs: XEFs and MEFs.
68 | MEFs are more challenging to approximate than XEFs since MEFs require the identification of the marginal power plants per time step.
69 | In `elmada`, this is done through a merit order simulation within the power plant (PP) and piecewise linear (PWL) method described in [@Fleschutz2021].
70 |
71 | Also, historical and simulated day-ahead electricity market prices are provided.
72 | They can be used either for the economic evaluation of electricity demands or to model the incentive signal of price-based demand response.
73 |
74 | Currently, `elmada` provides data for 30 European countries and for each year since 2017.
75 | `elmada` works mainly with data from the ENTSO-E Transparency Platform [@ENTSOE].
76 |
77 | # Current and Future Usage
78 |
79 | So far, `elmada` has been used in a study where MEFs, XEFs and the results of load shift simulations based on them are compared across 20 European countries [@Fleschutz2021].
80 | In ongoing research, `elmada` is used to quantify the costs and emission-saving potentials that arise from the exploitation of existing and future flexibility in decentral energy hubs.
81 |
82 | We hope that `elmada` reduces the difficulty associated with the use of dynamic CEFs and prices in the modeling of decentral energy systems.
83 |
84 | # Acknowledgements
85 |
86 | The author acknowledges funding by the MTU Risam scholarship scheme and the German Federal Ministry of the Environment, Nature Conservation and Nuclear Safety (BMU) via the project WIN4Climate (No. 03KF0094 A) as part of the National Climate Initiative.
87 |
88 | # References
89 |
--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------
1 | [tool.black]
2 | line-length = 100
3 | skip-magic-trailing-comma = true
4 | exclude = '''
5 | /(
6 | \.eggs
7 | | \.git
8 | | \.github
9 | | \.mypy_cache
10 | | \.pytest_cache
11 | | \.vscode
12 | | .*\.egg-info
13 | # directories without python source files
14 | | doc
15 | )/
16 | '''
17 |
18 | [tool.isort]
19 | line_length = 100
20 | include_trailing_comma = true
21 | profile = "black"
22 |
--------------------------------------------------------------------------------
/pytest.ini:
--------------------------------------------------------------------------------
1 | [pytest]
2 | addopts =
3 | --strict-markers
4 | --cov=elmada
5 | --cov-report=term-missing
6 | ; --cov-report=html
7 | ; -m="not apikey"
8 | markers =
9 | apikey: marks tests that are dependent on an api key
10 | wantcache: marks test that are fast when CEFs are already cached
11 | python_files = test_*.py
12 | python_functions = test_*
13 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | from pathlib import Path
2 |
3 | from setuptools import find_packages, setup
4 |
5 | long_description = Path("README.md").read_text().strip()
6 |
7 | setup(
8 | name="elmada",
9 | version="0.1.1",
10 | author="Markus Fleschutz",
11 | author_email="mfleschutz@gmail.com",
12 | description="Dynamic electricity carbon emission factors and prices for Europe",
13 | long_description=long_description,
14 | long_description_content_type="text/markdown",
15 | url="https://github.com/DrafProject/elmada",
16 | license="LGPLv3",
17 | packages=find_packages(exclude=["doc", "tests"]),
18 | python_requires=">=3.7",
19 | install_requires=[
20 | "appdirs",
21 | "beautifulsoup4",
22 | "entsoe-py",
23 | "ipython",
24 | "lxml",
25 | "matplotlib",
26 | "numpy",
27 | "pandas",
28 | "pyarrow",
29 | "quandl",
30 | "requests",
31 | "scipy",
32 | "xlrd",
33 | ],
34 | extras_require={
35 | "dev": [
36 | "black",
37 | "iso3166",
38 | "isort",
39 | "mypy",
40 | "plotly",
41 | "pytest-cov",
42 | "pytest-mock",
43 | "pytest-responsemock",
44 | "pytest",
45 | ]
46 | },
47 | include_package_data=True,
48 | package_data={"elmada": ["*.parquet", "*.csv", "*.txt", "*xls"]},
49 | classifiers=[
50 | "Development Status :: 4 - Beta",
51 | "Intended Audience :: Developers",
52 | "Intended Audience :: Science/Research",
53 | "License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)",
54 | "Natural Language :: English",
55 | "Operating System :: OS Independent",
56 | "Programming Language :: Python :: 3 :: Only",
57 | "Programming Language :: Python :: 3",
58 | "Topic :: Scientific/Engineering :: Information Analysis",
59 | "Topic :: Scientific/Engineering",
60 | "Topic :: Software Development :: Libraries :: Python Modules",
61 | ],
62 | keywords=["energy market data", "energy systems", "carbon emission factors", "demand response"],
63 | )
64 |
--------------------------------------------------------------------------------
/tests/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/tests/__init__.py
--------------------------------------------------------------------------------
/tests/common/CEF_hashes.csv:
--------------------------------------------------------------------------------
1 | year,country,hash
2 | 2017,AT,1bb9addc1e
3 | 2017,BE,4042e619ec
4 | 2017,CZ,b0e2ce1686
5 | 2017,DE,d647ed210e
6 | 2017,DK,5a7ac0c19f
7 | 2017,ES,8d45a6ad21
8 | 2017,FI,ecf2892b92
9 | 2017,FR,73655f4685
10 | 2017,GB,3d1bef9034
11 | 2017,GR,c38a72b989
12 | 2017,HU,98f9b22f69
13 | 2017,IE,9d4c3084b7
14 | 2017,IT,0744c6f60a
15 | 2017,LT,a0082eb6e7
16 | 2017,NL,513b6287a2
17 | 2017,PL,913ce0c500
18 | 2017,PT,cb277b0911
19 | 2017,RO,0510717cc7
20 | 2017,RS,a593272138
21 | 2017,SI,fef58f4333
22 | 2018,AT,775d6aca97
23 | 2018,BE,5431c95125
24 | 2018,CZ,710cf6391a
25 | 2018,DE,fb0e86ab4a
26 | 2018,DK,f1172f4451
27 | 2018,ES,22b11c96e8
28 | 2018,FI,d6b1c5ad37
29 | 2018,FR,8e0b03e29d
30 | 2018,GB,1b0102e608
31 | 2018,GR,c3a01d7511
32 | 2018,HU,d27a910ce5
33 | 2018,IE,b7ebc7cc45
34 | 2018,IT,a7f60936fd
35 | 2018,LT,bccc1e5351
36 | 2018,NL,a4cf092415
37 | 2018,PL,6177bf9ff0
38 | 2018,PT,fe1b845335
39 | 2018,RO,adc088da0d
40 | 2018,RS,876c5c640a
41 | 2018,SI,86cabddf10
42 | 2019,AT,927088e3a5
43 | 2019,BE,666e86c022
44 | 2019,CZ,6001e05ad0
45 | 2019,DE,dc18650c05
46 | 2019,DK,99f24a6bca
47 | 2019,ES,3e00c63778
48 | 2019,FI,fe2ebc8e3e
49 | 2019,FR,2e1a7bea03
50 | 2019,GB,3849b8d15c
51 | 2019,GR,f1e93c0074
52 | 2019,HU,ae3e0e290d
53 | 2019,IE,1519d1a542
54 | 2019,IT,2357510854
55 | 2019,LT,1bc9360d8d
56 | 2019,NL,5ca410f7cb
57 | 2019,PL,93328aed5e
58 | 2019,PT,1c34a3f551
59 | 2019,RO,7ca2717477
60 | 2019,RS,2e526053e4
61 | 2019,SI,11811b7578
62 | 2020,AT,ac5fede203
63 | 2020,BE,bc23ed0bae
64 | 2020,CZ,83689b14ce
65 | 2020,DE,140ff5e062
66 | 2020,DK,7fc5af282d
67 | 2020,ES,5b529815f1
68 | 2020,FI,e527a9d374
69 | 2020,FR,71aeb8b006
70 | 2020,GB,fbd87e0351
71 | 2020,GR,b913a22b6e
72 | 2020,HU,3636284fb5
73 | 2020,IE,ae747993c0
74 | 2020,IT,07af4beb52
75 | 2020,LT,ba582d8030
76 | 2020,NL,4a1de84d1e
77 | 2020,PL,02c5d8b6bc
78 | 2020,PT,6579b5ad75
79 | 2020,RO,8486012d96
80 | 2020,RS,60bfe300f7
81 | 2020,SI,b1884fb6db
82 |
--------------------------------------------------------------------------------
/tests/common/GEN_hashes.csv:
--------------------------------------------------------------------------------
1 | year,country,hash
2 | 2017,AT,fe66313ddf
3 | 2017,BE,808cd31201
4 | 2017,CZ,9f32abf640
5 | 2017,DE,b9b9582772
6 | 2017,DK,8c5382a651
7 | 2017,ES,68d0782345
8 | 2017,FI,bb0e6e1971
9 | 2017,FR,19deac2f57
10 | 2017,GB,221225874e
11 | 2017,GR,3330bf31fe
12 | 2017,HU,461a4e27c5
13 | 2017,IE,04861e5a83
14 | 2017,IT,1aee7f632d
15 | 2017,LT,b0a8f5d14c
16 | 2017,NL,12cd28396e
17 | 2017,PL,a7d9d0cce4
18 | 2017,PT,0e93cd9014
19 | 2017,RO,66c337624d
20 | 2017,RS,0e94b1076e
21 | 2017,SI,af86e269d6
22 | 2018,AT,a37190c476
23 | 2018,BE,c0324fe7b0
24 | 2018,CZ,a67879b56e
25 | 2018,DE,c2bdf939bf
26 | 2018,DK,45b6ad6c54
27 | 2018,ES,eb7b8b4ba8
28 | 2018,FI,9e886a535b
29 | 2018,FR,5aacaba48a
30 | 2018,GB,2600752851
31 | 2018,GR,97a42a9fcb
32 | 2018,HU,123e5031db
33 | 2018,IE,259874686e
34 | 2018,IT,69ed26e5ac
35 | 2018,LT,9b92905b1c
36 | 2018,NL,8c4310b299
37 | 2018,PL,32f055e886
38 | 2018,PT,d9fd771d52
39 | 2018,RO,4bbb6f554a
40 | 2018,RS,536dad0844
41 | 2018,SI,bde77fe111
42 | 2019,AT,568716c069
43 | 2019,BE,487519cfe3
44 | 2019,CZ,329f5235fd
45 | 2019,DE,e7384eadcf
46 | 2019,DK,23c2306a92
47 | 2019,ES,41aea05fa1
48 | 2019,FI,705ad8362e
49 | 2019,FR,29c9aac986
50 | 2019,GB,717da182c7
51 | 2019,GR,db8c98c92a
52 | 2019,HU,fb78d0a53d
53 | 2019,IE,fd7276fa80
54 | 2019,IT,9107b933ba
55 | 2019,LT,f738f0d6e6
56 | 2019,NL,47a4fa07a9
57 | 2019,PL,47a986b698
58 | 2019,PT,5bdcbaa107
59 | 2019,RO,bab341bcbf
60 | 2019,RS,c0db6df1af
61 | 2019,SI,6d4591ea55
62 | 2020,AT,8813bbb825
63 | 2020,BE,e8dab4bb36
64 | 2020,CZ,e0d3485da4
65 | 2020,DE,819a8148c6
66 | 2020,DK,768f6eac33
67 | 2020,ES,ba0a4f6ee1
68 | 2020,FI,f5eaecbd3b
69 | 2020,FR,00e513ee6e
70 | 2020,GB,83ac1a8f1b
71 | 2020,GR,d112f819b7
72 | 2020,HU,b7f758b8eb
73 | 2020,IE,bda2d42e84
74 | 2020,IT,3dc716dbda
75 | 2020,LT,3c5328fd71
76 | 2020,NL,3ab5aab28e
77 | 2020,PL,6d05f76515
78 | 2020,PT,98657b7329
79 | 2020,RO,ce2416fb8b
80 | 2020,RS,2fcfda9ed5
81 | 2020,SI,03133e7a1b
82 |
--------------------------------------------------------------------------------
/tests/common/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrafProject/elmada/9af3cf4496ea10ff74e087283974813d6341eade/tests/common/__init__.py
--------------------------------------------------------------------------------
/tests/common/averages.py:
--------------------------------------------------------------------------------
1 | from pathlib import Path
2 |
3 | import pandas as pd
4 |
5 | import elmada
6 | from elmada import mappings as mp
7 |
8 | MYDIR = Path(__file__).resolve().parent
9 |
10 |
11 | def make_avgs() -> pd.DataFrame:
12 | return pd.Series(
13 | {
14 | (str(year), country, cef_type): int(
15 | elmada.get_emissions(year=year, country=country, method=f"{cef_type}_PWL").mean()
16 | )
17 | for year in range(2017, 2021)
18 | for country in mp.COUNTRIES_FOR_ANALYSIS
19 | for cef_type in ("MEF", "XEF")
20 | },
21 | ).unstack([0])
22 |
23 |
24 | def save_expected_avgs() -> None:
25 | fp = MYDIR / "avgs.csv"
26 | if input("Do you really want to overwrite the data average table? (y/n)") == "y":
27 | make_avgs().to_csv(fp, header=True)
28 | else:
29 | print("Aborted.")
30 |
31 |
32 | def read_expected_avgs() -> pd.Series:
33 | fp = MYDIR / "avgs.csv"
34 | return pd.read_csv(fp, index_col=[0, 1])
35 |
--------------------------------------------------------------------------------
/tests/common/avgs.csv:
--------------------------------------------------------------------------------
1 | ,,2017,2018,2019,2020
2 | AT,MEF,591,574,550,501
3 | AT,XEF,145,131,139,125
4 | BE,MEF,448,260,473,449
5 | BE,XEF,88,35,103,69
6 | CZ,MEF,995,985,979,964
7 | CZ,XEF,399,407,395,355
8 | DE,MEF,871,826,890,896
9 | DE,XEF,416,415,349,342
10 | DK,MEF,791,821,796,790
11 | DK,XEF,234,266,191,186
12 | ES,MEF,569,559,624,681
13 | ES,XEF,272,233,270,199
14 | FI,MEF,731,672,724,693
15 | FI,XEF,143,159,149,100
16 | FR,MEF,74,62,59,5
17 | FR,XEF,2,2,1,0
18 | GB,MEF,700,660,586,626
19 | GB,XEF,298,252,209,208
20 | GR,MEF,807,867,918,889
21 | GR,XEF,718,679,667,620
22 | HU,MEF,710,780,726,709
23 | HU,XEF,349,317,329,315
24 | IE,MEF,536,535,568,495
25 | IE,XEF,423,398,352,351
26 | IT,MEF,505,497,483,494
27 | IT,XEF,384,348,354,366
28 | LT,MEF,728,723,726,743
29 | LT,XEF,252,195,223,323
30 | NL,MEF,614,573,530,533
31 | NL,XEF,448,417,524,525
32 | PL,MEF,840,826,880,877
33 | PL,XEF,808,820,785,766
34 | PT,MEF,587,595,610,620
35 | PT,XEF,397,328,348,314
36 | RO,MEF,1110,1112,1059,1064
37 | RO,XEF,454,423,407,362
38 | RS,MEF,1114,1104,1111,1130
39 | RS,XEF,757,714,744,766
40 | SI,MEF,1010,936,974,992
41 | SI,XEF,295,256,248,261
42 |
--------------------------------------------------------------------------------
/tests/common/geo_test_list.html:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | List of Gas PowerPlants - GEO
12 |
13 |
16 |
17 |
18 |
19 |
20 |
22 |
56 |
57 |
58 |
--------------------------------------------------------------------------------
/tests/common/hasher.py:
--------------------------------------------------------------------------------
1 | import hashlib
2 | from pathlib import Path
3 |
4 | import pandas as pd
5 | from numpy.core.arrayprint import str_format
6 |
7 | import elmada
8 | from elmada import mappings as mp
9 |
10 | HASH_DIR = Path(__file__).resolve().parent
11 | FILE_FMT = "{which}_hashes.csv"
12 |
13 |
14 | def read_expected_hashes(which: str) -> pd.Series:
15 | fp = HASH_DIR / FILE_FMT.format(which=which)
16 | return pd.read_csv(fp, index_col=[0, 1]).squeeze()
17 |
18 |
19 | def save_expected_hashes(which: str) -> None:
20 | fp = HASH_DIR / FILE_FMT.format(which=which)
21 | if input("Do you really want to overwrite the data hash table? (y/n)") == "y":
22 | make_hashes(which=which).to_csv(fp, header=True)
23 | else:
24 | print("Aborted.")
25 |
26 |
27 | def make_hashes(which) -> pd.Series:
28 |
29 | if which == "CEF":
30 | func = elmada.get_emissions
31 | kwargs = dict(freq="60min", method="_PWL", cache=True)
32 |
33 | elif which == "GEN":
34 | func = elmada.get_el_national_generation
35 | kwargs = dict(freq="60min")
36 |
37 | else:
38 | raise ValueError("`which` must be 'CEF' or 'GEN'.")
39 |
40 | return make_year_country_hashes(func=func, kwargs=kwargs)
41 |
42 |
43 | def make_year_country_hashes(func, kwargs) -> pd.Series:
44 | ser = pd.Series(
45 | {
46 | (year, country): get_hash(func(year=year, country=country, **kwargs))
47 | for year in range(2017, 2021)
48 | for country in mp.COUNTRIES_FOR_ANALYSIS
49 | },
50 | name="hash",
51 | )
52 | ser.index.names = ["year", "country"]
53 | return ser
54 |
55 |
56 | def get_hash(df) -> str_format:
57 | return hashlib.sha256(pd.util.hash_pandas_object(df, index=True).values).hexdigest()[:10]
58 |
--------------------------------------------------------------------------------
/tests/test_cc_share.py:
--------------------------------------------------------------------------------
1 | import collections
2 | from pathlib import Path
3 |
4 | import pandas as pd
5 |
6 | from elmada import cc_share
7 |
8 |
9 | def test_get_ccgt_shares_from_cascade():
10 | df = cc_share.get_ccgt_shares_from_cascade()
11 | assert isinstance(df, pd.DataFrame)
12 |
13 |
14 | def test_get_wiki_shares():
15 | result = cc_share.get_wiki_shares(cache=False)
16 | assert isinstance(result, pd.Series)
17 |
18 |
19 | def get_geo_shares_overview():
20 | result = cc_share.get_geo_shares_overview()
21 | assert isinstance(result, pd.DataFrame)
22 |
23 |
24 | def test_get_valid_countries():
25 | result = cc_share.get_valid_countries()
26 | assert isinstance(result, collections.OrderedDict)
27 |
28 |
29 | def test_get_geo_list():
30 | result = cc_share.get_geo_list(cache=True)
31 | assert isinstance(result, pd.DataFrame)
32 |
33 |
34 | def _read_fake_geo_body():
35 | fp = Path(__file__).parent / "common/geo_test_list.html"
36 | return fp.read_text()
37 |
38 |
39 | def test__scrape_geo_list(response_mock):
40 | url = "http://globalenergyobservatory.org/list.php?db=PowerPlants&type=Gas"
41 | fake_geo_body = _read_fake_geo_body()
42 | with response_mock(f"GET {url} -> 200 :{fake_geo_body}"):
43 | df = cc_share.get_geo_list(cache=False)
44 | assert isinstance(df, pd.DataFrame)
45 | assert df.loc[1, "country"] == "Albania"
46 | print(df)
47 |
48 |
49 | def test_get_geo_shares_overview():
50 | cc_share.get_geo_shares_overview()
51 |
--------------------------------------------------------------------------------
/tests/test_data.py:
--------------------------------------------------------------------------------
1 | import pytest
2 |
3 | from .common import averages, hasher
4 |
5 |
6 | def make_and_check_hashes(which: str) -> bool:
7 | current = hasher.make_hashes(which=which)
8 | expected = hasher.read_expected_hashes(which=which)
9 | return current.equals(expected)
10 |
11 |
12 | # @pytest.mark.wantcache
13 | # def test_emissions_data():
14 | # assert make_and_check_hashes("CEF")
15 |
16 |
17 | def test_get_el_national_generation():
18 | assert make_and_check_hashes("GEN")
19 |
20 |
21 | def test_averages():
22 | current = averages.make_avgs()
23 | expected = averages.read_expected_avgs()
24 | assert current.equals(expected)
25 |
--------------------------------------------------------------------------------
/tests/test_eu_pwl.py:
--------------------------------------------------------------------------------
1 | import pandas as pd
2 | import pytest
3 |
4 | from elmada import eu_pwl
5 |
6 |
7 | def test_prep_prices(mocker):
8 | mock = mocker.patch("elmada.eu_pwl.prep_CEFs", return_value={"marginal_cost": 42})
9 | result = eu_pwl.prep_prices(year=2019, freq="60min", country="DE")
10 | assert result == 42
11 | mock.assert_called_once_with(year=2019, freq="60min", country="DE")
12 |
13 |
14 | @pytest.mark.wantcache
15 | def test_prep_CEFs(mocker):
16 | mock = mocker.patch("elmada.from_opsd.get_CEFs_from_merit_order")
17 | eu_pwl.prep_CEFs(year=2019, freq="60min", country="DE")
18 | mock.assert_called_once()
19 |
20 |
21 | def test_approximate_min_max_values_raises_error():
22 | result = eu_pwl.approximate_min_max_values(method="interp")
23 | assert isinstance(result, tuple)
24 |
25 | with pytest.raises(ValueError):
26 | eu_pwl.approximate_min_max_values(method="this_method_is_not_implemented")
27 |
28 |
29 | def test_prep_installed_generation_capacity():
30 | with pytest.raises(ValueError):
31 | eu_pwl.prep_installed_generation_capacity(source="this_method_is_not_implemented")
32 |
33 |
34 | def test_get_pp_size():
35 | with pytest.raises(ValueError):
36 | eu_pwl.get_pp_size(
37 | pp_size_method="this_method_is_not_implemented", country="DE", fuel="gas"
38 | )
39 |
40 |
41 | def test_get_pp_sizes_from_germany():
42 | result = eu_pwl.get_pp_sizes_from_germany()
43 | assert isinstance(result, pd.Series)
44 |
--------------------------------------------------------------------------------
/tests/test_from_entsoe.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | import pandas as pd
3 | import pytest
4 | from entsoe import EntsoePandasClient
5 |
6 | from elmada import exceptions, from_entsoe
7 | from elmada import helper as hp
8 | from elmada import mappings as mp
9 | from elmada import paths
10 |
11 | country_resi_means = [
12 | ("AT", 1791),
13 | ("BE", 8285),
14 | ("CZ", 8143),
15 | ("DE", 31805),
16 | ("DK", 764),
17 | ("ES", 17111),
18 | ("FI", 4181),
19 | ("FR", 49036),
20 | ("GB", 20016),
21 | ("GR", 3244),
22 | ("HU", 3157),
23 | ("IE", 1560),
24 | ("IT", 18920),
25 | ("LT", 131),
26 | ("NL", 9014),
27 | ("PL", 14572),
28 | ("PT", 2804),
29 | ("RO", 3934),
30 | ("RS", 2786),
31 | ("SI", 1154),
32 | ]
33 |
34 |
35 | @pytest.mark.parametrize("country,mean", country_resi_means)
36 | def test_prep_residual_load(country, mean):
37 | assert mean == pytest.approx(
38 | from_entsoe.prep_residual_load(year=2019, country=country).mean(), 0.01
39 | )
40 |
41 |
42 | def test_prep_residual_load_with_conv2(mocker):
43 | mock = mocker.patch("elmada.from_entsoe.get_conv2_generation")
44 | from_entsoe.prep_residual_load(year=2019, country="DE", method="conv2")
45 | mock.assert_called_once()
46 |
47 |
48 | def test_prep_residual_load_method_not_implemented():
49 | with pytest.raises(ValueError):
50 | from_entsoe.prep_residual_load(
51 | year=2019, country="DE", method="this_method_is_not_implemented"
52 | )
53 |
54 |
55 | def test_prep_XEFs():
56 | result = from_entsoe.prep_XEFs(year=2019, freq="60min", country="DE")
57 | assert isinstance(result, pd.DataFrame)
58 |
59 |
60 | @pytest.mark.apikey
61 | def test_load_installed_generation_capacity():
62 | result = from_entsoe.load_installed_generation_capacity(year=2019, country="DE", cache=False)
63 | assert isinstance(result, pd.DataFrame)
64 |
65 |
66 | def test_prep_dayahead_prices(mocker):
67 | dummy = pd.Series(30, index=range(8760))
68 | dummy[2] = np.nan
69 | dummy[5] = 5000.0
70 | dummy.index = hp.make_datetimeindex(year=2019, freq="60min", tz="Europe/Berlin")
71 | assert isinstance(dummy.index, pd.DatetimeIndex)
72 |
73 | mocker.patch("elmada.from_entsoe._query_day_ahead_prices", return_value=dummy)
74 | result = from_entsoe.prep_dayahead_prices(year=2019, freq="60min", country="DE", cache=False)
75 |
76 | assert isinstance(result, pd.Series)
77 | assert result[2] == 30
78 | assert result[5] == 30
79 | assert isinstance(result.index, pd.RangeIndex)
80 |
81 |
82 | def test_get_empty_installed_generation_capacity_df():
83 | result = from_entsoe._get_empty_installed_generation_capacity_df(ensure_std_techs=True)
84 | expected = pd.DataFrame(index=mp.DRAF_FUELS, columns=[""]).T
85 | assert result.equals(expected)
86 |
87 | with pytest.raises(exceptions.NoDataError):
88 | from_entsoe._get_empty_installed_generation_capacity_df(ensure_std_techs=False)
89 |
90 |
91 | def test_get_conv2_generation():
92 | result = from_entsoe.get_conv2_generation(year=2019, freq="60min", country="DE")
93 | assert isinstance(result, pd.Series)
94 |
95 |
96 | def test_get_renewable_generation():
97 | result = from_entsoe.get_renewable_generation(year=2019, freq="60min", country="DE")
98 | assert isinstance(result, pd.Series)
99 |
100 |
101 | @pytest.mark.apikey
102 | def test_load_el_national_generation(mocker):
103 | df = hp.read(paths.SAFE_CACHE_DIR / "2019_DE_gen_entsoe.parquet")
104 | mock = mocker.patch("entsoe.EntsoePandasClient.query_generation", return_value=df)
105 | result = from_entsoe.load_el_national_generation(
106 | year=2019, country="DE", freq="15min", cache=False, split_queries=False
107 | )
108 | mock.assert_called()
109 | assert isinstance(result, pd.DataFrame)
110 |
111 |
112 | def test_get_bidding_zone():
113 | assert from_entsoe.get_bidding_zone(country="AT", year=2017) == "DE-AT-LU"
114 |
115 |
116 | def test_query_day_ahead_prices(mocker):
117 | mocker.patch("elmada.from_entsoe._get_client", return_value=EntsoePandasClient)
118 | mock = mocker.patch("entsoe.EntsoePandasClient.query_day_ahead_prices")
119 | from_entsoe._query_day_ahead_prices(
120 | 2019, bidding_zone=from_entsoe.get_bidding_zone(country="DE", year=2019)
121 | )
122 | mock.assert_called_once()
123 |
124 |
125 | def test__query_generation(mocker):
126 | mocker.patch("elmada.from_entsoe._get_client", return_value=EntsoePandasClient)
127 | mock = mocker.patch("entsoe.EntsoePandasClient.query_generation", return_value=pd.DataFrame())
128 | from_entsoe._query_generation(year=2019, country="DE", split_queries=False)
129 | mock.assert_called_once()
130 |
131 | mock = mocker.patch("entsoe.EntsoePandasClient.query_generation", return_value=pd.DataFrame())
132 | from_entsoe._query_generation(year=2019, country="DE", split_queries=True)
133 | assert mock.call_count == 12
134 |
--------------------------------------------------------------------------------
/tests/test_from_geo_scraped.py:
--------------------------------------------------------------------------------
1 | import pandas as pd
2 |
3 | from elmada import from_geo_scraped
4 |
5 | from .common.hasher import get_hash
6 |
7 |
8 | def test_get_pp_sizes_for_pwl():
9 | df = from_geo_scraped.get_pp_sizes_for_pwl()
10 | assert get_hash(df) == "f8ea5251f2"
11 |
12 |
13 | def test_get_pp_sizes():
14 | df = from_geo_scraped.get_pp_sizes()
15 | assert get_hash(df) == "fcf6b2ba28"
16 |
17 |
18 | def test_get_units_of_geo_list():
19 | df = from_geo_scraped.get_units_of_geo_list(cache=True)
20 | expected = pd.Index(
21 | ["cy", "fuel", "geoid", "capa", "eff", "commissioned", "unit_no"], dtype="object"
22 | )
23 | assert df.keys().equals(expected)
24 |
25 |
26 | def test__query_geo_power_plant_data(mocker):
27 | mock = mocker.patch("elmada.from_geo_scraped.get_df_from_geo_id", return_value=pd.DataFrame())
28 | from_geo_scraped._query_geo_power_plant_data()
29 | mock.assert_called()
30 |
31 |
32 | def test_get_df_from_geo_id():
33 | # NOTE: Requires internet connection
34 | df = from_geo_scraped.get_df_from_geo_id(45151)
35 | assert get_hash(df) == "f6be9a1b2b"
36 |
--------------------------------------------------------------------------------
/tests/test_from_geo_via_morph.py:
--------------------------------------------------------------------------------
1 | from elmada import from_geo_via_morph, paths
2 |
3 |
4 | def test_download_database(mocker):
5 | mock = mocker.patch("elmada.helper.get_api_key", return_value="123")
6 | mock = mocker.patch("requests.get")
7 | type(mock.return_value).content = mocker.PropertyMock(return_value="xx".encode())
8 | fp = paths.CACHE_DIR / "test_file.csv"
9 | from_geo_via_morph.download_database(fp=fp)
10 | assert fp.exists()
11 | fp.unlink()
12 | mock.assert_called_once()
13 |
--------------------------------------------------------------------------------
/tests/test_from_opsd.py:
--------------------------------------------------------------------------------
1 | import pandas as pd
2 | import pytest
3 | from pytest_mock import MockerFixture
4 |
5 | import elmada
6 | from elmada import from_opsd
7 | from elmada import helper as hp
8 | from elmada import paths
9 | from elmada.mode import ConfigUtil
10 |
11 |
12 | def test_prep_prices(mocker):
13 | mock = mocker.patch("elmada.from_opsd.prep_CEFs", return_value={"marginal_cost": 42})
14 | assert from_opsd.prep_prices(year=2019, freq="60min", country="DE") == 42
15 | mock.assert_called_once_with(year=2019, freq="60min", country="DE")
16 |
17 |
18 | def test_prep_CEFs():
19 | cefs = from_opsd.prep_CEFs(year=2019, freq="60min", country="DE")
20 |
21 | expected = pd.Index(
22 | data=[
23 | "residual_load",
24 | "total_load",
25 | "marginal_fuel",
26 | "efficiency",
27 | "marginal_cost",
28 | "MEFs",
29 | "XEFs",
30 | ],
31 | dtype="object",
32 | )
33 | assert cefs.keys().equals(expected)
34 |
35 | assert hp.is_correct_length(cefs, year=2019, freq="60min")
36 |
37 | assert isinstance(cefs, pd.DataFrame)
38 |
39 |
40 | @pytest.mark.apikey
41 | def test_read_opsd_powerplant_list(mocker: MockerFixture):
42 | mocker.patch.object(ConfigUtil, "mode", "safe")
43 | assert elmada.get_mode() == "safe"
44 | df = from_opsd.read_opsd_powerplant_list()
45 | assert len(df) == 893
46 | assert "fuel" in df
47 |
48 |
49 | def test_get_summary():
50 | result = from_opsd.get_summary()
51 | assert isinstance(result, pd.DataFrame)
52 |
53 |
54 | def test_get_summary_of_opsd_raw():
55 | result = from_opsd.get_summary_of_opsd_raw()
56 | assert isinstance(result, pd.DataFrame)
57 |
58 |
59 | def test_download_powerplant_list(mocker):
60 | mock = mocker.patch("requests.get")
61 | type(mock.return_value).content = mocker.PropertyMock(return_value="xx".encode())
62 | fp = paths.CACHE_DIR / "test_file.csv"
63 | from_opsd.download_powerplant_list(which="DE", fp=fp)
64 | assert fp.exists()
65 | fp.unlink()
66 | mock.assert_called_once()
67 |
--------------------------------------------------------------------------------
/tests/test_from_other.py:
--------------------------------------------------------------------------------
1 | from typing import Dict
2 |
3 | import pandas as pd
4 | import pytest
5 |
6 | from elmada import from_other
7 |
8 |
9 | def test_get_transmission_efficiency_series():
10 | assert isinstance(from_other.get_transmission_efficiency_series(cache=True), pd.Series)
11 | assert isinstance(from_other.get_transmission_efficiency_series(cache=False), pd.Series)
12 |
13 |
14 | def test_get_transmission_efficiency():
15 | result = from_other.get_transmission_efficiency("THIS_IS_NOT_A_VALID_COUNTRY")
16 | assert isinstance(result, float)
17 |
18 |
19 | def test__get_light_oil_conversion_factor():
20 | assert from_other._get_light_oil_conversion_factor(source=1) == 0.980
21 |
22 |
23 | def test_get_ice_eua_prices_with_cache():
24 | result = from_other.get_ice_eua_prices(cache=True)
25 | assert isinstance(result, Dict)
26 |
27 |
28 | @pytest.mark.apikey
29 | def test_get_ice_eua_prices_without_cache():
30 | result = from_other.get_ice_eua_prices(cache=False)
31 | assert isinstance(result, Dict)
32 |
33 |
34 | def test_get_sandbag_eua_prices():
35 | result = from_other.get_sandbag_eua_prices()
36 | assert isinstance(result, Dict)
37 |
--------------------------------------------------------------------------------
/tests/test_from_smard.py:
--------------------------------------------------------------------------------
1 | import pandas as pd
2 |
3 | from elmada import from_smard
4 |
5 |
6 | def test_prep_dayahead_prices():
7 | result = from_smard.prep_dayahead_prices(year=2018, country="DE", cache=False)
8 | assert isinstance(result, pd.Series)
9 | result = from_smard.prep_dayahead_prices(year=2015, country="DE", cache=False)
10 | assert isinstance(result, pd.Series)
11 |
--------------------------------------------------------------------------------
/tests/test_helper.py:
--------------------------------------------------------------------------------
1 | from pathlib import Path
2 |
3 | import pandas as pd
4 | import pytest
5 |
6 | from elmada import helper as hp
7 | from elmada import paths
8 |
9 |
10 | @pytest.mark.parametrize(
11 | "freq, year, month, day, expected",
12 | [["60min", 2020, 6, 6, 3768], ["30min", 1990, 3, 12, 3360], ["15min", 1999, 1, 1, 0]],
13 | )
14 | def test_datetime_to_int(freq: str, year: int, month: int, day: int, expected: int):
15 | assert hp.datetime_to_int(freq, year, month, day) == expected
16 |
17 |
18 | @pytest.mark.parametrize(
19 | "freq, year, pos, expected",
20 | [
21 | ["15min", 1999, 10, pd.Timestamp("1999-01-01 02:30:00")],
22 | ["45min", 2003, 34, pd.Timestamp("2003-01-02 01:30:00")],
23 | ["60min", 2020, 1, pd.Timestamp("2020-01-01 01:00:00")],
24 | ],
25 | )
26 | def test_int_to_datetime(freq: str, year: int, pos: int, expected: pd.Timestamp):
27 | assert hp.int_to_datetime(freq=freq, year=year, pos=pos) == expected
28 |
29 |
30 | @pytest.mark.parametrize(
31 | "year, freq, tz", [[2019, "60min", None], [1999, "15min", None], [2099, "45min", None]]
32 | )
33 | def test_make_datetimeindex(year: int, freq: str, tz: str):
34 | assert isinstance(hp.make_datetimeindex(year=year, freq=freq, tz=tz), pd.DatetimeIndex)
35 |
36 |
37 | @pytest.mark.parametrize(
38 | "freq, expected", [["60min", 60], ["30min", 30], ["45min", 45], ["15min", 15]]
39 | )
40 | def test_int_from_freq(freq: str, expected: int):
41 | assert hp.int_from_freq(freq=freq) == expected
42 |
43 |
44 | @pytest.mark.parametrize("freq, expected", [["60min", 1.0], ["30min", 0.5], ["15min", 0.25]])
45 | def test_freq_to_hours(freq: str, expected: float):
46 | assert hp.freq_to_hours(freq=freq) == expected
47 |
48 |
49 | def test_fill_outlier_and_nan():
50 | ser = pd.Series(6 * [1] + [50] + 4 * [2])
51 | assert ser[6] == 50
52 | assert hp.fill_outlier_and_nan(ser, method="linear")[6] == 1.5
53 | assert hp.fill_outlier_and_nan(ser, method="fill")[6] == 2
54 |
55 |
56 | def test_remove_outlier():
57 | x = 6 * [1.0] + [50.0] + 4 * [2.0]
58 | df = pd.DataFrame.from_dict({"a": x, "b": x})
59 | result = hp.remove_outlier(df)
60 | assert result.iloc[6].isna().sum() == 2
61 |
62 |
63 | def test_delete_cache(mocker, capsys):
64 | hp.delete_cache("XXXX")
65 | captured = capsys.readouterr()
66 | assert captured.out == "No file found containing 'XXXX'.\n"
67 |
68 | fp = paths.CACHE_DIR / "test_file_XXXX.parquet"
69 | tuplelist = [(True, "1 files deleted\n"), (False, "No files deleted\n")]
70 |
71 | for patch_value, msg in tuplelist:
72 | fp.touch(exist_ok=True)
73 | assert fp.exists()
74 | mocker.patch("elmada.helper.confirm_deletion", return_value=patch_value)
75 | hp.delete_cache("test_file_XXXX")
76 | captured = capsys.readouterr()
77 | assert msg in captured.out
78 |
79 | try:
80 | fp.unlink()
81 | except FileNotFoundError:
82 | pass
83 |
84 | assert not fp.exists()
85 |
86 |
87 | def test_print_error(capsys):
88 | a = pd.Series(10, range(10))
89 | b = a * 1.1
90 | hp.print_error(a, b)
91 | captured = capsys.readouterr()
92 | captured.out == "relative error = 10.00%, absolute error= 1.00 t CO2/MWh.\n"
93 |
94 |
95 | def test_add_row_for_steps():
96 | ser = pd.Series(range(5))
97 | result = hp.add_row_for_steps(ser)
98 | assert len(result) == 6
99 | assert result.iloc[0] == 0
100 |
101 |
102 | def test__append_rows():
103 | some_list = [1, 2, 3]
104 | ser = pd.Series(some_list)
105 | assert len(ser) == 3
106 | result = hp._append_rows(ser, 4)
107 | assert len(result) == 6
108 |
109 | df = pd.DataFrame(dict(a=ser, b=ser))
110 | assert len(ser) == 3
111 | result = hp._append_rows(df, 4)
112 | assert len(result) == 6
113 |
114 | with pytest.raises(RuntimeError):
115 | hp._append_rows(some_list, 4)
116 |
117 |
118 | def test_sizeof_fmt():
119 | assert hp.sizeof_fmt(5000) == "5.0 KB"
120 | assert hp.sizeof_fmt(2e24) == "2.0 YB"
121 |
122 |
123 | def test_write():
124 | ser = pd.Series(range(4))
125 | path_scheme = paths.CACHE_DIR / "test_file_XXX.suffix"
126 |
127 | with pytest.raises(ValueError):
128 | hp.write(ser, path_scheme)
129 |
130 | for suffix in [".parquet", ".csv"]:
131 | fp = path_scheme.with_suffix(suffix)
132 | assert not fp.exists()
133 | hp.write(ser, fp)
134 | assert fp.exists()
135 | fp.unlink()
136 |
137 |
138 | def test_read(mocker):
139 | suffices = ("parquet", "csv")
140 | funcs = ("pandas.read_parquet", "pandas.read_csv")
141 | for suffix, func in zip(suffices, funcs):
142 | fp = Path(f"_.{suffix}")
143 | mock = mocker.patch(func, return_value=pd.DataFrame({0: [1, 2, 3]}))
144 | result = hp.read(fp).squeeze()
145 | assert isinstance(result, pd.Series)
146 | result = hp.read(fp)
147 | assert isinstance(result, pd.DataFrame)
148 | assert mock.called
149 |
150 | with pytest.raises(ValueError):
151 | hp.read(Path("spam.egg"))
152 |
153 |
154 | def test_make_symlink_to_cache(mocker):
155 | mock = mocker.patch.object(Path, "symlink_to")
156 | hp.make_symlink_to_cache()
157 | mock.assert_called()
158 |
159 |
160 | def test_set_and_get_api_key(mocker, monkeypatch):
161 | key_dir = Path(__file__).parent / "common"
162 | mocker.patch("elmada.paths.KEYS_DIR", key_dir)
163 |
164 | # make sure there is no api key stored as environment variable
165 | monkeypatch.delenv(hp.APIS["entsoe"][2], raising=False)
166 |
167 | fp = key_dir / "entsoe.txt"
168 | if fp.exists():
169 | fp.unlink()
170 | hp.set_api_keys(entsoe="123")
171 | assert hp.get_api_key(which="entsoe") == "123"
172 | fp.unlink()
173 |
174 |
175 | def test_estimate_freq():
176 | ser = pd.Series(range(7000))
177 | with pytest.raises(RuntimeError):
178 | hp.estimate_freq(ser)
179 |
--------------------------------------------------------------------------------
/tests/test_main.py:
--------------------------------------------------------------------------------
1 | import pandas as pd
2 | import pytest
3 |
4 | import elmada
5 |
6 |
7 | def test_get_emissions(mocker):
8 | config = dict(year=2019, freq="60min", country="DE")
9 |
10 | methodtuples = [
11 | ("_EP", "elmada.from_entsoe.prep_XEFs", config),
12 | ("_PP", "elmada.from_opsd.prep_CEFs", config),
13 | ("_PWL", "elmada.eu_pwl.prep_CEFs", dict(**config, validation_mode=False)),
14 | ("_PWLv", "elmada.eu_pwl.prep_CEFs", dict(**config, validation_mode=True)),
15 | ]
16 |
17 | for (method, func, kwargs) in methodtuples:
18 | print(method, func, kwargs)
19 | mock = mocker.patch(func)
20 | elmada.get_emissions(**config, cache=False, method=method)
21 | mock.assert_called_once_with(**kwargs)
22 |
23 |
24 | pp_keys = pd.Index(
25 | [
26 | "id",
27 | "name_bnetza",
28 | "block_bnetza",
29 | "name_uba",
30 | "company",
31 | "street",
32 | "postcode",
33 | "city",
34 | "state",
35 | "country_code",
36 | "capa",
37 | "capacity_gross_uba",
38 | "fuel",
39 | "technology",
40 | "chp",
41 | "chp_capacity_uba",
42 | "commissioned",
43 | "commissioned_original",
44 | "retrofit",
45 | "shutdown",
46 | "status",
47 | "type",
48 | "lat",
49 | "lon",
50 | "eic_code_plant",
51 | "eic_code_block",
52 | "efficiency_data",
53 | "efficiency_source",
54 | "efficiency_estimate",
55 | "energy_source_level_1",
56 | "energy_source_level_2",
57 | "energy_source_level_3",
58 | "eeg",
59 | "network_node",
60 | "voltage",
61 | "network_operator",
62 | "merge_comment",
63 | "comment",
64 | "fuel_draf",
65 | "filler",
66 | "eta_k",
67 | "used_eff",
68 | "marginal_emissions_for_gen",
69 | "x_k",
70 | "fuel_cost",
71 | "GHG_cost",
72 | "marginal_emissions",
73 | "marginal_cost",
74 | "cumsum_capa",
75 | ],
76 | dtype="object",
77 | )
78 |
79 | pwl_keys = pd.Index(
80 | [
81 | "capa",
82 | "used_eff",
83 | "fuel_draf",
84 | "x_k",
85 | "fuel_cost",
86 | "GHG_cost",
87 | "marginal_emissions",
88 | "marginal_cost",
89 | "cumsum_capa",
90 | ],
91 | dtype="object",
92 | )
93 |
94 |
95 | @pytest.mark.parametrize(
96 | "method,expected_keys", [("PP", pp_keys), ("PWL", pwl_keys), ("PWLv", pwl_keys)]
97 | )
98 | def test_get_merit_order(method, expected_keys):
99 | df = elmada.get_merit_order(year=2019, country="DE", method=method)
100 | assert isinstance(df, pd.DataFrame)
101 | df.keys().equals(expected_keys)
102 |
103 |
104 | def test_get_residual_load(mocker):
105 | config = dict(year=2019, freq="60min", country="DE")
106 | mock = mocker.patch("elmada.from_entsoe.prep_residual_load", return_value=True)
107 | assert elmada.from_entsoe.prep_residual_load(**config)
108 | mock.assert_called_once_with(**config)
109 |
110 |
111 | def test_get_prices(mocker):
112 | config = dict(year=2019, freq="60min", country="DE", cache=True)
113 | ep_mock = mocker.patch("elmada.from_entsoe.prep_dayahead_prices", return_value=True)
114 | assert elmada.get_prices(**config, method="hist_EP")
115 | ep_mock.assert_called_once_with(**config)
116 |
117 | sm_mock = mocker.patch("elmada.from_smard.prep_dayahead_prices", return_value=True)
118 | assert elmada.get_prices(**config, method="hist_SM")
119 | sm_mock.assert_called_once_with(**config)
120 |
121 | pp_mock = mocker.patch("elmada.main.get_emissions", return_value={"marginal_cost": True})
122 | elmada.get_prices(**config, method="PP")
123 | pp_mock.assert_called_once_with(**config, method="_PP")
124 |
125 | pwl_mock = mocker.patch("elmada.main.get_emissions", return_value={"marginal_cost": True})
126 | elmada.get_prices(**config, method="PWL")
127 | pwl_mock.assert_called_once_with(**config, method="_PWL")
128 |
--------------------------------------------------------------------------------
/tests/test_mode.py:
--------------------------------------------------------------------------------
1 | import pytest
2 |
3 | import elmada
4 | from elmada import mode
5 |
6 |
7 | def test_set_mode():
8 | mode_backup = mode.get_mode()
9 |
10 | mode.set_mode("safe")
11 | assert mode.get_mode() == "safe"
12 |
13 | mode.set_mode("live")
14 | assert mode.get_mode() == "live"
15 |
16 | with pytest.raises(AssertionError):
17 | mode.set_mode("other")
18 |
19 | mode.set_mode(mode_backup)
20 |
21 |
22 | def test_is_safe_mode(mocker):
23 | mocker.patch("elmada.mode.get_mode", return_value="safe")
24 | assert mode.is_safe_mode()
25 |
--------------------------------------------------------------------------------
/tests/test_plots.py:
--------------------------------------------------------------------------------
1 | import matplotlib.pyplot as plt
2 | import elmada
3 |
4 |
5 | def test_merit_order():
6 | for method in ["PWL", "PP"]:
7 | plt.close()
8 | elmada.plots.merit_order(year=2019, country="DE", method=method, include_histo=True)
9 | assert plt.gcf().number == 1
10 | plt.close()
11 |
12 |
13 | def test_cefs_scatter():
14 | for method in ["XEF_PWL", "MEF_PWL", "XEF_PP", "MEF_PWL"]:
15 | plt.close()
16 | elmada.plots.cefs_scatter(year=2019, country="DE", method=method)
17 | assert plt.gcf().number == 1
18 | plt.close()
19 |
20 |
21 | def test_cef_country_map():
22 | elmada.plots.cef_country_map(year=2019, method="XEF_PWL", scope="Europe20")
23 |
24 |
25 | def test_cefs_scatter_plotly():
26 | elmada.plots.cefs_scatter_plotly(year=2019, freq="60min", country="DE", method="MEF_PWL")
27 |
--------------------------------------------------------------------------------