Skip to content

Python API

There are two ways to use fair-shares from your own code, and they have very different requirements.

The allocation functions, on data you supply

The allocation functions take population (and, for capability-adjusted approaches, GDP) as pandas dataframes and hand back a result object. They read no files, need no data directory, and take no configuration — a plain pip install fair-shares is enough, and the code below runs from any directory:

Python
import pandas as pd
from fair_shares.library.allocations.budgets.per_capita import equal_per_capita_budget

years = [str(y) for y in range(2020, 2051)]
population = pd.DataFrame(
    [[50.0] * len(years), [10.0] * len(years)],
    index=pd.MultiIndex.from_tuples(
        [("AAA", "million"), ("BBB", "million")], names=["iso3c", "unit"]
    ),
    columns=years,
)

result = equal_per_capita_budget(
    population_ts=population,
    allocation_year=2020,
    emission_category="co2-ffi",
)

print(result.relative_shares_cumulative_emission["2020"])

Country AAA gets 50/60 of the budget, BBB gets 10/60. Multiply those shares by your global budget to get absolute numbers. The dataframe needs one row per country, a two-level index of country code and unit, and one column per year labelled as a string.

The full set is in Budget Functions and Pathway Functions.

The orchestrated timeseries API

The fair_shares.library.python_api module runs a whole allocation — budget, remaining budget, and annual pathway — in memory and returns it as a dataclass, so downstream consumers never have to execute a notebook or manage its output directories. Unlike the functions above, it loads processed data from disk.

Overview

Notebook 601_reproduce_esabcc_2023 computes, for one remaining-carbon-budget anchor (a rcb_source / climate_assessment / quantile triple) and one emission category:

  • budget allocations of the remaining carbon budget (§ 4),
  • each country's remaining budget after netting observed emissions (§ 5), and
  • an annual pathway to pathway_end_year that spreads that remaining budget under a normative distribution grid, plus the non-CO2 and combined parts (§ 6).

calculate_allocation_timeseries performs exactly that computation and returns a ResultContainer. The heavy lifting stays in the existing library functions; this module ports the notebook's § 5 / § 6 orchestration and ties it together per (anchor, category).

The notebook and this module are two implementations of the same calculation. tests/integration/test_python_api_reproduces_notebook.py is the guard against them drifting apart: it re-runs calculate_allocation_timeseries against fixtures saved from the notebook. If you change either side, regenerate the fixtures with uv run python tests/fixtures/save_python_api_fixture.py and confirm that test still passes.

Scope

This module computes remaining-carbon-budget allocations only (TARGET = "rcbs"), for a single fully-specified allocation under a single RCB anchor. Loop over allocations, distribution settings and debt modes around it to build a fuller set. To run parameter grids or pathway targets, use the notebook workflows described in the User Guide.

Data location

calculate_allocation_timeseries reads processed data from disk. Inside a checkout it finds data/ and output/ on its own. From an installed wheel, set FAIR_SHARES_DATA_DIR and FAIR_SHARES_OUTPUT_DIR, or pass data_dir= and output_dir=. If the processed files are missing it rebuilds them with Snakemake, which only ships in the pipeline extra — so a plain pip install fair-shares works against a data tree that has already been built, but not one that has to be built first. (The older project_root argument still works and maps onto the two directories, but is deprecated.)

Computing Allocation Timeseries

calculate_allocation_timeseries

fair_shares.library.python_api.calculate_allocation_timeseries

Python
calculate_allocation_timeseries(
    *,
    emission_category: str,
    climate_assessment: str,
    quantile: float,
    rcb_source: str,
    allocation: dict[str, dict[str, Any]],
    shape: str,
    deviation_end_year: int,
    convergence_year: int,
    nonco2_debt_mode: str | None,
    pathway_end_year: int,
    base_share_floor_mt: float,
    desired_harmonisation_year: int,
    emissions_source: str,
    gdp_source: str,
    population_source: str,
    gini_source: str,
    lulucf_source: str,
    allocation_folder: str,
    project_root: Path | None = None,
    data_dir: Path | str | None = None,
    output_dir: Path | str | None = None
) -> ResultContainer

