Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ngfs-toolkit

Turn emissions and profit figures into carbon costs and stranded-asset flags under NGFS carbon-price scenarios — three benchmark prices, four vectorized scoring functions, zero setup.

import pandas as pd
from ngfs_toolkit import list_scenarios, stranding_score, is_stranded

firms = pd.DataFrame({
    "firm": ["Alpha Cement", "Beta Agri", "Gamma Logistics"],
    "profit_musd": [12.0, 3.5, 1.2],        # annual profit, M USD
    "emissions_mtco2e": [0.05, 0.08, 0.02], # annual emissions, MtCO2e
})

for scenario in list_scenarios():
    firms[f"score | {scenario}"] = stranding_score(
        firms["profit_musd"], firms["emissions_mtco2e"], scenario
    )
    firms[f"stranded | {scenario}"] = is_stranded(
        firms["profit_musd"], firms["emissions_mtco2e"], scenario
    )

Output (positive score = carbon cost exceeds profit = stranded under that scenario):

           firm  score | Delayed Transition  stranded | Delayed Transition  score | Net Zero(NZ) 2050  stranded | Net Zero(NZ) 2050  score | Divergent Net Zero  stranded | Divergent Net Zero
   Alpha Cement                       -11.5                          False                       -6.5                         False                         3.0                           True
      Beta Agri                        -2.7                          False                        5.3                          True                        20.5                           True
Gamma Logistics                        -1.0                          False                        1.0                          True                         4.8                           True

Nobody is stranded at 10 USD/t, everybody is at 300 USD/t — the interesting information is who flips in between.

What it does

  • Packages the NGFS Phase 3 benchmark carbon prices (REMIND model, USD 2010 per tCO2e) as a plain dict with a lookup helper that gives useful errors:

    >>> from ngfs_toolkit import SCENARIOS, get_price
    >>> SCENARIOS
    {'Delayed Transition': 10.0, 'Net Zero(NZ) 2050': 110.0, 'Divergent Net Zero': 300.0}
    >>> get_price("Net Zero 2050")
    KeyError: "Unknown scenario 'Net Zero 2050'. Did you mean 'Net Zero(NZ) 2050' or 'Divergent Net Zero'?"
  • Scores carbon exposure with four vectorized primitives — carbon_cost, adjusted_profit, stranding_score, is_stranded — that accept scalars, lists, numpy arrays or pandas Series and return the same kind of object.

  • Provides drop-in DataFrame wrappers (add_carbon_cost_index, add_adjusted_profit, add_carbon_risk_score) with the exact signatures and column-naming behaviour of the original research pipeline. Parity is enforced by tests against verbatim copies of the original code (tests/reference/), bit-for-bit on a 50-row synthetic frame across all three scenarios.

  • Computes a physical climate-stress composite (climate_stress): 0.4*drought + 0.4*flood + 0.2*z(temperature).

Extracted and generalized from a climate-finance research pipeline on agricultural credit risk.

Install

git clone https://github.com/0scarito/ngfs-toolkit
cd ngfs-toolkit
pip install -e .

Requires Python >= 3.10, numpy >= 1.24, pandas >= 2.0. No network access, no API keys, no configuration.

Usage

Vectorized primitives

from ngfs_toolkit import carbon_cost, adjusted_profit, stranding_score

carbon_cost(0.5, "Divergent Net Zero")               # 0.5 t * 300 USD/t = 150.0
carbon_cost(0.5, 87.0)                               # explicit price also works
adjusted_profit(100.0, 0.5, "Net Zero(NZ) 2050")     # 100 - 55 = 45.0
stranding_score(45.0, 0.5, "Divergent Net Zero")     # 150 - 45 = 105.0 -> stranded

Units are up to you, but keep them consistent: prices are per tCO2e, so tCO2e emissions give USD costs and MtCO2e give M USD.

DataFrame pipeline (drop-in for the original research code)

from ngfs_toolkit import (
    SCENARIOS, add_carbon_cost_index, add_adjusted_profit, add_carbon_risk_score,
)

df = add_carbon_cost_index(df, SCENARIOS, proxy_col="Emission_Proxy")
df = add_adjusted_profit(df, SCENARIOS, profit_col="Net_Profit")
df = add_carbon_risk_score(df, SCENARIOS, profit_col="Net_Profit")
# adds Carbon_Cost_<scenario>, Adj_Profit_<scenario>, Carbon_Risk_Score_<scenario>

Pass scenarios=None to use the packaged NGFS benchmarks. Note the cost index shifts each cost column so its minimum is 0 (the emission proxy is typically z-scored, so raw products can be negative) — it is a relative index within your sample, not an absolute cost.

Climate stress

from ngfs_toolkit import climate_stress

climate_stress(drought=[0.5, 0.5], flood=[0.25, 0.25], temperature=[10.0, 20.0])
# array([0.15857864, 0.44142136])

How it works

The scoring model is deliberately simple:

  • carbon_cost = emissions x price, with prices from the NGFS Phase 3 scenario set (REMIND-MAgPIE outputs, USD 2010 per tCO2e): 10 (Delayed Transition), 110 (Net Zero 2050), 300 (Divergent Net Zero).
  • adjusted_profit = profit - carbon_cost — what is left if the firm had to pay for its emissions at the scenario price.
  • stranding_score = carbon_cost - profit — positive means the scenario carbon bill exceeds current profit, i.e. the asset's economics do not survive that carbon price. This is a first-order transition-risk screen in the spirit of stranded-asset analyses (Caldecott, 2017).
  • climate_stress = 0.4*drought + 0.4*flood + 0.2*z(temperature), where z is the sample z-score with a 1e-9 floor on the standard deviation. Weights come from the original research pipeline, not from an estimation procedure.

Limitations

Honest list:

  • Three benchmark price points, not full NGFS trajectories. The NGFS scenarios are multi-decade price paths varying by region and model; this package ships one representative point per scenario. Loading full trajectories from the NGFS Scenario Explorer is roadmap, not implemented.
  • Static, first-order screen. No discounting, no demand response, no pass-through, no abatement — a firm that can cut emissions or raise prices is treated the same as one that cannot.
  • Prices are in USD 2010 real terms. Comparing them against nominal current-year profits mixes price bases; rebase your financials or accept the approximation.
  • The cost index is sample-relative. add_carbon_cost_index min-shifts each column, so values depend on which rows are in your frame; only the vectorized carbon_cost is an absolute figure.
  • climate_stress z-scores within the sample. The same row scored inside two different samples gives two different results, and the 0.4/0.4/0.2 weights are a research-pipeline convention, not calibrated coefficients.
  • Emission proxies are your problem. The original pipeline used a z-scored proxy, not measured emissions; garbage proxies give garbage scores.

References

  • NGFS (2022). NGFS Climate Scenarios for central banks and supervisors, Phase 3. https://www.ngfs.net/ngfs-scenarios-portal/
  • Luderer, G. et al. REMIND-MAgPIE integrated assessment model (Potsdam Institute for Climate Impact Research), one of the models behind the NGFS scenario set.
  • Caldecott, B. (2017). Introduction to special issue: stranded assets and the environment. Journal of Sustainable Finance & Investment, 7(1), 1-13.

Acknowledgments

Extracted from a group research project by Oscar, Daniil, Oulaya, Ulysse and Yuhan (CentraleSupelec / ESSEC coursework): https://github.com/0scarito/Research-project. The tests/reference/ directory keeps verbatim copies of the original modules so parity with the research code stays enforced by CI.

License

MIT

About

NGFS carbon-price scenarios + carbon-cost and stranded-asset scoring utilities for pandas/numpy

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages