Energy Estimation

Methodology and sources for CI Sizer’s energy consumption and carbon footprint estimates.

Overview

CI Sizer estimates the energy consumption and carbon footprint of CI/CD runner executions using established industry models. CPU utilization data collected by the collector sidecar (from /proc/stat) is combined with hardware power characteristics and grid carbon intensity to produce per-run energy scores.

These are statistical estimates, not real power measurements. They are suitable for trend analysis, cross-run comparison, and sustainability reporting.

Power Estimation Models

Teads SPECpower Curve (TDP-based)

When the hardware’s Thermal Design Power (TDP) is known and no per-vCPU min/max watt bounds are set, power is estimated using a 4-point piecewise linear interpolation derived from SPECpower benchmark data.

CPU UtilizationPower Coefficient (x TDP)
0%0.12
10%0.32
50%0.75
100%1.02
Power(u) = TDP x interpolate(u, [0, 10, 50, 100], [0.12, 0.32, 0.75, 1.02])

Between anchor points, values are linearly interpolated. The 1.02 coefficient at 100% accounts for turbo-boost overshoot above nominal TDP.

Source: Benjamin Davy, Teads Engineering (2021), standardized by the Green Software Foundation Impact Framework.

References:

CCF Linear Model (vCPU-based)

When only the vCPU count is known (or when per-vCPU min/max watt bounds are available), the Cloud Carbon Footprint linear interpolation model is used.

Power = vCPUs x (MinWatts + u x (MaxWatts - MinWatts))

Default coefficients (AWS average from SPECpower_ssj2008 benchmarks):

ParameterValueMeaning
MinWatts0.74 W/vCPUIdle power per vCPU
MaxWatts3.50 W/vCPUMax-load power per vCPU

When a specific CPU is auto-detected, TDP-derived bounds replace the defaults:

MinWatts = TDP x 0.12 / vCPUs    (idle fraction from Teads curve)
MaxWatts = TDP x 1.02 / vCPUs    (max fraction from Teads curve)

Reference: Cloud Carbon Footprint Methodology

Model Selection

ConditionModel Used
Profile has per-vCPU min/max watts (both > 0)CCF Linear
Profile has TDP only (no min/max watts)Teads Curve

In practice, auto-detected CPUs derive min/max watts from TDP, so the CCF linear model is used for both generic and auto-detected profiles. The Teads curve is only used when a user provides a raw TDP-only hardware profile.

Energy Calculation

Energy_raw (kWh) = Power (W) x Duration (s) / 3600 / 1000
Energy_adjusted (kWh) = Energy_raw x PUE

Power Usage Effectiveness (PUE)

ParameterValue
Default1.3

A PUE of 1.3 means the datacenter uses 30% more energy than the IT equipment alone. This is a conservative middle ground:

ContextTypical PUE
Hyperscalers (Google, AWS, Azure)1.10–1.18
New datacenter builds (Uptime 2024)~1.3
Industry average1.56

Provider-specific PUEs (from Cloud Carbon Footprint):

ProviderPUE
AWS1.135
GCP1.1
Azure1.125

References:

Carbon Intensity

Carbon emissions are calculated as:

Carbon (gCO2eq) = Energy (kWh) x CarbonIntensity (gCO2eq/kWh)

Carbon intensity is resolved through a 3-tier fallback chain. Each tier is tried in order; the first successful response wins. The methodology string always reflects which tier actually supplied the data.

Data Quality Comparison

Energy Charts (Tier 1)FfE Projection (Tier 2)Static Table (Tier 3)
Data typeReal MW from actual power plantsModeled scenario projectionDerived seasonal averages
UpdatesEvery 15 minutesStatic per projection yearNever (compiled into binary)
AccuracyActual grid state right nowWeather-year-2012 estimateSeasonal/hourly average
Zones13 EU countriesDE only13 EU countries
AvailabilitySometimes delayed or unavailableAlways availableAlways available
Reflects today’s weather✅ Yes — real wind/solar/demand❌ No — same values every year for the same hour❌ No — averaged over months

Tier 1: Energy Charts Real-Time (default)

PropertyValue
SourceFraunhofer Institute for Solar Energy Systems (Fraunhofer ISE)
APIhttps://api.energy-charts.info/public_power?country={cc}
DataReal-time actual generation — MW output from real power plants operating right now
Resolution15-minute intervals, updated continuously
AuthenticationNone required
CacheIn-memory, 15-minute TTL (matching data resolution)
Methodology stringenergy-charts
Supported zonesDE, AT, FR, NL, PL, DK, CH, ES, IT, BE, SE, NO, FI

Direct carbon intensity calculation: Carbon intensity is calculated directly from the generation mix using IPCC AR5 lifecycle emission factors per fuel type: grid_intensity = Σ(fuel_MW × emission_factor) / Σ(generation_MW). Each 15-minute interval’s generation data is fetched from the /public_power endpoint. For each production type with a known emission factor, the MW output is multiplied by the factor; the weighted sum is divided by total generation to yield gCO₂eq/kWh. Negative values (storage consumption) and non-generation keys (Load, Battery Consumption, etc.) are excluded.

IPCC AR5 lifecycle emission factors used by CI Sizer:

Fuel Type (Energy Charts name)gCO₂eq/kWhSource
Fossil peat1100IPCC AR5
Fossil brown coal / lignite1054IPCC AR5
Fossil hard coal888IPCC AR5
Fossil coal-derived gas850IPCC AR5
Fossil oil733IPCC AR5
Fossil gas410IPCC AR5
Others400Conservative estimate
Waste330IPCC AR5 (mixed waste)
Biomass230IPCC AR5
Solar45IPCC AR5
Geothermal38IPCC AR5
Other renewables30IPCC AR5
Hydro Run-of-River24IPCC AR5
Hydro water reservoir24IPCC AR5
Hydro pumped storage24IPCC AR5
Nuclear12IPCC AR5
Wind offshore12IPCC AR5
Wind onshore11IPCC AR5

For example, when the grid runs on 5000 MW lignite and 5000 MW gas: (5000×1054 + 5000×410) / 10000 = 732 gCO₂eq/kWh.

This approach calculates intensity directly from the actual fuel dispatch, providing more accurate values than the previous simplified formula.

Reference: Fraunhofer ISE — Energy Charts

Tier 2: FfE Projection Data (first fallback)

PropertyValue
SourceForschungsstelle für Energiewirtschaft (FfE), Munich
DataModeled projection — 8760 hourly values from the Dynamis energy scenario model, based on weather reference year 2012
StorageAzure blob storage (no rate limits): ffeopendatastorage.blob.core.windows.net
LicenseCC-BY-4.0
Year selectionNearest available projection year (2020, 2025, 2030, 2035, 2040, 2045, 2050)
CacheIn-memory, 24-hour TTL (data is static per year)
Methodology stringffe-projection

These are modeled projections, NOT actual measurements. The simulation uses weather reference year 2012 to produce a plausible hourly carbon intensity profile. This means:

  • The same hour-of-year always returns the same value, regardless of when you query
  • It captures realistic seasonal and diurnal patterns (e.g., midday solar dips, winter peaks)
  • It cannot reflect today’s actual wind speed, cloud cover, or demand conditions

Note: FfE projection data is only available for Germany (zone DE). For other zones, Tier 2 is skipped and the chain falls through directly to Tier 3.

The data is produced under the InDEED research project (Integrating Decentralized Energy Data).

Reference: FfE OpenData (InDEED project)

Tier 3: Static Lookup Table (last resort)

A 192-value lookup table for all 13 supported zones, indexed by season (4), day type (weekday/weekend), and hour (24). Derived from Energy Charts /public_power data (2025–2026, IPCC AR5 emission factors).