Compute the distributed allocation timeseries for one (anchor, category).

Runs §4 (budget allocation, in memory), §5 (remaining budget) and §6 (time distribution) for a single fully-specified allocation, under the single RCB anchor (rcb_source, climate_assessment, quantile). Loop over allocations / distributions / debt modes around this function to build a fuller set. No files are written and no notebook is executed; every input is explicit (wrap this with your own defaults if desired).

Parameters:

Name Type Description Default
allocation dict[str, dict[str, Any]]

One approach mapped to its scalar parameters, e.g. {"equal-per-capita-budget": {"allocation_year": 2015, "preserve_allocation_year_shares": True}}.

required
shape str

The single §6 distribution setting (debt-redress envelope shape, the year it closes, and the per-capita convergence year).

required
deviation_end_year str

The single §6 distribution setting (debt-redress envelope shape, the year it closes, and the per-capita convergence year).

required
convergence_year str

The single §6 distribution setting (debt-redress envelope shape, the year it closes, and the per-capita convergence year).

required
nonco2_debt_mode str | None

The single non-CO2 debt-settlement mode ("free-rider" / "co2-debit") for composite categories; must be None for co2-ffi (which has no non-CO2 part).

required
project_root Path | None

Deprecated. Superseded by data_dir and output_dir, onto which it maps as project_root/"data" and project_root/"output".

None
data_dir Path | str | None

Input and product directories. Both default to the resolved directories — see :mod:fair_shares.library.paths.

None
output_dir Path | str | None

Input and product directories. Both default to the resolved directories — see :mod:fair_shares.library.paths.

None

Allocation Steps

These are the individual steps calculate_allocation_timeseries composes. Call them directly when you already hold an allocations frame and want only part of the calculation.

compute_remaining_budgets

fair_shares.library.python_api.compute_remaining_budgets

Python
compute_remaining_budgets(
    category: str,
    allocations_absolute: DataFrame,
    processed_dir: Path,
) -> DataFrame

Remaining budget per country after netting observed emissions (§5).

Budget parts: remaining = allocated total minus actual consumption from the allocation year through the last observed year. Non-CO2 pathway parts: remaining = the forward-window allocation, with the historical deviation (actual minus allocated over the past window) reported separately. For composite categories a combined row per configuration is added (CO2 budget part + non-CO2 pathway part), matched on the shared equity params.

distribute_remaining_pathways

fair_shares.library.python_api.distribute_remaining_pathways

Python
distribute_remaining_pathways(
    category: str,
    allocations_absolute: DataFrame,
    remaining: DataFrame,
    processed_dir: Path,
    *,
    distribution_grid: dict[str, Sequence[Any]],
    pathway_end_year: int,
    base_share_floor_mt: float,
    nonco2_debt_modes: Sequence[str]
) -> DataFrame

Distribute every budget configuration's remaining budget to 2100 (§6).

