Compute emissions

morie.emissions (Python) and morie_emissions_* (R) estimate the energy and CO2-equivalent of a computation the way CodeCarbon does, without its dependencies. The usage page is Measure the compute cost, and prove it.

Energy

Power is sampled every measure_power_secs and integrated:

\[E = \sum_i \left( P_{cpu}(u_i) + P_{ram} \right) \Delta t_i, \qquad P_{cpu}(u) = TDP \left(0.1 + 0.9\,u^3\right)\]

where \(u_i\) is the machine’s CPU utilisation over the sample (the kernel’s aggregate counters: /proc/stat on Linux, host_statistics on macOS; the Python arm reads them from a daemon thread, the R arm from a C++ std::thread, so the same cubic weighting applies per sample in both) and TDP is looked up from the CPU model name (Apple M-series 10-15 W, Core i5/i7 65 W, i9 125 W, Ryzen 7 65 W, Ryzen 9 105 W, Xeon 150 W, otherwise 85 W). RAM power is 5 W per DIMM on x86 and 1.5 W on ARM with the CodeCarbon DIMM-count estimate from total memory and its efficiency scaling beyond four DIMMs (minimum 10 W / 3 W). No GPU power is counted. On a platform without CPU counters the R arm uses the process’s CPU time over wall time and records tracking_mode = process.

Emissions

\[CO_2eq = E \times PUE \times I_{grid}, \qquad H_2O = E \times PUE \times WUE\]

\(I_{grid}\) (kg CO2eq per kWh) comes from the IEA / Our World in Data energy mix shipped with both packages (213 countries): the country’s published intensity when it has one, otherwise the generation-weighted mean of the per-source intensities (coal 995, petroleum 816, natural gas 743, fossil 635, geothermal 38, hydro 26, nuclear 29, solar 48, wind 26 gCO2/kWh), and the world average of 475 g/kWh for an unknown country. The country is the country_iso_code argument, else MORIE_COUNTRY_ISO, else a geolocation lookup (skipped under MORIE_EMISSIONS_OFFLINE).

The CSV row

One row per run in the CodeCarbon layout (36 columns: timestamp, project, run id, duration, emissions and rate, CPU / GPU / RAM power and energy, total energy, water, country, OS, interpreter, CPU count and model, RAM size, tracking mode, utilisation, PUE, WUE, …), so rows from either package or from CodeCarbon itself concatenate.

Provenance

Each run is sealed in a bricklayer capsule: emissions_manifest.json (measurements, method, sources, environment) and capsule_bundle.json (SHA-256 of the CSV and the manifest under an ML-DSA-44 signature; rmoriebricklayer::capsule_bundle()). rmorie::morie_emissions_verify() checks it. The key is per run unless supplied, which makes the capsule a proof of integrity, not of authorship.

Limits

The TDP-times-utilisation model is an estimate, not a measurement: it ignores GPUs, frequency scaling and the rest of the machine, and the RAM heuristic is coarse. A machine with a power meter or RAPL counters will disagree with it by a factor that depends on the workload. The figure is comparable across runs on one machine and across the two packages, which is what the capsule is for.

References

  • CodeCarbon, https://github.com/mlco2/codecarbon (methodology and the energy-mix data, MIT licence).

  • IEA, Global Energy and CO2 Status Report; Our World in Data, electricity mix by country.