PatternRange (gCO2/kWh)Cause
Summer midday (10:00–14:00)220–265High solar generation (DE example)
Summer night (00:00–05:00)470–525Fossil baseload (DE example)
Winter (all day)350–435Flatter, wind-dependent (DE example)
Weekend vs. weekday10–20% lowerReduced industrial demand

Methodology string: static

Methodological basis:

  • Kono, J., Ostermeyer, Y. & Wallbaum, H. (2017). “The trends of hourly carbon emission factors in Germany and investigation on relevant consumption patterns for its application.” International Journal of Life Cycle Assessment, 22, 1493–1501. DOI: 10.1007/s11367-017-1277-z
  • Holzapfel, P., Bach, V. & Finkbeiner, M. (2023). “Increasing temporal resolution in greenhouse gas accounting of electricity consumption divided into Scopes 2 and 3.” International Journal of Life Cycle Assessment, 28, 1622–1639. DOI: 10.1007/s11367-023-02240-3

Last-Resort Fallback

ParameterValue
Intensity380 gCO2eq/kWh

Updated from 400 g/kWh in v0.2.x to reflect declining German grid intensity. Per Umweltbundesamt (UBA), the annual average was 386 g/kWh (2023) and 363 g/kWh (2024). The carbon_source field is set to "fallback" to flag this condition.

Reference: Umweltbundesamt — Strom- und Wärmeversorgung in Zahlen

Provider Selection

The --carbon-provider flag (or RUNNER_CARBON_PROVIDER env var) controls which providers are used:

ValueBehaviorUse Case
energy-charts (default)Full 3-tier chain: Energy Charts → FfE projection → static tableBest accuracy; requires internet access
ffeLegacy alias — creates the same full 3-tier chain as energy-chartsBackward compatibility
staticStatic lookup table only (no external dependencies)Air-gapped environments, deterministic testing

Multi-Country Carbon Zones

CI Sizer supports 13 European electricity grid zones for carbon intensity estimation. The zone is configured via the --carbon-zone flag or RUNNER_CARBON_ZONE environment variable (default: DE).

Supported Zones

ZoneCountryTypical CI (gCO₂eq/kWh)Notes
ATAustria~67Hydro-dominated
BEBelgium~179Gas + nuclear mix
CHSwitzerland~42Hydro + nuclear
DEGermany~258Mixed (coal/gas/wind/solar)
DKDenmark~88Wind-heavy
ESSpain~94Solar + wind + gas
FIFinland~59Nuclear + hydro + biomass
FRFrance~24Nuclear-dominated
ITItaly~206Gas-heavy
NLNetherlands~369Gas-dominated
NONorway~28Hydro-dominated
PLPoland~505Coal-dominated
SESweden~33Hydro + nuclear

Configuration

# French grid (nuclear-dominated, very low CI)
./collector --carbon-zone FR

# Or via environment variable
RUNNER_CARBON_ZONE=PL ./collector

Provider Chain by Zone

The fallback chain differs depending on the selected zone:

  • DE (Germany): Energy Charts → FfE blob projection → static (seasonal/weekday table with 192 values)
  • All other zones: Energy Charts → static (seasonal/weekday table with 192 values)

FfE projection data (Tier 2) is only available for Germany. For all other zones, the provider chain skips FfE and falls back directly to the static table. All 13 supported zones have full 192-value static tables (4 seasons × 2 day types × 24 hours) derived from Energy Charts data. If all providers fail, the hardcoded 380 gCO₂/kWh fallback is used regardless of zone.

CPU TDP Database

A built-in database of 38 CPU models maps processor names to TDP (Thermal Design Power) values:

FamilyGenerationsTDP Range
Intel Xeon PlatinumSkylake, Cascade Lake, Ice Lake, Sapphire Rapids195–350 W
Intel Xeon GoldVarious165–205 W
Intel Xeon SilverVarious100–135 W
Intel Xeon E5Ivy Bridge, Haswell, Broadwell115–145 W
AMD EPYC Rome7000-series225–280 W
AMD EPYC Milan7000-series225–280 W
AMD EPYC Genoa9000-series360 W
AWS GravitonGraviton2, Graviton3, Graviton4130–210 W
AmpereAltra, AmpereOne160–210 W

