Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions codecarbon/emissions_tracker.py
Original file line number Diff line number Diff line change
Expand Up @@ -501,7 +501,9 @@ def __init__(
`sudo lshw -C memory -short | grep DIMM` to get RAM slots,
then RAM power (W) = Number of RAM Slots × 5 Watts.
:param pue: PUE (Power Usage Effectiveness) of the data center where the
experiment is being run.
experiment is being run. It multiplies both the reported power
and the reported energy, including forced values: with
`force_cpu_power=100` and `pue=1.5` the CPU is reported at 150 W.
:param wue: WUE (Water Usage Effectiveness) of the data center. Units of L/kWh:
litres of water consumed per kilowatt-hour of electricity consumed.
:param force_carbon_intensity_g_co2e_kwh: Override grid carbon intensity
Expand Down Expand Up @@ -1174,7 +1176,9 @@ def _do_measurements(self) -> None:
power,
energy,
) = hardware.measure_power_and_energy(last_duration=last_duration)
# Apply the PUE of the datacenter to the consumed energy
# Apply the PUE of the datacenter to both the power and the consumed
# energy, so the two stay consistent at the facility level
power *= self._pue
energy *= self._pue
water = Water.from_litres(litres=self._wue * energy.kWh)
self._total_energy += energy
Expand Down Expand Up @@ -1584,7 +1588,9 @@ def track_emissions(
:param force_ram_power: Force the RAM power consumption in watts. Estimate with
`sudo lshw -C memory -short | grep DIMM` for RAM slots,
then RAM power (W) = Number of RAM Slots × 5 Watts.
:param pue: PUE (Power Usage Effectiveness) of the data center.
:param pue: PUE (Power Usage Effectiveness) of the data center. It multiplies both
the reported power and the reported energy, including forced values:
with `force_cpu_power=100` and `pue=1.5` the CPU is reported at 150 W.
:param wue: WUE (Water Usage Effectiveness) of the data center. Units of L/kWh:
litres of water consumed per kilowatt-hour of electricity consumed.
:param force_carbon_intensity_g_co2e_kwh: Override grid carbon intensity
Expand Down
9 changes: 9 additions & 0 deletions docs/reference/output.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,15 @@ The package has an in-built logger that logs data into a CSV file named `emissio
| ram_utilization_percent | Average RAM utilization during tracking period (%) |
| ram_used_gb | Average RAM used during tracking period (GB) |

!!! note "PUE and the power columns"
Since v3.4.0, the `pue` multiplier is applied to the reported power columns
(`cpu_power`, `gpu_power`, `ram_power`) as well as to the energy columns, so
that `energy_consumed` stays reconstructible from the reported power. Before
that, only the energy was scaled. With `pue=1.5`, a machine measured at 100 W
is reported as 150 W, and that also applies to forced values: `force_cpu_power=100`
with `pue=1.5` reports a 150 W CPU. This is intended: the columns describe the
power drawn at the facility level, not at the socket.

!!! note
Developers can enhance the Output interface by implementing a custom class that extends `BaseOutput` at `codecarbon/output.py`. For example, to log into a database.

Expand Down
33 changes: 33 additions & 0 deletions tests/test_emissions_tracker_constant.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,39 @@ def test_carbon_tracker_offline_constant_force_cpu_power(
assertdf = pd.read_csv(self.emissions_file_path)
self.assertEqual(USER_INPUT_CPU_POWER / 2, assertdf["cpu_power"][0])

@mock.patch.object(cpu.TDP, "_get_cpu_power_from_registry")
@mock.patch.object(cpu, "is_psutil_available")
def test_carbon_tracker_offline_constant_pue(self, mock_tdp, mock_psutil):
# The PUE must be applied to the reported power as well as to the energy,
# so that energy_consumed stays consistent with the power columns.
USER_INPUT_CPU_POWER = 1_000
PUE = 2.0
mock_tdp.return_value = None
mock_psutil.return_value = False
tracker = OfflineEmissionsTracker(
country_iso_code="USA",
output_dir=self.emissions_path,
output_file=self.emissions_file,
force_cpu_power=USER_INPUT_CPU_POWER,
pue=PUE,
)
tracker.start()
heavy_computation(run_time_secs=1)
emissions = tracker.stop()
assert isinstance(emissions, float)
assertdf = pd.read_csv(self.emissions_file_path)
self.assertEqual(USER_INPUT_CPU_POWER / 2 * PUE, assertdf["cpu_power"][0])
# energy_consumed must be reconstructible from the reported power
total_power = (
assertdf["cpu_power"][0]
+ assertdf["gpu_power"][0]
+ assertdf["ram_power"][0]
)
expected_energy = total_power * assertdf["duration"][0] / 3600 / 1000
self.assertAlmostEqual(
expected_energy, assertdf["energy_consumed"][0], delta=expected_energy * 0.1
)

@mock.patch("codecarbon.external.hardware.psutil.cpu_percent", return_value=50.0)
@mock.patch.object(cpu.TDP, "_get_cpu_power_from_registry")
@mock.patch.object(cpu, "is_psutil_available")
Expand Down
Loading