Spreads each remaining budget to pathway_end_year across the distribution grid. For composite categories each CO2 configuration is distributed under every nonco2_debt_modes entry (free-rider leaves the CO2 budget as is; co2-debit subtracts the country's past non-CO2 over-use and retires it), and the allocated non-CO2 and combined rows are appended.

build_history

fair_shares.library.python_api.build_history

Python
build_history(
    category: str, processed_dir: Path
) -> DataFrame

Observed emissions for every coverage this category needs (§5 inputs).

One frame with a (coverage, unit, iso3c) MultiIndex and year columns. The combined basket is the sum of its parts (NaN where either part lacks a year, e.g. the all-ghg basket before the NGHGI CO2 record starts).

Results

ResultContainer

fair_shares.library.python_api.ResultContainer dataclass

Python
ResultContainer(
    allocation_timeseries: DataFrame,
    history: DataFrame,
    emission_category: str,
    climate_assessment: str,
    quantile: float,
    rcb_source: str,
    source_id: str,
    emissions_source: str,
    gdp_source: str,
    population_source: str,
    gini_source: str,
    lulucf_source: str,
    unit: str,
    harmonisation_year: int | None,
    netting_end_year: int,
    pathway_end_year: int,
    base_share_floor_mt: float,
    shape: str,
    deviation_end_year: int,
    convergence_year: int,
    nonco2_debt_mode: str | None,
    allocation_folder: str,
    metadata: dict[str, Any] = dict(),
)

The allocation timeseries for one (anchor, category), with its inputs.

Everything common to every row is an explicit attribute; metadata is a fallback only for approach-specific extras that do not belong on the frame.

allocation_timeseries instance-attribute
Python
allocation_timeseries: DataFrame

Distributed annual pathways. MultiIndex holds every identifying column (category, emission-category part, unit, source, climate-assessment, quantile, approach, config params, the §6 knobs shape / deviation-end-year / nonco2-debt-mode, iso3c); columns are the years.

history instance-attribute
Python
history: DataFrame

Observed emissions used, one row per (coverage, unit, iso3c); columns are the years. Includes the summed combined basket(s) for this category.

emission_category instance-attribute
Python
emission_category: str
climate_assessment instance-attribute
Python
climate_assessment: str
quantile instance-attribute
Python
quantile: float
rcb_source instance-attribute
Python
rcb_source: str
source_id instance-attribute
Python
source_id: str
emissions_source instance-attribute
Python
emissions_source: str
gdp_source instance-attribute
Python
gdp_source: str
population_source instance-attribute
Python
population_source: str
gini_source instance-attribute
Python
gini_source: str
lulucf_source instance-attribute
Python
lulucf_source: str
unit instance-attribute
Python
unit: str
harmonisation_year instance-attribute
Python
harmonisation_year: int | None
netting_end_year instance-attribute
Python
netting_end_year: int
pathway_end_year instance-attribute
Python
pathway_end_year: int
base_share_floor_mt instance-attribute
Python
base_share_floor_mt: float
shape instance-attribute
Python
shape: str
deviation_end_year instance-attribute
Python
deviation_end_year: int
convergence_year instance-attribute
Python
convergence_year: int
nonco2_debt_mode instance-attribute
Python
nonco2_debt_mode: str | None
allocation_folder instance-attribute
Python
allocation_folder: str
metadata class-attribute instance-attribute
Python
metadata: dict[str, Any] = field(default_factory=dict)

save_results

fair_shares.library.python_api.save_results

Python
save_results(
    results: ResultContainer, outpath: Path
) -> Path

Persist a :class:ResultContainer under outpath (created if needed).

Writes allocation_timeseries.parquet, history.parquet, a metadata.json of every scalar attribute, and CITATIONS.md listing the software and the data sources this run used. Returns outpath.

Citing a run

Most inputs are third-party datasets that require attribution, and which ones a run uses depends on its settings: a budget run subtracts international bunker emissions, a composite category such as all-ghg also draws on the AR6 scenario ensemble, and the Gini source is whichever you chose.

save_results writes a CITATIONS.md into the output directory alongside the parquet files, so a saved run always records what to cite. Runs kept in memory produce no file, since there is no directory to put one in.

To get the same list directly:

Python
from fair_shares.library.citations import citations

run = citations(active_sources, emission_category="all-ghg")
print(run.text())     # software, then each data source with DOI and licence
print(run.bibtex())   # the same as BibTeX entries

Or from the command line, without writing any code:

Bash
uv run fair-shares cite
uv run fair-shares cite --sources gini=wdi-2025,target=pathway --bibtex

Sources with no DOI issued (World Bank, OWID, UN WPP) are credited by name, and the output says so rather than leaving a blank field. Sources whose terms ask for more than attribution — the Global Carbon Project's co-authorship request, CMIP7's component datasets, WIID's non-commercial clause — are called out in a separate section.

fair_shares.library.citations.citations

Python
citations(
    active_sources: dict[str, str],
    *,
    emission_category: str | None = None,
    registry: Registry | None = None
) -> RunCitations

Return everything a run should cite: the software and its data sources.

See Also