Skip to content

feat: add SCI report output method - #1361

Open
davidberenstein1957 wants to merge 3 commits into
masterfrom
feat/sci-report-output
Open

feat: add SCI report output method#1361
davidberenstein1957 wants to merge 3 commits into
masterfrom
feat/sci-report-output

Conversation

@davidberenstein1957

@davidberenstein1957 davidberenstein1957 commented Aug 12, 2026

Copy link
Copy Markdown
Collaborator

Adds an SCI (Software Carbon Intensity, ISO/IEC 21031:2024) output method, following the same pattern as the existing BoAmps exporter.

What it adds

codecarbon/output_methods/sci.py, a single module holding the two declaration dataclasses, the pure mapping function, and the handler:

  • FunctionalUnit / EmbodiedDeclaration — the R and M terms, which only the user can supply.
  • map_emissions_to_sci(data, ...) — pure, no I/O. I is derived as emissions * 1000 / energy_consumed rather than recomputed, so the report agrees with the CSV by construction and inherits the cloud-region, PUE and country/region handling already applied upstream.
  • SCIOutput(BaseOutput)out() writes sci_report_<run_id>.json, task_out() writes sci_report_tasks_<run_id>.json with one entry per task, live_out() is a documented no-op because the report is final rather than incremental.
  • OutputMethod.SCI registered in _init_output_methods, reading an optional sci_context_file from the usual config hierarchy.

Two honesty rules, both tested:

  • No R declared → the report is still written, with sci: null and a status field explaining why, instead of dividing by zero or assuming R = 1.
  • No M declared → M_gCO2e: 0 with M_source: "not declared", so a reader can see the report covers only the operational half.

User-facing surface

from codecarbon import EmissionsTracker
from codecarbon.output_methods.sci import FunctionalUnit, SCIOutput

sci = SCIOutput(functional_unit=FunctionalUnit(name="inference request", count=10_000))
tracker = EmissionsTracker(output_handlers=[sci])

sci.set_functional_unit_count(n) for the common case where the count is only known at the end; SCIOutput.from_file("sci_context.json") for the declarative path; output_methods = csv,sci plus sci_context_file in .codecarbon.config.

Docs: a new SCI section in docs/reference/output.md (including a sample report), a row in docs/how-to/examples.md, and a runnable examples/sci_output.py. No new dependencies.

Verification

uv run pytest tests/test_sci_output.py -q — 14 tests covering the formula, provenance assembly, the undeclared-R and undeclared-M branches, zero-energy, the handler and its task path, and context-file loading (valid, missing, malformed). black --check and ruff check were run scoped to the touched files; the remaining ruff hits are the same Optional/List/dict() style the surrounding modules already use, so the new code matches its neighbours.

Deliberately left out

  • Any automatic embodied-carbon estimation: no EmbodiedProfile tracker parameter, no bundled hardware table, no cloud per-instance figures. CodeCarbon has no defensible manufacturing data, and an uncited M in a compliance report is worse than a blank one. M is user-declared or explicitly undeclared.
  • Any change to what emissions means. Existing consumers are untouched.
  • Multiple simultaneous functional units, and schema validation on write (there is no stable machine-readable SCI schema to validate against yet).

Closes #1353

🤖 Generated with Claude Code


Review follow-up: embodied carbon in task reports

task_out() passed the same declared M to every task, so a run with 5 tasks reported the device's full embodied carbon 5 times, and each per-task sci included 100% of it — a wrong number in a document framed as ISO/IEC 21031 output.

M is now apportioned by each task's share of the run duration. Chosen over omitting M from task reports because the split is exhaustive: R is already the run-level count, so with an apportioned M the per-task figures sum back to the run-level report instead of quietly dropping half the accounting. M_source records the share applied, e.g. "vendor LCA; apportioned by duration (25.0% of the run)".

Tested by test_task_out_apportions_embodied_by_duration (unequal durations: asserts the 10/30 split of a declared 40 gCO2e and that the shares sum back to 40), plus the undeclared-M and zero-duration branches.

Also documented in docs/reference/output.md that the configuration-only path (output_methods = csv,sci) can never produce a non-null sci unless functionalUnit.count is hardcoded in the context file, since set_functional_unit_count() needs a reference to a handler the tracker owns.

Emit an ISO/IEC 21031 Software Carbon Intensity report alongside the
existing output methods. E and I come from the measured run; R and M are
user declarations and are reported as undeclared rather than guessed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@codecov

codecov Bot commented Aug 12, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 91.60%. Comparing base (065d0e6) to head (e06fdc3).

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #1361      +/-   ##
==========================================
+ Coverage   91.39%   91.60%   +0.20%     
==========================================
  Files          49       50       +1     
  Lines        5056     5156     +100     
==========================================
+ Hits         4621     4723     +102     
+ Misses        435      433       -2     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

davidberenstein1957 and others added 2 commits August 12, 2026 19:12
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
task_out() handed the same declared M to every task, so a run with 5 tasks
reported the device's full embodied carbon 5 times and each per-task sci
included 100% of it.

Split M by each task's share of the run duration. Chosen over dropping M
from task reports because the split is exhaustive: the per-task figures now
sum back to the run-level report, and M_source records the share applied.

Also document that `output_methods = csv,sci` alone can never produce a
non-null sci unless the context file hardcodes functionalUnit.count.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@davidberenstein1957
davidberenstein1957 marked this pull request as ready for review August 12, 2026 19:14
@davidberenstein1957
davidberenstein1957 requested a review from a team as a code owner August 12, 2026 19:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature proposal: SCI (ISO/IEC 21031) report output method

1 participant