diff --git a/.gitignore b/.gitignore
index 83c1397e6..54b36be88 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,6 +10,7 @@ __pycache__
*dconf
gurobi.log
.vscode
+.idea
*.orig
/bak
@@ -25,6 +26,7 @@ gurobi.log
/tmp
doc/_build
+
/scripts/old
/scripts/create_scenarios.py
/config/create_scenarios.py
@@ -56,6 +58,14 @@ dask-worker-space/
publications.jrc.ec.europa.eu/
d1gam3xoknrgr2.cloudfront.net/
+#latex files in report/
+report/*.aux
+report/*.bbl
+report/*.blg
+report/*.log
+report/*.out
+report/*.toc
+
*.org
*.nc
diff --git a/.reuse/dep5 b/.reuse/dep5
index 251189232..0f7829d69 100644
--- a/.reuse/dep5
+++ b/.reuse/dep5
@@ -34,3 +34,7 @@ License: CC0-1.0
Files: borg-it
Copyright: 2017-2024 The PyPSA-Eur Authors
License: CC0-1.0
+
+Files: report/*
+Copyright: Open Energy Transition gGmbH
+License: MIT
diff --git a/README.md b/README.md
index 1ed6f43cf..2eb2d11ef 100644
--- a/README.md
+++ b/README.md
@@ -13,96 +13,86 @@ SPDX-License-Identifier: CC-BY-4.0
[![REUSE status](https://api.reuse.software/badge/github.com/pypsa/pypsa-eur)](https://api.reuse.software/info/github.com/pypsa/pypsa-eur)
[![Stack Exchange questions](https://img.shields.io/stackexchange/stackoverflow/t/pypsa)](https://stackoverflow.com/questions/tagged/pypsa)
-# PyPSA-Eur: A Sector-Coupled Open Optimisation Model of the European Energy System
-
-PyPSA-Eur is an open model dataset of the European energy system at the
-transmission network level that covers the full ENTSO-E area. The model is suitable both for operational studies and generation and transmission expansion planning studies.
-The continental scope and highly resolved spatial scale enables a proper description of the long-range
-smoothing effects for renewable power generation and their varying resource availability.
-
-
-
-
-The model is described in the [documentation](https://pypsa-eur.readthedocs.io)
-and in the paper
-[PyPSA-Eur: An Open Optimisation Model of the European Transmission
-System](https://arxiv.org/abs/1806.01613), 2018,
-[arXiv:1806.01613](https://arxiv.org/abs/1806.01613).
-The model building routines are defined through a snakemake workflow.
-Please see the [documentation](https://pypsa-eur.readthedocs.io/)
-for installation instructions and other useful information about the snakemake workflow.
-The model is designed to be imported into the open toolbox
-[PyPSA](https://github.com/PyPSA/PyPSA).
-
-**WARNING**: PyPSA-Eur is under active development and has several
-[limitations](https://pypsa-eur.readthedocs.io/en/latest/limitations.html) which
-you should understand before using the model. The github repository
-[issues](https://github.com/PyPSA/pypsa-eur/issues) collect known topics we are
-working on (please feel free to help or make suggestions). The
-[documentation](https://pypsa-eur.readthedocs.io/) remains somewhat patchy. You
-can find showcases of the model's capabilities in the Joule paper [The potential
-role of a hydrogen network in
-Europe](https://doi.org/10.1016/j.joule.2023.06.016), another [paper in Joule
-with a description of the industry
-sector](https://doi.org/10.1016/j.joule.2022.04.016), or in [a 2021 presentation
-at EMP-E](https://nworbmot.org/energy/brown-empe.pdf). We do not recommend to
-use the full resolution network model for simulations. At high granularity the
-assignment of loads and generators to the nearest network node may not be a
-correct assumption, depending on the topology of the underlying distribution
-grid, and local grid bottlenecks may cause unrealistic load-shedding or
-generator curtailment. We recommend to cluster the network to a couple of
-hundred nodes to remove these local inconsistencies. See the discussion in
-Section 3.4 "Model validation" of the paper.
-
-
-![PyPSA-Eur Grid Model](doc/img/elec.png)
-
-The dataset consists of:
-
-- A grid model based on a modified [GridKit](https://github.com/bdw/GridKit)
- extraction of the [ENTSO-E Transmission System
- Map](https://www.entsoe.eu/data/map/). The grid model contains 7072 lines
- (alternating current lines at and above 220kV voltage level and all high
- voltage direct current lines) and 3803 substations.
-- The open power plant database
- [powerplantmatching](https://github.com/PyPSA/powerplantmatching).
-- Electrical demand time series from the
- [OPSD project](https://open-power-system-data.org/).
-- Renewable time series based on ERA5 and SARAH, assembled using the [atlite tool](https://github.com/PyPSA/atlite).
-- Geographical potentials for wind and solar generators based on land use (CORINE) and excluding nature reserves (Natura2000) are computed with the [atlite library](https://github.com/PyPSA/atlite).
-
-A sector-coupled extension adds demand
-and supply for the following sectors: transport, space and water
-heating, biomass, industry and industrial feedstocks, agriculture,
-forestry and fishing. This completes the energy system and includes
-all greenhouse gas emitters except waste management and land use.
-
-This diagram gives an overview of the sectors and the links between
-them:
-
-![sector diagram](doc/img/multisector_figure.png)
-
-Each of these sectors is built up on the transmission network nodes
-from [PyPSA-Eur](https://github.com/PyPSA/pypsa-eur):
-
-![network diagram](https://github.com/PyPSA/pypsa-eur/blob/master/doc/img/base.png?raw=true)
-
-For computational reasons the model is usually clustered down
-to 50-200 nodes.
-
-Already-built versions of the model can be found in the accompanying [Zenodo
-repository](https://doi.org/10.5281/zenodo.3601881).
-
-# Contributing and Support
-We strongly welcome anyone interested in contributing to this project. If you have any ideas, suggestions or encounter problems, feel invited to file issues or make pull requests on GitHub.
-- In case of code-related **questions**, please post on [stack overflow](https://stackoverflow.com/questions/tagged/pypsa).
-- For non-programming related and more general questions please refer to the [mailing list](https://groups.google.com/group/pypsa).
-- To **discuss** with other PyPSA users, organise projects, share news, and get in touch with the community you can use the [discord server](https://discord.com/invite/AnuJBk23FU).
-- For **bugs and feature requests**, please use the [PyPSA-Eur Github Issues page](https://github.com/PyPSA/pypsa-eur/issues).
-
-# Licence
-
-The code in PyPSA-Eur is released as free software under the
-[MIT License](https://opensource.org/licenses/MIT), see [`doc/licenses.rst`](doc/licenses.rst).
-However, different licenses and terms of use may apply to the various
-input data, see [`doc/data_sources.rst`](doc/data_sources.rst).
+# EU-Flex Platform
+
+
+
+## Overview
+The EU-Flex Platform is an implementation of ACER's EU-wide flexibility assessment platform based on PyPSA-Eur. This project develops a comprehensive modeling tool for assessing power system flexibility needs across the European Union, as mandated by Regulation (EU) 2024/1747.
+
+## Project Scope
+This repository covers Phase 1 (Implementation) and Phase 2 (Testing) of the EU-Flex Platform development:
+
+### Phase 1: Implementation (Dec 2024 - Nov 2025)
+- Development of core modeling capabilities using PyPSA
+- Implementation of flexibility assessment functionalities
+- Integration of various data sources and power system components
+- Development of APIs and user interfaces
+- Setting up security and operational protocols
+
+### Phase 2: Testing (Dec 2025 - June 2026)
+- Comprehensive testing with complete datasets
+- Performance optimization and refinement
+- Integration testing with real-world scenarios
+- System validation and quality assurance
+- Methodology alignment and adjustments
+
+## Key Features (WIP)
+- EU-wide power system modeling
+- Flexibility needs assessment across multiple timeframes
+- Integration of renewable energy sources
+- Analysis of demand-side response and storage options
+- Cross-border capacity evaluation
+- Stochastic analysis capabilities
+
+## Repository structure
+
+* `benchmarks`: will store `snakemake` benchmarks (does not exist initially)
+* `config`: configurations used in the study
+* `cutouts`: will store raw weather data cutouts from `atlite` (does not exist initially)
+* `data`: includes input data that is not produced by any `snakemake` rule
+* `doc`: includes all files necessary to build the `readthedocs` documentation of PyPSA-Eur
+* `envs`: includes all the `mamba` environment specifications to run the workflow
+* `logs`: will store log files (does not exist initially)
+* `notebooks`: includes all the `notebooks` used for ad-hoc analysis
+* `report`: contains all files necessary to build the report; plots and result files are generated automatically
+* `rules`: includes all the `snakemake`rules loaded in the `Snakefile`
+* `resources`: will store intermediate results of the workflow which can be picked up again by subsequent rules (does not exist initially)
+* `results`: will store the solved PyPSA network data, summary files and plots (does not exist initially)
+* `scripts`: includes all the Python scripts executed by the `snakemake` rules to build the model
+
+## Technology Stack
+- [OET/PyPSA-Eur](https://github.com/open-energy-transition/pypsa-eur) used via the [OET's soft-fork strategy](https://open-energy-transition.github.io/handbook/docs/Engineering/SoftForkStrategy). The model is described in the [documentation](https://pypsa-eur.readthedocs.io) and in the paper
+[PyPSA-Eur: An Open Optimisation Model of the European Transmission System](https://arxiv.org/abs/1806.01613), 2018, [arXiv:1806.01613](https://arxiv.org/abs/1806.01613).
+- [Snakemake](https://snakemake.readthedocs.io) for workflow management
+
+## Installation and Usage
+
+### 1. Installation
+
+Clone the repository:
+
+ git clone https://github.com/open-energy-transition/{{repository}}
+
+You need [mamba](https://mamba.readthedocs.io/en/latest/) to run the analysis. Users may also prefer to use [micromamba](https://mamba.readthedocs.io/en/latest/installation/micromamba-installation.html) or [conda](https://docs.conda.io/projects/conda/en/latest/index.html). Using `mamba`, you can create an environment from within you can run it:
+
+ mamba env create -f environment.yaml
+
+Activate the newly created `{{project_short_name}}` environment:
+
+ mamba activate {{project_short_name}}
+
+### 2. Run the analysis
+
+ snakemake -call
+
+This will run all analysis steps to reproduce results and build the report.
+
+To generate a PDF of the dependency graph of all steps `resources/dag.pdf` run:
+
+ snakemake -c1 dag
+
+* Open Energy Transition (g)GmbH, Königsallee 52, 95448 Bayreuth, Germany
+
+## Access and Usage
+This platform is being developed for ACER (European Union Agency for the Cooperation of Energy Regulators). Access and usage rights are restricted according to ACER's policies and regulations.
diff --git a/Snakefile b/Snakefile
index 79e58c573..e0db636fd 100644
--- a/Snakefile
+++ b/Snakefile
@@ -54,6 +54,7 @@ include: "rules/solve_electricity.smk"
include: "rules/postprocess.smk"
include: "rules/validate.smk"
include: "rules/development.smk"
+include: "rules/report.smk"
if config["foresight"] == "overnight":
diff --git a/config/config.default.yaml b/config/config.default.yaml
index 64197dbbe..94c12fc99 100644
--- a/config/config.default.yaml
+++ b/config/config.default.yaml
@@ -710,6 +710,7 @@ sector:
biogas_upgrading_cc: false
conventional_generation:
OCGT: gas
+ keep_existing_capacities: false
biomass_to_liquid: false
biomass_to_liquid_cc: false
electrobiofuels: false
diff --git a/doc/configtables/sector.csv b/doc/configtables/sector.csv
index ba98adee0..61b7d4726 100644
--- a/doc/configtables/sector.csv
+++ b/doc/configtables/sector.csv
@@ -178,6 +178,7 @@ biomass_spatial,--,"{true, false}",Add option for resolving biomass demand regio
biomass_transport,--,"{true, false}",Add option for transporting solid biomass between nodes
biogas_upgrading_cc,--,"{true, false}",Add option to capture CO2 from biomass upgrading
conventional_generation,,,Add a more detailed description of conventional carriers. Any power generation requires the consumption of fuel from nodes representing that fuel.
+keep_existing_capacities,--,"{true, false}",Keep existing conventional carriers from the power model. Defaults to false.
biomass_to_liquid,--,"{true, false}",Add option for transforming solid biomass into liquid fuel with the same properties as oil
biomass_to_liquid_cc,--,"{true, false}",Add option for transforming solid biomass into liquid fuel with the same properties as oil with carbon capture
biosng,--,"{true, false}",Add option for transforming solid biomass into synthesis gas with the same properties as natural gas
diff --git a/doc/release_notes.rst b/doc/release_notes.rst
index 749b96b0b..52e1032a8 100644
--- a/doc/release_notes.rst
+++ b/doc/release_notes.rst
@@ -479,6 +479,11 @@ PyPSA-Eur 0.12.0 (30th August 2024)
* Address various deprecations.
+* Allow running the sector model for isolated non-EU28 countries, by filling missing sectoral
+ data with defaults, average EU values or zeros, if not available.
+
+* Enable retaining exisiting conventional capacities added in the power only model for sector coupeled applications.
+
PyPSA-Eur 0.11.0 (25th May 2024)
=====================================
diff --git a/report/references.bib b/report/references.bib
new file mode 100644
index 000000000..c239cf057
--- /dev/null
+++ b/report/references.bib
@@ -0,0 +1,8 @@
+% SPDX-FileCopyrightText: Open Energy Transition gGmbH
+%
+% SPDX-License-Identifier: MIT
+
+@misc{ Nobody06,
+ author = "Nobody Jr",
+ title = "My Article",
+ year = "2006" }
\ No newline at end of file
diff --git a/report/report.tex b/report/report.tex
new file mode 100644
index 000000000..7988756f6
--- /dev/null
+++ b/report/report.tex
@@ -0,0 +1,205 @@
+% SPDX-FileCopyrightText: Open Energy Transition gGmbH
+%
+% SPDX-License-Identifier: MIT
+
+\documentclass[5p]{elsarticle}
+
+\journal{TBA}
+
+\bibliographystyle{elsarticle-num}
+\biboptions{numbers,sort&compress}
+
+% format hacks
+\usepackage{libertine}
+\usepackage{libertinust1math}
+% \usepackage{geometry}
+% \geometry{
+% top=30mm,
+% bottom=35mm,
+% }
+
+\usepackage{amsmath}
+\usepackage{bbold}
+\usepackage{graphicx}
+\usepackage{eurosym}
+\usepackage{mathtools}
+\usepackage{url}
+\usepackage{booktabs}
+\usepackage{epstopdf}
+\usepackage{xfrac}
+\usepackage{tabularx}
+\usepackage{bm}
+\usepackage{subcaption}
+\usepackage{blindtext}
+\usepackage{longtable}
+\usepackage{multirow}
+\usepackage{threeparttable}
+\usepackage{pdflscape}
+\usepackage[export]{adjustbox}
+\usepackage[version=4]{mhchem}
+\usepackage[colorlinks]{hyperref}
+\usepackage[parfill]{parskip}
+\usepackage[nameinlink,sort&compress,capitalise,noabbrev]{cleveref}
+\usepackage[leftcaption,raggedright]{sidecap}
+\usepackage[prependcaption,textsize=footnotesize]{todonotes}
+
+\usepackage{siunitx}
+\sisetup{
+ range-units = single,
+ per-mode = symbol
+}
+\DeclareSIUnit\year{a}
+\DeclareSIUnit{\tco}{t_{\ce{CO2}}}
+\DeclareSIUnit{\sieuro}{\mbox{\euro}}
+
+\usepackage{lipsum}
+
+\usepackage[resetlabels,labeled]{multibib}
+\newcites{S}{Supplementary References}
+\bibliographystyleS{elsarticle-num}
+
+\graphicspath{
+ {../results/graphics/},
+}
+
+% \usepackage[
+% type={CC},
+% modifier={by},
+% version={4.0},
+% ]{doclicense}
+
+\newcommand{\abs}[1]{\left|#1\right|}
+\newcommand{\norm}[1]{\left\lVert#1\right\rVert}
+\newcommand{\set}[1]{\left\{#1\right\} }
+\DeclareMathOperator*{\argmin}{\arg\!\min}
+\newcommand{\R}{\mathbb{R} }
+\newcommand{\B}{\mathbb{B} }
+\newcommand{\N}{\mathbb{N} }
+\newcommand{\co}{\ce{CO2}~}
+\def\el{${}_{\textrm{el}}$}
+\def\th{${}_{\textrm{th}}$}
+\def\deg{${}^\circ$}
+
+\begin{document}
+
+ \begin{frontmatter}
+
+ \title{ project name }
+
+ \author[affil]{ name surname } % \corref{correspondingauthor}
+ \ead{ name.surname@openenergytransition.org }
+
+ \address[affil]{ affiliation }
+
+ \begin{abstract}
+ \input{sections/abstract.tex}
+ \end{abstract}
+
+ \begin{keyword}
+ TODO
+ \end{keyword}
+
+ % \begin{graphicalabstract}
+ % \end{graphicalabstract}
+
+ \begin{highlights}
+ \item A
+ \item B
+ \item C
+ \end{highlights}
+
+\end{frontmatter}
+
+% \listoftodos[TODOs]
+
+% \tableofcontents
+
+\section{Introduction}
+\label{sec:intro}
+
+\input{sections/introduction.tex}
+
+\section{Methods}
+\label{sec:methods}
+
+\input{sections/methods.tex}
+
+\section{Results}
+\label{sec:results}
+
+\input{sections/results.tex}
+
+\section{Discussion}
+\label{sec:discussion}
+
+\input{sections/discussion.tex}
+
+\section{Conclusion}
+\label{sec:conclusion}
+
+\input{sections/conclusion.tex}
+
+\section*{Acknowledgements}
+
+% XY gratefully acknowledge funding from XY under grant no. 00000.
+
+\section*{License}
+% \doclicenseLongText
+% \doclicenseIcon
+
+\section*{Author Contributions}
+
+% following https://casrai.org/credit/
+
+\textbf{Author XY}:
+Conceptualization --
+Data curation --
+Formal Analysis --
+Funding acquisition --
+Investigation --
+Methodology --
+Project administration --
+Resources --
+Software --
+Supervision --
+Validation --
+Visualization --
+Writing - original draft --
+Writing - review \& editing
+
+\section*{Data and Code Availability}
+
+A dataset of the model results is available at \href{zenodoTBA}{doi:zenodoTBA}.
+The code to reproduce the experiments is available at \href{https://github.com/repository}{github.com/repository}.
+
+% tidy with https://flamingtempura.github.io/bibtex-tidy/
+\addcontentsline{toc}{section}{References}
+\renewcommand{\ttdefault}{\sfdefault}
+\bibliography{references}
+
+% supplementary information
+
+\newpage
+
+\makeatletter
+\renewcommand \thesection{S\@arabic\c@section}
+\renewcommand\thetable{S\@arabic\c@table}
+\renewcommand \thefigure{S\@arabic\c@figure}
+\makeatother
+\renewcommand{\citenumfont}[1]{S#1}
+\setcounter{equation}{0}
+\setcounter{figure}{0}
+\setcounter{table}{0}
+\setcounter{section}{0}
+
+
+\section{Supplementary Information}
+\label{sec:si}
+
+\input{sections/supplementary.tex}
+
+\addcontentsline{toc}{section}{Supplementary References}
+\renewcommand{\ttdefault}{\sfdefault}
+\bibliographyS{references}
+
+\end{document}
\ No newline at end of file
diff --git a/report/sections/.gitkeep b/report/sections/.gitkeep
new file mode 100644
index 000000000..071569975
--- /dev/null
+++ b/report/sections/.gitkeep
@@ -0,0 +1,3 @@
+# SPDX-FileCopyrightText: Open Energy Transition gGmbH
+#
+# SPDX-License-Identifier: CC0-1.0
\ No newline at end of file
diff --git a/report/sections/abstract.tex b/report/sections/abstract.tex
new file mode 100644
index 000000000..bf978e970
--- /dev/null
+++ b/report/sections/abstract.tex
@@ -0,0 +1,5 @@
+% SPDX-FileCopyrightText: Open Energy Transition gGmbH
+%
+% SPDX-License-Identifier: MIT
+
+\lipsum[1]
\ No newline at end of file
diff --git a/report/sections/conclusion.tex b/report/sections/conclusion.tex
new file mode 100644
index 000000000..bf978e970
--- /dev/null
+++ b/report/sections/conclusion.tex
@@ -0,0 +1,5 @@
+% SPDX-FileCopyrightText: Open Energy Transition gGmbH
+%
+% SPDX-License-Identifier: MIT
+
+\lipsum[1]
\ No newline at end of file
diff --git a/report/sections/discussion.tex b/report/sections/discussion.tex
new file mode 100644
index 000000000..bf978e970
--- /dev/null
+++ b/report/sections/discussion.tex
@@ -0,0 +1,5 @@
+% SPDX-FileCopyrightText: Open Energy Transition gGmbH
+%
+% SPDX-License-Identifier: MIT
+
+\lipsum[1]
\ No newline at end of file
diff --git a/report/sections/introduction.tex b/report/sections/introduction.tex
new file mode 100644
index 000000000..bf978e970
--- /dev/null
+++ b/report/sections/introduction.tex
@@ -0,0 +1,5 @@
+% SPDX-FileCopyrightText: Open Energy Transition gGmbH
+%
+% SPDX-License-Identifier: MIT
+
+\lipsum[1]
\ No newline at end of file
diff --git a/report/sections/methods.tex b/report/sections/methods.tex
new file mode 100644
index 000000000..a3c1cd5e9
--- /dev/null
+++ b/report/sections/methods.tex
@@ -0,0 +1,6 @@
+% SPDX-FileCopyrightText: Open Energy Transition gGmbH
+%
+% SPDX-License-Identifier: MIT
+
+\lipsum[1]
+Blablabla said Nobody ~\cite{Nobody06}.
\ No newline at end of file
diff --git a/report/sections/results.tex b/report/sections/results.tex
new file mode 100644
index 000000000..bf978e970
--- /dev/null
+++ b/report/sections/results.tex
@@ -0,0 +1,5 @@
+% SPDX-FileCopyrightText: Open Energy Transition gGmbH
+%
+% SPDX-License-Identifier: MIT
+
+\lipsum[1]
\ No newline at end of file
diff --git a/report/sections/supplementary.tex b/report/sections/supplementary.tex
new file mode 100644
index 000000000..bf978e970
--- /dev/null
+++ b/report/sections/supplementary.tex
@@ -0,0 +1,5 @@
+% SPDX-FileCopyrightText: Open Energy Transition gGmbH
+%
+% SPDX-License-Identifier: MIT
+
+\lipsum[1]
\ No newline at end of file
diff --git a/report/static/.gitkeep b/report/static/.gitkeep
new file mode 100644
index 000000000..071569975
--- /dev/null
+++ b/report/static/.gitkeep
@@ -0,0 +1,3 @@
+# SPDX-FileCopyrightText: Open Energy Transition gGmbH
+#
+# SPDX-License-Identifier: CC0-1.0
\ No newline at end of file
diff --git a/rules/build_sector.smk b/rules/build_sector.smk
index 04f5c3e8e..7fd9c0400 100755
--- a/rules/build_sector.smk
+++ b/rules/build_sector.smk
@@ -1095,6 +1095,7 @@ rule prepare_sector_network:
countries=config_provider("countries"),
adjustments=config_provider("adjustments", "sector"),
emissions_scope=config_provider("energy", "emissions"),
+ electricity=config_provider("electricity"),
biomass=config_provider("biomass"),
RDIR=RDIR,
heat_pump_sources=config_provider("sector", "heat_pump_sources"),
diff --git a/rules/report.smk b/rules/report.smk
new file mode 100755
index 000000000..c79d4f0de
--- /dev/null
+++ b/rules/report.smk
@@ -0,0 +1,19 @@
+# SPDX-FileCopyrightText: Open Energy Transition gGmbH
+#
+# SPDX-License-Identifier: MIT
+
+rule report:
+ message: "Compile report."
+ params:
+ fn="report"
+ input:
+ tex="report/report.tex",
+ bib="report/references.bib"
+ output: "report/report.pdf"
+ shell:
+ """
+ pdflatex -output-directory report {input.tex}
+ cd report; bibtex {params.fn}; cd ..
+ pdflatex -output-directory report {input.tex}
+ pdflatex -output-directory report {input.tex}
+ """
diff --git a/scripts/add_electricity.py b/scripts/add_electricity.py
index 3dfffac23..82c752d69 100755
--- a/scripts/add_electricity.py
+++ b/scripts/add_electricity.py
@@ -463,6 +463,7 @@ def set_transmission_costs(
def attach_wind_and_solar(
n: pypsa.Network,
costs: pd.DataFrame,
+ ppl: pd.DataFrame,
input_profiles: str,
carriers: list | set,
extendable_carriers: list | set,
@@ -512,12 +513,20 @@ def attach_wind_and_solar(
else:
capital_cost = costs.at[car, "capital_cost"]
+ if not ppl.query("carrier == @car").empty:
+ caps = ppl.query("carrier == @car").groupby("bus").p_nom.sum()
+ caps = pd.Series(data = caps, index = ds.indexes["bus"]).fillna(0)
+ else:
+ caps = pd.Series(index = ds.indexes["bus"]).fillna(0)
+
n.add(
"Generator",
ds.indexes["bus"],
" " + car,
bus=ds.indexes["bus"],
carrier=car,
+ p_nom = caps,
+ p_nom_min = caps,
p_nom_extendable=car in extendable_carriers["Generator"],
p_nom_max=ds["p_nom_max"].to_pandas(),
marginal_cost=costs.at[supcar, "marginal_cost"],
@@ -1054,6 +1063,7 @@ def attach_stores(n, costs, extendable_carriers):
attach_wind_and_solar(
n,
costs,
+ ppl,
snakemake.input,
renewable_carriers,
extendable_carriers,
diff --git a/scripts/build_district_heat_share.py b/scripts/build_district_heat_share.py
index e84819189..3056b688f 100644
--- a/scripts/build_district_heat_share.py
+++ b/scripts/build_district_heat_share.py
@@ -55,9 +55,11 @@
pop_layout = pd.read_csv(snakemake.input.clustered_pop_layout, index_col=0)
year = str(snakemake.params.energy_totals_year)
- district_heat_share = pd.read_csv(snakemake.input.district_heat_share, index_col=0)[
- year
- ]
+ district_heat_share = pd.read_csv(snakemake.input.district_heat_share, index_col=0)
+ if not district_heat_share.empty:
+ district_heat_share = district_heat_share[year]
+ else:
+ district_heat_share = pd.Series(index=pop_layout.index, data=0)
# make ct-based share nodal
district_heat_share = district_heat_share.reindex(pop_layout.ct).fillna(0)
@@ -70,7 +72,8 @@
pop_layout["urban_ct_fraction"] = pop_layout.urban / pop_layout.ct.map(ct_urban.get)
# fraction of node that is urban
- urban_fraction = pop_layout.urban / pop_layout[["rural", "urban"]].sum(axis=1)
+ urban_fraction = (pop_layout.urban / pop_layout[["rural", "urban"]].sum(axis=1)).fillna(0)
+
# maximum potential of urban demand covered by district heating
central_fraction = snakemake.config["sector"]["district_heating"]["potential"]
@@ -78,7 +81,7 @@
# district heating share at each node
dist_fraction_node = (
district_heat_share * pop_layout["urban_ct_fraction"] / pop_layout["fraction"]
- )
+ ).fillna(0)
# if district heating share larger than urban fraction -> set urban
# fraction to district heating share
diff --git a/scripts/build_energy_totals.py b/scripts/build_energy_totals.py
index 6afa6b334..d6aeccaa2 100644
--- a/scripts/build_energy_totals.py
+++ b/scripts/build_energy_totals.py
@@ -1625,7 +1625,40 @@ def build_heating_efficiencies(
)
swiss = build_swiss()
- idees = build_idees(idees_countries)
+ if len(idees_countries) > 0:
+ idees = build_idees(idees_countries)
+ else:
+ # e.g. UA and MD
+ logger.info(f"No IDEES data available for {countries} and years 2000-2015. Filling with zeros.")
+ years = range(2000, 2016)
+ idees = pd.DataFrame(
+ index=pd.MultiIndex.from_tuples([(country, year) for country in countries for year in years]),
+ columns=[
+ "passenger cars",
+ "passenger car efficiency",
+ "total passenger cars",
+ "total other road passenger",
+ "total light duty road freight",
+ "total two-wheel",
+ "total heavy duty road freight",
+ "electricity passenger cars",
+ "electricity other road passenger",
+ "electricity light duty road freight",
+ "total rail passenger",
+ "total rail freight",
+ "electricity rail passenger",
+ "electricity rail freight",
+ "total domestic aviation passenger",
+ "total domestic aviation freight",
+ "total international aviation passenger",
+ "total international aviation freight",
+ "derived heat residential",
+ "derived heat services",
+ "thermal uses residential",
+ "thermal uses services",
+ ],
+ data=0
+ )
energy = build_energy_totals(countries, eurostat, swiss, idees)
diff --git a/scripts/build_industrial_energy_demand_per_country_today.py b/scripts/build_industrial_energy_demand_per_country_today.py
index f011e61f6..18817bfdd 100644
--- a/scripts/build_industrial_energy_demand_per_country_today.py
+++ b/scripts/build_industrial_energy_demand_per_country_today.py
@@ -65,6 +65,7 @@
import country_converter as coco
import pandas as pd
+import numpy as np
from _helpers import set_scenario_config
from tqdm import tqdm
@@ -207,12 +208,14 @@ def add_non_eu27_industrial_energy_demand(countries, demand, production):
return demand
eu27_production = production.loc[countries.intersection(eu27)].sum()
+ if eu27_production.sum() == 0:
+ logger.info("EU production is zero. Fallback: Filling non EU28 countries with zeros.")
eu27_energy = demand.groupby(level=1).sum()
eu27_averages = eu27_energy / eu27_production
demand_non_eu27 = pd.concat(
{k: v * eu27_averages for k, v in production.loc[non_eu27].iterrows()}
- )
+ ).fillna(0)
return pd.concat([demand, demand_non_eu27])
@@ -292,7 +295,14 @@ def add_coke_ovens(demand, fn, year, factor=0.75):
year = params.get("reference_year", 2019)
countries = pd.Index(snakemake.params.countries)
- demand = industrial_energy_demand(countries.intersection(eu27), year)
+ if len(countries.intersection(eu27)) > 0:
+ demand = industrial_energy_demand(countries.intersection(eu27), year)
+ else:
+ # e.g. only UA or MD
+ logger.info(
+ f"No industrial energy demand available for {countries}. Filling with average values of EU."
+ )
+ demand = industrial_energy_demand(eu27, year)
# output in MtMaterial/a
production = (
diff --git a/scripts/build_industrial_energy_demand_per_node_today.py b/scripts/build_industrial_energy_demand_per_node_today.py
index 0675ce079..2bd51c306 100644
--- a/scripts/build_industrial_energy_demand_per_node_today.py
+++ b/scripts/build_industrial_energy_demand_per_node_today.py
@@ -72,7 +72,15 @@ def build_nodal_industrial_energy_demand():
buses = keys.index[keys.country == country]
mapping = sector_mapping.get(sector, "population")
- key = keys.loc[buses, mapping]
+ try:
+ key = keys.loc[buses, mapping].fillna(0)
+ except:
+ logger.info(
+ f"No industrial demand available for {mapping}. Filling with zeros."
+ )
+ keys[mapping] = 0
+ key = keys.loc[buses, mapping].fillna(0)
+
demand = industrial_demand[country, sector]
outer = pd.DataFrame(
diff --git a/scripts/build_industrial_production_per_node.py b/scripts/build_industrial_production_per_node.py
index cdc6e1a20..a1263b378 100644
--- a/scripts/build_industrial_production_per_node.py
+++ b/scripts/build_industrial_production_per_node.py
@@ -75,6 +75,15 @@ def build_nodal_industrial_production():
buses = keys.index[keys.country == country]
mapping = sector_mapping.get(sector, "population")
+ try:
+ key = keys.loc[buses, mapping].fillna(0)
+ except:
+ logger.info(
+ f"No industrial production available for {mapping}. Filling with zeros."
+ )
+ keys[mapping] = 0
+ key = keys.loc[buses, mapping].fillna(0)
+
key = keys.loc[buses, mapping]
nodal_production.loc[buses, sector] = (
industrial_production.at[country, sector] * key
diff --git a/scripts/prepare_sector_network.py b/scripts/prepare_sector_network.py
index 7332c5947..395954f74 100755
--- a/scripts/prepare_sector_network.py
+++ b/scripts/prepare_sector_network.py
@@ -1099,12 +1099,13 @@ def annuity_factor(v):
return costs
-def add_generation(n, costs):
+def add_generation(n, costs, existing_capacities=0, existing_efficiencies=None):
logger.info("Adding electricity generation")
nodes = pop_layout.index
- conventionals = options["conventional_generation"]
+ fallback = {"OCGT": "gas"}
+ conventionals = options.get("conventional_generation", fallback)
for generator, carrier in conventionals.items():
carrier_nodes = vars(spatial)[carrier].nodes
@@ -1121,9 +1122,28 @@ def add_generation(n, costs):
* costs.at[generator, "VOM"], # NB: VOM is per MWel
capital_cost=costs.at[generator, "efficiency"]
* costs.at[generator, "fixed"], # NB: fixed cost is per MWel
- p_nom_extendable=True,
+ p_nom_extendable=(
+ True
+ if generator
+ in snakemake.params.electricity.get("extendable_carriers", dict()).get(
+ "Generator", list()
+ )
+ else False
+ ),
+ p_nom=(
+ existing_capacities[generator] / existing_efficiencies[generator]
+ if not existing_capacities == 0 else 0
+ ), # NB: existing capacities are MWel
+ p_max_pu = 0.7 if carrier == "uranium" else 1, # be conservative for nuclear (maintance or unplanned shut downs)
+ p_nom_min=(
+ existing_capacities[generator] if not existing_capacities == 0 else 0
+ ),
carrier=generator,
- efficiency=costs.at[generator, "efficiency"],
+ efficiency=(
+ existing_efficiencies[generator]
+ if existing_efficiencies is not None
+ else costs.at[generator, "efficiency"]
+ ),
efficiency2=costs.at[carrier, "CO2 intensity"],
lifetime=costs.at[generator, "lifetime"],
)
@@ -4595,6 +4615,28 @@ def add_enhanced_geothermal(n, egs_potentials, egs_overlap, costs):
cyclic_state_of_charge=True,
)
+def get_capacities_from_elec(n, carriers, component):
+ """
+ Gets capacities and efficiencies for {carrier} in n.{component} that were
+ previously assigned in add_electricity.
+ """
+ component_list = ["generators", "storage_units", "links", "stores"]
+ component_dict = {name: getattr(n, name) for name in component_list}
+ e_nom_carriers = ["stores"]
+ nom_col = {x: "e_nom" if x in e_nom_carriers else "p_nom" for x in component_list}
+ eff_col = "efficiency"
+
+ capacity_dict = {}
+ efficiency_dict = {}
+ for carrier in carriers:
+ capacity_dict[carrier] = component_dict[component].query("carrier in @carrier")[
+ nom_col[component]
+ ]
+ efficiency_dict[carrier] = component_dict[component].query(
+ "carrier in @carrier"
+ )[eff_col]
+
+ return capacity_dict, efficiency_dict
# %%
if __name__ == "__main__":
@@ -4639,6 +4681,15 @@ def add_enhanced_geothermal(n, egs_potentials, egs_overlap, costs):
)
pop_weighted_energy_totals.update(pop_weighted_heat_totals)
+ if options.get("keep_existing_capacities", False):
+ existing_capacities, existing_efficiencies = get_capacities_from_elec(
+ n,
+ carriers=options.get("conventional_generation").keys(),
+ component="generators",
+ )
+ else:
+ existing_capacities, existing_efficiencies = 0, None
+
landfall_lengths = {
tech: settings["landfall_length"]
for tech, settings in snakemake.params.renewable.items()
@@ -4652,7 +4703,7 @@ def add_enhanced_geothermal(n, egs_potentials, egs_overlap, costs):
spatial = define_spatial(pop_layout.index, options)
- if snakemake.params.foresight in ["myopic", "perfect"]:
+ if snakemake.params.foresight in ["overnight", "myopic", "perfect"]:
add_lifetime_wind_solar(n, costs)
conventional = snakemake.params.conventional_carriers
@@ -4663,7 +4714,7 @@ def add_enhanced_geothermal(n, egs_potentials, egs_overlap, costs):
add_co2_tracking(n, costs, options)
- add_generation(n, costs)
+ add_generation(n, costs, existing_capacities, existing_efficiencies)
add_storage_and_grids(n, costs)