Skip to content

Commit 0fd0e61

Browse files
authored
Feature/add water usage (#941)
* Add support for setting WUE (water usage effectiveness) via a flag and calculating water usage. This adds a --wue flag for a L/kWh WUE value. I also fix some tests that for some reason started failing on Mac. Unclear why they are failing now. * Create documentation and an example script for WUE. Also remove an unecessary asserts in unit.py that was breaking the pue.py example script. It's uncessary because we only need to assert one number in a product is a float to get a float result. * Check scheduler exists before checking if it has stopped - a cause of test flakiness
1 parent bbbdf04 commit 0fd0e61

14 files changed

Lines changed: 145 additions & 37 deletions

File tree

codecarbon/core/units.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,6 @@ def __add__(self, other: "Energy") -> "Energy":
9090
return Energy(self.kWh + other.kWh)
9191

9292
def __mul__(self, factor: float) -> "Energy":
93-
assert isinstance(factor, float)
9493
assert isinstance(self.kWh, float)
9594
result = Energy(self.kWh * factor)
9695
return result
@@ -162,3 +161,19 @@ def __truediv__(self, divisor: float) -> "Power":
162161

163162
def __floordiv__(self, divisor: float) -> "Power":
164163
return Power(self.kW // divisor)
164+
165+
166+
@dataclass
167+
class Water:
168+
"""
169+
Measured in litres
170+
"""
171+
172+
litres: float
173+
174+
@classmethod
175+
def from_litres(cls, litres: float) -> "Water":
176+
return cls(litres=litres)
177+
178+
def __add__(self, other: "Water") -> "Water":
179+
return Water(self.litres + other.litres)

codecarbon/emissions_tracker.py

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
from codecarbon.core.config import get_hierarchical_config
1818
from codecarbon.core.emissions import Emissions
1919
from codecarbon.core.resource_tracker import ResourceTracker
20-
from codecarbon.core.units import Energy, Power, Time
20+
from codecarbon.core.units import Energy, Power, Time, Water
2121
from codecarbon.core.util import count_cpus, count_physical_cpus, suppress
2222
from codecarbon.external.geography import CloudMetadata, GeoMetadata
2323
from codecarbon.external.hardware import CPU, GPU, AppleSiliconChip
@@ -179,6 +179,7 @@ def __init__(
179179
force_cpu_power: Optional[int] = _sentinel,
180180
force_ram_power: Optional[int] = _sentinel,
181181
pue: Optional[int] = _sentinel,
182+
wue: Optional[bool] = _sentinel,
182183
force_mode_cpu_load: Optional[bool] = _sentinel,
183184
allow_multiple_runs: Optional[bool] = _sentinel,
184185
):
@@ -238,6 +239,7 @@ def __init__(
238239
:param pue: PUE (Power Usage Effectiveness) of the datacenter.
239240
:param force_mode_cpu_load: Force the addition of a CPU in MODE_CPU_LOAD
240241
:param allow_multiple_runs: Allow multiple instances of codecarbon running in parallel. Defaults to False.
242+
:param wue: WUE (Water Usage Effectiveness) of the datacenter, L/kWh.
241243
"""
242244

243245
# logger.info("base tracker init")
@@ -287,6 +289,7 @@ def __init__(
287289
self._set_from_conf(force_cpu_power, "force_cpu_power", None, float)
288290
self._set_from_conf(force_ram_power, "force_ram_power", None, float)
289291
self._set_from_conf(pue, "pue", 1.0, float)
292+
self._set_from_conf(wue, "wue", 0, float)
290293
self._set_from_conf(force_mode_cpu_load, "force_mode_cpu_load", False, bool)
291294
self._set_from_conf(
292295
experiment_id, "experiment_id", "5b0fa12a-3dd7-45bb-9766-cc326314d9f1"
@@ -299,6 +302,7 @@ def __init__(
299302
self._start_time: Optional[float] = None
300303
self._last_measured_time: float = time.perf_counter()
301304
self._total_energy: Energy = Energy.from_energy(kWh=0)
305+
self._total_water: Water = Water.from_litres(litres=0)
302306
self._total_cpu_energy: Energy = Energy.from_energy(kWh=0)
303307
self._total_gpu_energy: Energy = Energy.from_energy(kWh=0)
304308
self._total_ram_energy: Energy = Energy.from_energy(kWh=0)
@@ -703,6 +707,7 @@ def _prepare_emissions_data(self) -> EmissionsData:
703707
gpu_energy=self._total_gpu_energy.kWh,
704708
ram_energy=self._total_ram_energy.kWh,
705709
energy_consumed=self._total_energy.kWh,
710+
water_consumed=self._total_water.litres,
706711
country_name=country_name,
707712
country_iso_code=country_iso_code,
708713
region=region,
@@ -721,6 +726,7 @@ def _prepare_emissions_data(self) -> EmissionsData:
721726
ram_total_size=self._conf.get("ram_total_size"),
722727
tracking_mode=self._conf.get("tracking_mode"),
723728
pue=self._pue,
729+
wue=self._wue,
724730
)
725731
logger.debug(total_emissions)
726732
return total_emissions
@@ -778,7 +784,9 @@ def _do_measurements(self) -> None:
778784
) = hardware.measure_power_and_energy(last_duration=last_duration)
779785
# Apply the PUE of the datacenter to the consumed energy
780786
energy *= self._pue
787+
water = Water.from_litres(litres=self._wue * energy.kWh)
781788
self._total_energy += energy
789+
self._total_water += water
782790
if isinstance(hardware, CPU):
783791
self._total_cpu_energy += energy
784792
self._cpu_power = power
@@ -825,7 +833,7 @@ def _do_measurements(self) -> None:
825833
f"Done measure for {hardware.__class__.__name__} - measurement time: {h_time:,.4f} s - last call {last_duration:,.2f} s"
826834
)
827835
logger.info(
828-
f"{self._total_energy.kWh:.6f} kWh of electricity used since the beginning."
836+
f"{self._total_energy.kWh:.6f} kWh of electricity and {self._total_water.litres:.6f} L of water were used since the beginning."
829837
)
830838

831839
def _measure_power_and_energy(self) -> None:
@@ -843,7 +851,11 @@ def _measure_power_and_energy(self) -> None:
843851
raise e
844852

845853
warning_duration = self._measure_power_secs * 3
846-
if last_duration > warning_duration and not self._scheduler._stopped:
854+
if (
855+
last_duration > warning_duration
856+
and self._scheduler
857+
and not self._scheduler._stopped
858+
):
847859
warn_msg = (
848860
"Background scheduler didn't run for a long period"
849861
+ " (%ds), results might be inaccurate"
@@ -1066,6 +1078,7 @@ def track_emissions(
10661078
force_cpu_power: Optional[int] = _sentinel,
10671079
force_ram_power: Optional[int] = _sentinel,
10681080
pue: Optional[int] = _sentinel,
1081+
wue: Optional[float] = _sentinel,
10691082
allow_multiple_runs: Optional[bool] = _sentinel,
10701083
):
10711084
"""
@@ -1141,6 +1154,7 @@ def track_emissions(
11411154
:param force_cpu_power: cpu power to be used instead of automatic detection.
11421155
:param force_ram_power: ram power to be used instead of automatic detection.
11431156
:param pue: PUE (Power Usage Effectiveness) of the datacenter.
1157+
:param wue: WUE (Water Usage Effectiveness) of the datacenter, L/kWh.
11441158
:param allow_multiple_runs: Prevent multiple instances of codecarbon running. Defaults to False.
11451159
11461160
:return: The decorated function
@@ -1181,6 +1195,7 @@ def wrapped_fn(*args, **kwargs):
11811195
force_cpu_power=force_cpu_power,
11821196
force_ram_power=force_ram_power,
11831197
pue=pue,
1198+
wue=wue,
11841199
allow_multiple_runs=allow_multiple_runs,
11851200
)
11861201
else:
@@ -1212,6 +1227,7 @@ def wrapped_fn(*args, **kwargs):
12121227
force_cpu_power=force_cpu_power,
12131228
force_ram_power=force_ram_power,
12141229
pue=pue,
1230+
wue=wue,
12151231
allow_multiple_runs=allow_multiple_runs,
12161232
)
12171233
tracker.start()

codecarbon/external/task.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ def out(self):
3434
gpu_energy=self.emissions_data.gpu_energy,
3535
ram_energy=self.emissions_data.ram_energy,
3636
energy_consumed=self.emissions_data.energy_consumed,
37+
water_consumed=self.emissions_data.water_consumed,
3738
country_name=self.emissions_data.country_name,
3839
country_iso_code=self.emissions_data.country_iso_code,
3940
region=self.emissions_data.region,

codecarbon/output_methods/emissions_data.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ class EmissionsData:
2323
gpu_energy: float
2424
ram_energy: float
2525
energy_consumed: float
26+
water_consumed: float
2627
country_name: str
2728
country_iso_code: str
2829
region: str
@@ -41,6 +42,7 @@ class EmissionsData:
4142
tracking_mode: str
4243
on_cloud: str = "N"
4344
pue: float = 1
45+
wue: float = 0
4446

4547
@property
4648
def values(self) -> OrderedDict:
@@ -55,6 +57,7 @@ def compute_delta_emission(self, previous_emission):
5557
self.gpu_energy -= previous_emission.gpu_energy
5658
self.ram_energy -= previous_emission.ram_energy
5759
self.energy_consumed -= previous_emission.energy_consumed
60+
self.water_consumed -= previous_emission.water_consumed
5861
if delta_duration > 0:
5962
# emissions_rate in g/s : delta_emissions in kg.CO2 / delta_duration in s
6063
self.emissions_rate = delta_emissions / delta_duration
@@ -81,6 +84,7 @@ class TaskEmissionsData:
8184
gpu_energy: float
8285
ram_energy: float
8386
energy_consumed: float
87+
water_consumed: float
8488
country_name: str
8589
country_iso_code: str
8690
region: str

docs/_sources/parameters.rst.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,11 @@ Input Parameters
3636
* - pue
3737
- | PUE (Power Usage Effectiveness) of the data center
3838
| where the experiment is being run.
39+
* - wue
40+
- | WUE (Water Usage Effectiveness) of the data center
41+
| where the experiment is being run.
42+
| Units of *L/kWh* - how many litres of water are consumed per kilowatt-hour
43+
| of electricity consumed.
3944
* - force_cpu_power
4045
- | Force the CPU max power consumption in watts,
4146
| use this if you know the TDP of your machine.

docs/edit/parameters.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,11 @@ Input Parameters
3636
* - pue
3737
- | PUE (Power Usage Effectiveness) of the data center
3838
| where the experiment is being run.
39+
* - wue
40+
- | WUE (Water Usage Effectiveness) of the data center
41+
| where the experiment is being run.
42+
| Units of *L/kWh* - how many litres of water are consumed per kilowatt-hour
43+
| of electricity consumed.
3944
* - force_cpu_power
4045
- | Force the CPU max power consumption in watts,
4146
| use this if you know the TDP of your machine.

docs/objects.inv

-242 Bytes
Binary file not shown.

docs/parameters.html

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -158,15 +158,24 @@ <h2>Input Parameters<a class="headerlink" href="#input-parameters" title="Link t
158158
</div>
159159
</td>
160160
</tr>
161-
<tr class="row-even"><td><p>force_cpu_power</p></td>
161+
<tr class="row-even"><td><p>wue</p></td>
162+
<td><div class="line-block">
163+
<div class="line">WUE (Water Usage Effectiveness) of the data center</div>
164+
<div class="line">where the experiment is being run.</div>
165+
<div class="line">Units of <em>L/kWh</em> - how many litres of water are consumed per kilowatt-hour</div>
166+
<div class="line">of electricity consumed.</div>
167+
</div>
168+
</td>
169+
</tr>
170+
<tr class="row-odd"><td><p>force_cpu_power</p></td>
162171
<td><div class="line-block">
163172
<div class="line">Force the CPU max power consumption in watts,</div>
164173
<div class="line">use this if you know the TDP of your machine.</div>
165174
<div class="line"><em>(POWER_CONSTANT x CONSUMPTION_PERCENTAGE)</em></div>
166175
</div>
167176
</td>
168177
</tr>
169-
<tr class="row-odd"><td><p>force_ram_power</p></td>
178+
<tr class="row-even"><td><p>force_ram_power</p></td>
170179
<td><div class="line-block">
171180
<div class="line">Force the RAM power consumption in watts,</div>
172181
<div class="line">use this if you know the power consumption of your RAM.</div>
@@ -176,7 +185,7 @@ <h2>Input Parameters<a class="headerlink" href="#input-parameters" title="Link t
176185
</div>
177186
</td>
178187
</tr>
179-
<tr class="row-even"><td><p>allow_multiple_runs</p></td>
188+
<tr class="row-odd"><td><p>allow_multiple_runs</p></td>
180189
<td><div class="line-block">
181190
<div class="line">Boolean variable indicating if multiple instance of CodeCarbon</div>
182191
<div class="line">on the same machine is allowed,</div>

docs/searchindex.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

examples/wue.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import time
2+
3+
from codecarbon import track_emissions
4+
5+
6+
@track_emissions(
7+
measure_power_secs=3,
8+
wue=1.1,
9+
)
10+
def train_model():
11+
"""
12+
This function will do nothing.
13+
"""
14+
print("30 seconds before ending script...")
15+
time.sleep(30)
16+
17+
18+
if __name__ == "__main__":
19+
model = train_model()

0 commit comments

Comments
 (0)