Budget Allocation Functions¶
Budget allocation functions distribute a fixed cumulative emission budget among countries in a given allocation year.
Overview¶
All budget allocation approaches return a BudgetAllocationResult containing:
approach: Name of the allocation approach usedparameters: Dictionary of parameters used in the calculationrelative_shares_cumulative_emission: DataFrame of budget shares (sum to 1.0)
Per Capita Budgets¶
equal_per_capita_budget¶
fair_shares.library.allocations.budgets.per_capita.equal_per_capita_budget ¶
equal_per_capita_budget(
population_ts: TimeseriesDataFrame,
allocation_year: int,
emission_category: str,
preserve_allocation_year_shares: bool = False,
group_level: str = "iso3c",
unit_level: str = "unit",
ur: PlainRegistry = get_default_unit_registry(),
) -> BudgetAllocationResult
Equal per capita budget allocation for cumulative emissions.
This function generates cumulative shares for the allocation year based on equal per capita principles.
Mathematical Foundation
Two allocation modes are supported:
Mode 1: Dynamic shares (preserve_allocation_year_shares=False, default)
Population shares are calculated using cumulative population from allocation_year onwards. This accounts for changes in relative population shares over time:
Where:
- \(A(g)\): Budget share allocated to country \(g\)
- \(P(g, t)\): Population of country \(g\) in year \(t\)
- \(t_a\): Allocation year
- \(\sum_{t \geq t_a} P(g, t)\): Cumulative population of country \(g\) from allocation year onwards
- \(\sum_{g} \sum_{t \geq t_a} P(g, t)\): Total cumulative population across all countries from allocation year onwards
Mode 2: Preserved shares (preserve_allocation_year_shares=True)
Population shares calculated at the allocation year are preserved. This means the relative allocation between groups remains constant:
Where:
- \(A(g)\): Budget share allocated to country \(g\)
- \(P(g, t_a)\): Population of country \(g\) at allocation year \(t_a\)
- \(\sum_{g} P(g, t_a)\): Total world population at allocation year
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
population_ts
|
TimeseriesDataFrame
|
Timeseries of population for each group of interest |
required |
allocation_year
|
int
|
Year from which to start calculating allocations and budgets. See docs/science/parameter-effects.md#allocation_year for how this affects country shares |
required |
emission_category
|
str
|
Emission category to include in the output |
required |
preserve_allocation_year_shares
|
bool
|
If False (default), shares are calculated using cumulative population from allocation_year onwards. If True, shares calculated at allocation_year are preserved |
False
|
group_level
|
str
|
Level in the index which specifies group information |
'iso3c'
|
unit_level
|
str
|
Level in the index which specifies the unit of each timeseries |
'unit'
|
ur
|
PlainRegistry
|
The unit registry to use for calculations |
get_default_unit_registry()
|
Returns:
| Type | Description |
|---|---|
BudgetAllocationResult
|
Container with relative shares for cumulative emissions budget allocation. The TimeseriesDataFrame contains only the allocation_year column with population shares that sum to 1 across groups for the specified emission category. |
Notes
Theoretical grounding:
See docs/science/allocations.md#equal-per-capita for detailed mathematical formulation, limitations, and when to use this approach.
For translating the egalitarian tradition (which grounds the equal per capita principle) into code, see docs/science/principle-to-code.md#egalitarianism for implementation guidance.
Examples:
Calculate equal per capita budget allocation for 2020:
>>> from fair_shares.library.utils import create_example_data
>>> data = create_example_data()
>>> result = equal_per_capita_budget(
... population_ts=data["population"],
... allocation_year=2020,
... emission_category="co2-ffi",
... )
Converting units...
>>> result.approach
'equal-per-capita-budget'
>>> # Shares are proportional to population
>>> shares = result.relative_shares_cumulative_emission
>>> bool(abs(shares["2020"].sum() - 1.0) < 1e-10) # Should sum to 1.0
True
Using preserve_allocation_year_shares to fix shares at allocation year:
>>> result_preserved = equal_per_capita_budget(
... population_ts=data["population"],
... allocation_year=2020,
... emission_category="co2-ffi",
... preserve_allocation_year_shares=True,
... )
Converting units...
>>> # Shares are based only on 2020 population, not cumulative from 2020 onwards
>>> result_preserved.parameters["preserve_allocation_year_shares"]
True
per_capita_adjusted_budget¶
fair_shares.library.allocations.budgets.per_capita.per_capita_adjusted_budget ¶
per_capita_adjusted_budget(
population_ts: TimeseriesDataFrame,
allocation_year: int,
emission_category: str,
country_actual_emissions_ts: (
TimeseriesDataFrame | None
) = None,
gdp_ts: TimeseriesDataFrame | None = None,
pre_allocation_responsibility_weight: float = 0.0,
capability_weight: float = 0.0,
pre_allocation_responsibility_year: int = 1990,
pre_allocation_responsibility_per_capita: bool = True,
pre_allocation_responsibility_exponent: float = 1.0,
pre_allocation_responsibility_functional_form: str = "asinh",
capability_per_capita: bool = True,
capability_exponent: float = 1.0,
capability_functional_form: str = "asinh",
max_deviation_sigma: float | None = 2.0,
preserve_allocation_year_shares: bool = False,
historical_discount_rate: float = 0.0,
group_level: str = "iso3c",
unit_level: str = "unit",
ur: PlainRegistry = get_default_unit_registry(),
) -> BudgetAllocationResult
Per capita budget allocation with pre-allocation responsibility and capability adjustments.
This function generates cumulative shares for the allocation year based on adjusted per capita principles, incorporating pre-allocation responsibility and economic capability adjustments (but without Gini correction).
Mathematical Foundation
The per capita adjusted budget allocation adjusts for pre-allocation responsibility and economic capability using the following approach.
Core Allocation Formula
For budget allocation with dynamic shares (default mode):
Where:
- \(A(g)\): Budget share allocated to country \(g\)
- \(R(g)\): Responsibility adjustment factor for country \(g\) (constant over time, equals 1.0 if not used)
- \(C(g, t)\): Capability adjustment factor for country \(g\) in year \(t\) (equals 1.0 if not used)
- \(P(g, t)\): Population of country \(g\) in year \(t\)
- \(t_a\): Allocation year
Responsibility Adjustment
Historical emissions reduce future allocation rights.
For per capita responsibility (:code:pre_allocation_responsibility_per_capita=True, default):
Where:
- \(R(g)\): Responsibility adjustment factor (inverse - higher emissions = lower allocation)
- \(E(g, t)\): Emissions of country \(g\) in year \(t\)
- \(t_h\): Historical responsibility start year
- \(t_a\): Allocation year
- \(w_r\): Normalized responsibility weight
- \(e_r\): Responsibility exponent
For absolute responsibility (:code:pre_allocation_responsibility_per_capita=False):
Capability Adjustment
Economic capacity reduces allocation rights for wealthier countries.
For per capita capability (:code:capability_per_capita=True, default):
Where:
- \(C(g, t)\): Capability adjustment factor (inverse - higher GDP per capita = lower allocation)
- \(\text{GDP}(g, t)\): Gross domestic product of country \(g\) in year \(t\)
- \(w_c\): Normalized capability weight
- \(e_c\): Capability exponent
For absolute capability (:code:capability_per_capita=False):
Two allocation modes are supported based on
:code:preserve_allocation_year_shares:
- False (default): Uses cumulative adjusted population from allocation_year onwards
- True: Uses adjusted population at allocation_year only
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
population_ts
|
TimeseriesDataFrame
|
Timeseries of population for each group of interest |
required |
allocation_year
|
int
|
Year from which to start calculating allocations and budgets. See docs/science/parameter-effects.md#allocation_year for how this affects country shares |
required |
emission_category
|
str
|
Emission category to include in the output |
required |
country_actual_emissions_ts
|
TimeseriesDataFrame | None
|
Historical emissions data (required if pre_allocation_responsibility_weight > 0) |
None
|
gdp_ts
|
TimeseriesDataFrame | None
|
GDP data (required if capability_weight > 0) |
None
|
pre_allocation_responsibility_weight
|
float
|
Weight for historical responsibility adjustment (0.0 to 1.0). See docs/science/parameter-effects.md#pre_allocation_responsibility_weight for real allocation examples showing how this affects country shares |
0.0
|
capability_weight
|
float
|
Weight for economic capability adjustment (0.0 to 1.0). See docs/science/parameter-effects.md#capability_weight for real allocation examples showing how this affects country shares |
0.0
|
pre_allocation_responsibility_year
|
int
|
Start year for calculating historical responsibility (default: 1990) |
1990
|
pre_allocation_responsibility_per_capita
|
bool
|
If True, use per capita emissions; if False, use absolute emissions |
True
|
pre_allocation_responsibility_exponent
|
float
|
Exponent for the responsibility adjustment function |
1.0
|
pre_allocation_responsibility_functional_form
|
str
|
Functional form for responsibility: "asinh" or "power" |
'asinh'
|
capability_exponent
|
float
|
Exponent for the capability adjustment function |
1.0
|
capability_functional_form
|
str
|
Functional form for capability: "asinh" or "power" |
'asinh'
|
max_deviation_sigma
|
float | None
|
Maximum allowed deviation from equal per capita in standard deviations. Constrains allocations to remain within a statistically reasonable range of the equal per capita baseline, preventing extreme adjustments. If None, no constraint is applied |
2.0
|
preserve_allocation_year_shares
|
bool
|
If False (default), shares are calculated using cumulative adjusted population from allocation_year onwards. If True, shares calculated at allocation_year are preserved |
False
|
historical_discount_rate
|
float
|
Discount rate for historical emissions (0.0 to <1.0). When > 0, earlier emissions are weighted less via (1 - rate)^(reference_year - t). Implements natural CO2 removal rationale (Dekker Eq. 5). Default: 0.0 (no discounting). |
0.0
|
group_level
|
str
|
Level in the index which specifies group information |
'iso3c'
|
unit_level
|
str
|
Level in the index which specifies the unit of each timeseries |
'unit'
|
ur
|
PlainRegistry
|
The unit registry to use for calculations |
get_default_unit_registry()
|
Returns:
| Type | Description |
|---|---|
BudgetAllocationResult
|
Container with relative shares for cumulative emissions budget allocation. The TimeseriesDataFrame contains only the allocation_year column with adjusted population shares that sum to 1 across groups for the specified emission category. |
Notes
Theoretical grounding:
See docs/science/allocations.md#per-capita-adjusted for detailed mathematical formulation, parameter considerations, and CBDR-RC alignment.
For translating equity principles into code, see:
- docs/science/principle-to-code.md#historical-responsibility-polluter-pays for responsibility weight implementation
- docs/science/principle-to-code.md#capability-ability-to-pay for capability weight implementation
- docs/science/principle-to-code.md#cbdr-rc for combining both principles
Examples:
Calculate allocation with both responsibility and capability adjustments:
>>> from fair_shares.library.utils import create_example_data
>>> data = create_example_data()
>>> result = per_capita_adjusted_budget(
... population_ts=data["population"],
... allocation_year=2030,
... emission_category="co2-ffi",
... country_actual_emissions_ts=data["emissions"],
... gdp_ts=data["gdp"],
... pre_allocation_responsibility_weight=0.5,
... capability_weight=0.5,
... pre_allocation_responsibility_year=2020,
... )
Converting units...
>>> result.approach
'per-capita-adjusted-budget'
>>> # Shares sum to 1.0
>>> shares = result.relative_shares_cumulative_emission
>>> bool(abs(shares["2030"].sum() - 1.0) < 1e-10)
True
>>> # Parameters include normalized weights
>>> result.parameters["pre_allocation_responsibility_weight"]
0.5...
>>> result.parameters["capability_weight"]
0.5...
Using only responsibility adjustment (no capability):
>>> result_resp_only = per_capita_adjusted_budget(
... population_ts=data["population"],
... allocation_year=2030,
... emission_category="co2-ffi",
... country_actual_emissions_ts=data["emissions"],
... pre_allocation_responsibility_weight=1.0,
... capability_weight=0.0,
... pre_allocation_responsibility_year=2020,
... )
Converting units...
>>> # Countries with higher historical emissions get lower allocations
>>> result_resp_only.approach
'per-capita-adjusted-budget'
>>> result_resp_only.parameters["pre_allocation_responsibility_weight"]
1.0...
>>> result_resp_only.parameters["capability_weight"]
0.0...
per_capita_adjusted_gini_budget¶
fair_shares.library.allocations.budgets.per_capita.per_capita_adjusted_gini_budget ¶
per_capita_adjusted_gini_budget(
population_ts: TimeseriesDataFrame,
gdp_ts: TimeseriesDataFrame,
gini_s: DataFrame,
allocation_year: int,
emission_category: str,
country_actual_emissions_ts: (
TimeseriesDataFrame | None
) = None,
pre_allocation_responsibility_weight: float = 0.0,
capability_weight: float = 1.0,
pre_allocation_responsibility_year: int = 1990,
pre_allocation_responsibility_per_capita: bool = True,
pre_allocation_responsibility_exponent: float = 1.0,
pre_allocation_responsibility_functional_form: str = "asinh",
capability_per_capita: bool = True,
capability_exponent: float = 1.0,
capability_functional_form: str = "asinh",
income_floor: float = 0.0,
max_gini_adjustment: float = 0.8,
max_deviation_sigma: float | None = 2.0,
preserve_allocation_year_shares: bool = False,
historical_discount_rate: float = 0.0,
group_level: str = "iso3c",
unit_level: str = "unit",
ur: PlainRegistry = get_default_unit_registry(),
) -> BudgetAllocationResult
Per capita budget allocation with pre-allocation responsibility, capability, and Gini adjustments.
This function generates cumulative shares for the allocation year based on adjusted per capita principles, incorporating pre-allocation responsibility, Gini-corrected GDP capability adjustments, and inequality considerations.
Mathematical Foundation
The Gini-adjusted budget allocation extends the per capita adjusted approach by incorporating income inequality within countries.
Core Allocation Formula
For budget allocation with dynamic shares (default mode):
Where:
- \(A(g)\): Budget share allocated to country \(g\)
- \(R(g)\): Responsibility adjustment factor (equals 1.0 if not used)
- \(C_{\text{Gini}}(g, t)\): Gini-adjusted capability factor (equals 1.0 if not used)
- \(P(g, t)\): Population of country \(g\) in year \(t\)
- \(t_a\): Allocation year
Gini Adjustment Process
GDP is adjusted using an interpretation of the Greenhouse Development
Rights (GDR) framework's capability metric (note: GDR was designed for
burden-sharing; fair-shares adapts its capability calculation for
entitlement allocation). Only income above a development threshold counts
as capability. When combined with the income floor, higher inequality
means more national income sits above the threshold — increasing measured
capability. See
:func:~fair_shares.library.utils.math.allocation.calculate_gini_adjusted_gdp
for the full mathematical derivation.
Responsibility Adjustment
Identical to per capita adjusted budget (see that function for details).
For per capita responsibility (:code:pre_allocation_responsibility_per_capita=True, default):
Where:
- \(E(g, t)\): Emissions of country \(g\) in year \(t\)
- \(t_h\): Historical responsibility start year
- \(t_a\): Allocation year
- \(w_r\): Normalized responsibility weight
- \(e_r\): Responsibility exponent
Capability Adjustment with Gini-Adjusted GDP
For per capita capability (:code:capability_per_capita=True, default):
Where:
- \(C_{\text{Gini}}(g, t)\): Gini-adjusted capability factor (inverse - higher adjusted GDP = lower allocation)
- \(\text{GDP}^{\text{adj}}(g, t)\): Gini-adjusted GDP (see :func:
~fair_shares.library.utils.math.allocation.calculate_gini_adjusted_gdp) - \(w_c\): Normalized capability weight
- \(e_c\): Capability exponent
When combined with the income floor, higher inequality means more income above the development threshold, giving high-inequality countries smaller emission allocations than unadjusted GDP would suggest.
Two allocation modes are supported based on
:code:preserve_allocation_year_shares:
- False (default): Uses cumulative adjusted population from allocation_year onwards
- True: Uses adjusted population at allocation_year only
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
population_ts
|
TimeseriesDataFrame
|
Timeseries of population for each group of interest |
required |
gdp_ts
|
TimeseriesDataFrame
|
Timeseries of GDP for each group of interest (required) |
required |
gini_s
|
DataFrame
|
DataFrame containing Gini coefficient data for each group (required) |
required |
allocation_year
|
int
|
Year from which to start calculating allocations and budgets. See docs/science/parameter-effects.md#allocation_year for how this affects country shares |
required |
emission_category
|
str
|
Emission category to include in the output |
required |
country_actual_emissions_ts
|
TimeseriesDataFrame | None
|
Historical emissions data (required if pre_allocation_responsibility_weight > 0) |
None
|
pre_allocation_responsibility_weight
|
float
|
Weight for historical responsibility adjustment (0.0 to 1.0). See docs/science/parameter-effects.md#pre_allocation_responsibility_weight for real allocation examples showing how this affects country shares |
0.0
|
capability_weight
|
float
|
Weight for economic capability adjustment (0.0 to 1.0, default 1.0) |
1.0
|
pre_allocation_responsibility_year
|
int
|
Start year for calculating historical responsibility (default: 1990) |
1990
|
pre_allocation_responsibility_per_capita
|
bool
|
If True, use per capita emissions; if False, use absolute emissions |
True
|
pre_allocation_responsibility_exponent
|
float
|
Exponent for the responsibility adjustment function |
1.0
|
pre_allocation_responsibility_functional_form
|
str
|
Functional form for responsibility: "asinh" or "power" |
'asinh'
|
capability_exponent
|
float
|
Exponent for the capability adjustment function |
1.0
|
capability_functional_form
|
str
|
Functional form for capability: "asinh" or "power" |
'asinh'
|
income_floor
|
float
|
Income floor for Gini adjustment (in USD PPP per capita). Income below this threshold is excluded from capability calculations, adapted from the Greenhouse Development Rights (GDR) development threshold [Baer 2013] (GDR was designed for burden-sharing; fair-shares uses its capability metric in an entitlement allocation context). See docs/science/parameter-effects.md#income_floor for real allocation examples showing how this affects country shares |
0.0
|
max_gini_adjustment
|
float
|
Maximum allowed Gini adjustment factor |
0.8
|
max_deviation_sigma
|
float | None
|
Maximum allowed deviation from equal per capita in standard deviations. Constrains allocations to remain within a statistically reasonable range of the equal per capita baseline, preventing extreme adjustments. If None, no constraint is applied |
2.0
|
preserve_allocation_year_shares
|
bool
|
If False (default), shares are calculated using cumulative adjusted population from allocation_year onwards. If True, shares calculated at allocation_year are preserved |
False
|
historical_discount_rate
|
float
|
Discount rate for historical emissions (0.0 to <1.0). When > 0, earlier emissions are weighted less via (1 - rate)^(reference_year - t). Implements natural CO2 removal rationale (Dekker Eq. 5). Default: 0.0 (no discounting). |
0.0
|
group_level
|
str
|
Level in the index which specifies group information |
'iso3c'
|
unit_level
|
str
|
Level in the index which specifies the unit of each timeseries |
'unit'
|
ur
|
PlainRegistry
|
The unit registry to use for calculations |
get_default_unit_registry()
|
Returns:
| Type | Description |
|---|---|
BudgetAllocationResult
|
Container with relative shares for cumulative emissions budget allocation. The TimeseriesDataFrame contains only the allocation_year column with Gini-adjusted capability-weighted population shares that sum to 1 across groups for the specified emission category. |
Notes
Theoretical grounding:
See docs/science/allocations.md#gini-adjusted for detailed mathematical formulation, intra-national equity considerations, and when to use this approach.
For translating equity principles into code, see:
- docs/science/principle-to-code.md#subsistence-protection for income floor and Gini adjustment implementation
- docs/science/principle-to-code.md#cbdr-rc for combining with pre-allocation responsibility and capability
Examples:
Calculate allocation with Gini-adjusted capability (no responsibility):
>>> from fair_shares.library.utils import create_example_data
>>> data = create_example_data()
>>> result = per_capita_adjusted_gini_budget(
... population_ts=data["population"],
... gdp_ts=data["gdp"],
... gini_s=data["gini"],
... allocation_year=2030,
... emission_category="co2-ffi",
... pre_allocation_responsibility_weight=0.0,
... capability_weight=1.0,
... )
Converting units...
>>> result.approach
'per-capita-adjusted-gini-budget'
>>> # Shares are adjusted for inequality in addition to GDP
>>> shares = result.relative_shares_cumulative_emission
>>> bool(abs(shares["2030"].sum() - 1.0) < 1e-10) # Should sum to 1.0
True
>>> # Gini parameters are stored in result
>>> result.parameters["income_floor"]
0.0...
>>> result.parameters["max_gini_adjustment"]
0.8...
Calculate allocation with both responsibility and Gini-adjusted capability:
>>> result_full = per_capita_adjusted_gini_budget(
... population_ts=data["population"],
... gdp_ts=data["gdp"],
... gini_s=data["gini"],
... allocation_year=2030,
... emission_category="co2-ffi",
... country_actual_emissions_ts=data["emissions"],
... pre_allocation_responsibility_weight=0.5,
... capability_weight=0.5,
... pre_allocation_responsibility_year=2020,
... )
Converting units...
>>> result_full.approach
'per-capita-adjusted-gini-budget'
>>> # Both adjustments are reflected in parameters
>>> result_full.parameters["pre_allocation_responsibility_weight"]
0.5...
>>> result_full.parameters["capability_weight"]
0.5...
>>> # With income floor, high inequality means more income above threshold
>>> shares_full = result_full.relative_shares_cumulative_emission
>>> bool(abs(shares_full["2030"].sum() - 1.0) < 1e-10)
True
See Also¶
- Pathway Allocations: Annual emission pathways
- Scientific Documentation: Budget Allocations: Theoretical foundations
- country-fair-shares Guide: Conceptual overview