Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion AUTHORS.rst
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ The following persons contributed to the development of the |pyam| package:
- Pietro Monticone `@pitmonticone <https://github.com/pitmonticone>`_
- Edward Byers `@byersiiasa <https://github.com/byersiiasa>`_
- Fridolin Glatter `@glatterf42 <https://github.com/glatterf42>`_
- Linh Ho `@linhho <https://github.com/LinhHo>`
- Linh Ho `@linhho <https://github.com/LinhHo>`_
- Zachary Schmidt `@zacharyschmidt <https://github.com/zacharyschmidt>

| The core maintenance of the |pyam| package is done by
the *Scenario Services & Scientific Software* research theme
Expand Down
1 change: 1 addition & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

## Individual updates

- [#875](https://github.com/IAMconsortium/pyam/pull/875) Add methods to the `compute` module implementing Kaya decomposition analysis.
- [#901](https://github.com/IAMconsortium/pyam/pull/901) Add support for Python 3.13
- [#899](https://github.com/IAMconsortium/pyam/pull/899) Add `to_netcdf()` method
- [#896](https://github.com/IAMconsortium/pyam/pull/896) Add `sort_data()` method
Expand Down
123 changes: 123 additions & 0 deletions pyam/compute.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@
import pandas as pd
import wquantiles

import pyam
from pyam._debiasing import _compute_bias
from pyam.index import replace_index_values
from pyam.kaya import kaya_factors, kaya_variables
from pyam.timeseries import growth_rate
from pyam.utils import remove_from_list

Expand Down Expand Up @@ -249,6 +251,127 @@ def bias(self, name, method, axis):
"""
_compute_bias(self._df, name, method, axis)

def kaya_variables(self, append=False):
"""Create the set of variables needed to compute Kaya factors.

Parameters
----------
append : bool, optional
Whether to append computed timeseries data to this instance.

Returns
-------
:class:`IamDataFrame` or **None**
Computed timeseries data or None if `append=True`.

Notes
-----

Example of calling the method:

.. code-block:: python

df.compute.kaya_variables(append=True)

The IamDataFrame must contain the following variables, otherwise the method
will return None:
.. list-table::
- Required Variables
- Population
- GDP (MER or PPP)
- Final Energy
- Primary Energy
- Primary Energy|Coal
- Primary Energy|Oil
- Primary Energy|Gas
- Emissions|CO2|Industrial Processes
- Emissions|CO2|Carbon Capture and Storage
- Emissions|CO2|Carbon Capture and Storage|Biomass
- Emissions|CO2|Fossil Fuels and Industry
- Emissions|CO2|AFOLU
- Carbon Sequestration|CCS|Fossil|Energy
- Carbon Sequestration|CCS|Fossil|Industrial Processes
- Carbon Sequestration|CCS|Biomass|Energy
- Carbon Sequestration|CCS|Biomass|Industrial Processes

"""

kaya_variables_frame = kaya_variables.compute_kaya_variables(self._df)
if kaya_variables_frame is None:
return None
if append:
self._df.append(
_find_non_duplicate_rows(self._df, kaya_variables_frame), inplace=True
)
return None

return kaya_variables_frame

def kaya_factors(self, append=False):
"""Compute the factors for the Kaya Decomposition Analysis

Parameters
----------
append : bool, optional
Whether to append computed timeseries data to this instance.

Returns
-------
:class:`IamDataFrame` or **None**
Computed timeseries data or None if `append=True`.

Notes
-----

Example of calling the method:

.. code-block:: python

df.compute.kaya_factors(append=True)

The IamDataFrame must contain the following variables, otherwise the method
will return None:
.. list-table::
- Required Variables
- Population
- GDP (MER or PPP)
- Final Energy
- Primary Energy
- Primary Energy|Coal
- Primary Energy|Oil
- Primary Energy|Gas
- Emissions|CO2|Industrial Processes
- Emissions|CO2|Carbon Capture and Storage
- Emissions|CO2|Carbon Capture and Storage|Biomass
- Emissions|CO2|Fossil Fuels and Industry
- Emissions|CO2|AFOLU
- Carbon Sequestration|CCS|Fossil|Energy
- Carbon Sequestration|CCS|Fossil|Industrial Processes
- Carbon Sequestration|CCS|Biomass|Energy
- Carbon Sequestration|CCS|Biomass|Industrial Processes
"""
kaya_variables = self.kaya_variables(append=False)
if kaya_variables is None:
return None
kaya_factors_frame = kaya_factors.compute_kaya_factors(kaya_variables)
if kaya_factors_frame is None:
return None
if append:
self._df.append(
_find_non_duplicate_rows(self._df, kaya_factors_frame), inplace=True
)
return kaya_factors_frame


def _find_non_duplicate_rows(original_df, variables_to_add):
variables_for_append = pyam.IamDataFrame(
variables_to_add.as_pandas(meta_cols=False)
.merge(original_df.as_pandas(meta_cols=False), how="left", indicator=True)
.query('_merge=="left_only"')
.drop(columns="_merge")
)
return variables_for_append


def _compute_learning_rate(x, performance, experience):
"""Internal implementation for computing implicit learning rate from timeseries data
Expand Down
17 changes: 17 additions & 0 deletions pyam/kaya/input_variable_names.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
POPULATION = "Population"
GDP_MER = "GDP|MER"
GDP_PPP = "GDP|PPP"
FINAL_ENERGY = "Final Energy"
PRIMARY_ENERGY = "Primary Energy"
PRIMARY_ENERGY_COAL = "Primary Energy|Coal"
PRIMARY_ENERGY_OIL = "Primary Energy|Oil"
PRIMARY_ENERGY_GAS = "Primary Energy|Gas"
EMISSIONS_CO2_INDUSTRIAL_PROCESSES = "Emissions|CO2|Industrial Processes"
EMISSIONS_CO2_CCS = "Emissions|CO2|Carbon Capture and Storage"
EMISSIONS_CO2_CCS_BIOMASS = "Emissions|CO2|Carbon Capture and Storage|Biomass"
EMISSIONS_CO2_FOSSIL_FUELS_AND_INDUSTRY = "Emissions|CO2|Fossil Fuels and Industry"
EMISSIONS_CO2_AFOLU = "Emissions|CO2|AFOLU"
CCS_FOSSIL_ENERGY = "Carbon Sequestration|CCS|Fossil|Energy"
CCS_FOSSIL_INDUSTRY = "Carbon Sequestration|CCS|Fossil|Industrial Processes"
CCS_BIOMASS_ENERGY = "Carbon Sequestration|CCS|Biomass|Energy"
CCS_BIOMASS_INDUSTRY = "Carbon Sequestration|CCS|Biomass|Industrial Processes"
6 changes: 6 additions & 0 deletions pyam/kaya/kaya_factor_names.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
GNP_per_P = "GNP/P"
FE_per_GNP = "FE/GNP"
PEdeq_per_FE = "PEDEq/FE"
PEFF_per_PEDEq = "PEFF/PEDEq"
TFC_per_PEFF = "TFC/PEFF"
NFC_per_TFC = "NFC/TFC"
81 changes: 81 additions & 0 deletions pyam/kaya/kaya_factors.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import pyam
from pyam.kaya import input_variable_names, kaya_factor_names, kaya_variable_names


def compute_kaya_factors(kaya_variables_frame):
kaya_factors = pyam.concat(
[
_calc_gnp_per_p(kaya_variables_frame),
_calc_fe_per_gnp(kaya_variables_frame),
_calc_pedeq_per_fe(kaya_variables_frame),
_calc_peff_per_pedeq(kaya_variables_frame),
_calc_tfc_per_peff(kaya_variables_frame),
_calc_nfc_per_tfc(kaya_variables_frame),
kaya_variables_frame.filter(
variable=[kaya_variable_names.TFC, input_variable_names.POPULATION]
),
]
)
return kaya_factors


def _calc_gnp_per_p(input_data):
variable = input_variable_names.GDP_PPP
if input_data.filter(variable=variable).empty:
variable = input_variable_names.GDP_MER
return input_data.divide(
variable,
input_variable_names.POPULATION,
kaya_factor_names.GNP_per_P,
append=False,
)


def _calc_fe_per_gnp(input_data):
variable = input_variable_names.GDP_PPP
if input_data.filter(variable=variable).empty:
variable = input_variable_names.GDP_MER
return input_data.divide(
input_variable_names.FINAL_ENERGY,
variable,
kaya_factor_names.FE_per_GNP,
append=False,
)


def _calc_pedeq_per_fe(input_data):
return input_data.divide(
input_variable_names.PRIMARY_ENERGY,
input_variable_names.FINAL_ENERGY,
kaya_factor_names.PEdeq_per_FE,
append=False,
)


def _calc_peff_per_pedeq(input_data):
return input_data.divide(
kaya_variable_names.PRIMARY_ENERGY_FF,
input_variable_names.PRIMARY_ENERGY,
kaya_factor_names.PEFF_per_PEDEq,
append=False,
)


def _calc_tfc_per_peff(input_data):
return input_data.divide(
kaya_variable_names.TFC,
kaya_variable_names.PRIMARY_ENERGY_FF,
kaya_factor_names.TFC_per_PEFF,
ignore_units="Mt CO2/EJ",
append=False,
)


def _calc_nfc_per_tfc(input_data):
return input_data.divide(
kaya_variable_names.NFC,
kaya_variable_names.TFC,
kaya_factor_names.NFC_per_TFC,
ignore_units="",
append=False,
) # .rename(unit={"unknown": ""})
3 changes: 3 additions & 0 deletions pyam/kaya/kaya_variable_names.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
PRIMARY_ENERGY_FF = "Primary Energy|Fossil"
TFC = "Total Fossil Carbon"
NFC = "Net Fossil Carbon"
Loading