Sources:

Graviton TDP values (Graviton2 ~130 W, Graviton3 ~180 W, Graviton4 ~210 W) are engineering estimates based on ARM Neoverse power characteristics, not manufacturer specifications.

Confidence Levels

Each energy score includes a confidence field indicating the quality of the estimate:

LevelHardware SourceTypical Accuracy
user-providedUser explicitly specified hardwareDepends on user
auto-detectedCPU model matched in TDP database±15–20%
generic-estimateFell back to average cloud defaults±50%

The confidence level reflects the hardware profile quality. Carbon data source quality is captured separately in the carbon_source field (energy-charts, ffe-projection, static, or fallback).

Limitations

LimitationImpact
Statistical approximationPower estimates are modeled, not measured from hardware power meters
CPU-only power modelMemory, storage, network, and GPU power are not modeled separately
Carbon intensity variabilityHourly/15-min data is preferred over annual averages; actual intensity varies by time of day and season
Energy Charts emission factorsIPCC AR5 lifecycle emission factors per fuel type are median values; actual plant-level emissions vary, giving ±10–15% uncertainty
FfE projection dataBased on the Dynamis energy scenario model; projection years (2025, 2030, etc.) may not match actual grid conditions
Graviton/ARM TDP estimatesNot manufacturer specifications; based on ARM Neoverse power characteristics
Uniform PUESingle global default; actual PUE varies by datacenter location, load, and ambient temperature
Limited zone supportCarbon intensity available for 13 European zones via Energy Charts (DE, AT, FR, NL, PL, DK, CH, ES, IT, BE, SE, NO, FI). FfE projection data is DE-only. Static fallback covers all 13 zones with full seasonal/weekday/hourly resolution. Unsupported zones use 380 gCO₂/kWh fallback
Average utilizationMean CPU utilization over the run smooths out short spikes

Verifying the Data

You can query the upstream carbon intensity sources directly to verify what CI Sizer is seeing.

Reading the Methodology String

Each energy score in CI Sizer includes a methodology string like:

ccf-linear+energy-charts+DE+pue-1.30
PartMeaning
ccf-linearPower model — Cloud Carbon Footprint linear interpolation (vCPUs × watts)
energy-chartsCarbon intensity source that successfully returned data
DEElectricity grid zone
pue-1.30Power Usage Effectiveness multiplier (1.3× datacenter overhead)

If the carbon source shows ffe-projection or static instead of energy-charts, the system fell back because Energy Charts was unavailable.

Querying Energy Charts (Tier 1)

The Energy Charts API requires lowercase country codes and date-only format (no timestamps):

# German grid generation mix for today (replace dates with today/tomorrow)
curl -s "https://api.energy-charts.info/public_power?country=de&start=2026-05-20&end=2026-05-21" \
  | jq '{
    timestamps: (.unix_seconds | length),
    first: (.unix_seconds[0] | todate),
    last: (.unix_seconds[-1] | todate),
    fuels: [.production_types[] | .name]
  }'

# See actual MW per fuel type at a specific timestamp
curl -s "https://api.energy-charts.info/public_power?country=de&start=2026-05-20&end=2026-05-21" \
  | jq '{
    time: (.unix_seconds[40] | todate),
    generation_MW: [.production_types[] | select(.data[40] != null and .data[40] > 0) | {(.name): (.data[40] | round)}] | add
  }'

# French grid (nuclear-dominated, very low carbon)
curl -s "https://api.energy-charts.info/public_power?country=fr&start=2026-05-20&end=2026-05-21" \
  | jq '[.production_types[] | .name]'

The response contains unix_seconds[] (15-minute intervals) and production_types[{name, data[]}] with MW output per fuel type. CI Sizer multiplies each fuel’s MW by its IPCC AR5 emission factor and divides by total generation to compute gCO₂eq/kWh.

Important: Replace the dates in the examples with today’s date. The API returns data up to the most recent 15-minute interval.

Computing Grid Carbon Intensity

To replicate the exact calculation CI Sizer performs — weighted average of generation MW × emission factor:

# Compute current German grid carbon intensity (same formula as ci-sizer)
curl -s "https://api.energy-charts.info/public_power?country=de&start=$(date -u +%Y-%m-%d)&end=$(date -u -v+1d +%Y-%m-%d)" | jq '
  (.unix_seconds | length - 1) as $idx |
  (.unix_seconds[$idx] | todate) as $time |
  {"Fossil brown coal / lignite":1054,"Fossil hard coal":888,"Fossil gas":410,
   "Fossil oil":733,"Fossil coal-derived gas":850,"Fossil peat":1100,
   "Nuclear":12,"Biomass":230,"Geothermal":38,"Wind offshore":12,
   "Wind onshore":11,"Solar":45,"Hydro Run-of-River":24,
   "Hydro water reservoir":24,"Hydro pumped storage":24,
   "Waste":330,"Others":400,"Other renewables":30} as $f |
  [.production_types[] | select(.data[$idx]!=null and .data[$idx]>0) |
   {name,mw:.data[$idx]} | select($f[.name]!=null) |
   {name,mw,w:(.mw*$f[.name])}] as $g |
  {timestamp: $time,
   grid_carbon_intensity_gCO2_per_kWh: ([$g[]|.w]|add)/([$g[]|.mw]|add)|round,
   total_generation_MW: ([$g[]|.mw]|add)|round,
   top_contributors: [$g | sort_by(-.w)[0:5][] | {fuel:.name, MW:(.mw|round), share_pct:((.w/([$g[]|.w]|add)*100)*10|round/10)}]}'

This takes the most recent 15-minute interval, multiplies each fuel type’s MW output by its IPCC emission factor, sums the weighted values, and divides by total generation to get gCO₂eq/kWh. The top_contributors field shows which fuels are driving most of the carbon impact.

Note: On Linux, replace date -v+1d with date -d "+1 day". Or simply hardcode tomorrow’s date.

Querying FfE Projection (Tier 2)

The FfE blob contains 8760 hourly projection values for an entire year (DE only):

# Get 2025 projection metadata
curl -s "https://ffeopendatastorage.blob.core.windows.net/opendata/id_opendata_2/id_opendata_2_year_2025.json" \
  | jq '[.[] | select(.internal_id == [2,1,1])][0] | {
    year: .year,
    weather_reference_year: .year_weather,
    total_hours: (.values | length),
    annual_average_gCO2_per_kWh: (.value * 1000 | round),
    unit: "values are in kg/kWh, multiply by 1000 for g/kWh"
  }'

# Get projection for a specific hour (e.g., May 20 at 09:00 UTC = hour index 3345)
curl -s "https://ffeopendatastorage.blob.core.windows.net/opendata/id_opendata_2/id_opendata_2_year_2025.json" \
  | jq '[.[] | select(.internal_id == [2,1,1])][0] | {
    hour_index: 3345,
    carbon_intensity_gCO2_per_kWh: (.values[3345] * 1000 | round),
    note: "This is a modeled projection, not real-time data"
  }'

Note: Hour index = (day_of_year - 1) × 24 + hour_utc. These values are static projections based on weather year 2012 — they will return the same result regardless of when you query them.

Tier 3 (Static Table)

The static fallback table is compiled into the binary — there is no external API to query. It provides 192 values per zone (4 seasons × 2 day types × 24 hours) derived from Energy Charts historical data.

StandardReference
Green Software Foundation — Software Carbon Intensity (SCI)sci-guide.greensoftware.foundation
Cloud Carbon Footprintcloudcarbonfootprint.org
SPECpower_ssj2008spec.org/power_ssj2008
Green Software Foundation Impact Frameworkif.greensoftware.foundation