carbond_lib/
lib.rs

1// SPDX-FileCopyrightText: 2024 Andreas Schmidt <andreas.schmidt@cs.uni-saarland.de>
2//
3// SPDX-License-Identifier: Apache-2.0 OR MIT
4
5pub mod constants;
6pub mod data {
7    pub mod carbon_tracking;
8    pub mod cpu_tracking;
9}
10mod fs;
11pub mod macros;
12pub mod metrics;
13
14use std::path::PathBuf;
15
16pub use core_affinity;
17use log::debug;
18use metrics::{
19    carbon_equivalent::CarbonEquivalent, cpu::CpuCycleIntensity, flag::Flag, joule::Joule,
20    CarbonIntensity, Efficiency, Metric, MetricError,
21};
22pub use rand;
23use uom::si::f64::{Energy, Mass, MassPerEnergy};
24
25struct BatteryInfo {
26    name: String,
27    carbon_path: PathBuf,
28    energy_path: PathBuf,
29}
30
31/// Loads the carbon intensity of the system, which comes either from the grid converted by the power supply or from the battery.
32/// Since it is loaded from the file system API, this method can return an error.
33pub async fn try_load_system_intensity() -> Result<CarbonIntensity, MetricError> {
34    CarbonIntensity::try_read_from_fs(constants::SYSTEM_INTENSITY_PATH).await
35}
36/// Loads the carbon intensity of the system synchronously, which comes either from the grid converted by the power supply or from the battery.
37/// Since it is loaded from the file system API, this method can return an error.
38pub fn try_load_system_intensity_sync() -> Result<CarbonIntensity, MetricError> {
39    CarbonIntensity::try_read_from_fs_sync(constants::SYSTEM_INTENSITY_PATH)
40}
41
42/// Loads the carbon intensity of the grid, provided by external data providers.
43/// Since it is loaded from the file system API, this method can return an error.
44pub async fn try_load_grid_intensity() -> Result<CarbonIntensity, MetricError> {
45    CarbonIntensity::try_read_from_fs(constants::GRID_INTENSITY_PATH).await
46}
47/// Loads the carbon intensity of the grid synchronously, provided by external data providers.
48/// Since it is loaded from the file system API, this method can return an error.
49pub fn try_load_grid_intensity_sync() -> Result<CarbonIntensity, MetricError> {
50    CarbonIntensity::try_read_from_fs_sync(constants::GRID_INTENSITY_PATH)
51}
52
53/// Loads the current efficiency of the Power Supply Unit.
54/// Since it is loaded from the file system API, this method can return an error.
55pub async fn try_load_psu_efficiency() -> Result<Efficiency, MetricError> {
56    Efficiency::try_read_from_fs(constants::PSU_EFFICIENCY_PATH).await
57}
58/// Loads the current efficiency of the Power Supply Unit synchronously.
59/// Since it is loaded from the file system API, this method can return an error.
60pub fn try_load_psu_efficiency_sync() -> Result<Efficiency, MetricError> {
61    Efficiency::try_read_from_fs_sync(constants::PSU_EFFICIENCY_PATH)
62}
63
64/// Loads the current carbon intensity from the file system and calculates the carbon emission with a given energy.
65pub async fn try_calculate_carbon_emission(energy: Energy) -> Result<Mass, MetricError> {
66    let carbon_intensity: MassPerEnergy = try_load_system_intensity().await?.get_value();
67    Ok(carbon_intensity * energy)
68}
69/// Loads the current carbon intensity from the file system synchronously and calculates the carbon emission with a given energy.
70pub fn try_calculate_carbon_emission_sync(energy: Energy) -> Result<Mass, MetricError> {
71    let carbon_intensity: MassPerEnergy = try_load_system_intensity_sync()?.get_value();
72    Ok(carbon_intensity * energy)
73}
74
75/// Loads the embodied intensity of the CPU from the file system.
76pub async fn try_load_cpu_embodied_intensity() -> Result<Mass, MetricError> {
77    Ok(
78        CpuCycleIntensity::try_read_from_fs(constants::CPU_EMBODIED_PATH)
79            .await?
80            .get_value(),
81    )
82}
83/// Loads the embodied intensity of the CPU from the file system synchronously.
84pub fn try_load_cpu_embodied_intensity_sync() -> Result<Mass, MetricError> {
85    Ok(CpuCycleIntensity::try_read_from_fs_sync(constants::CPU_EMBODIED_PATH)?.get_value())
86}
87
88/// Loads the embodied intensity of the CPU from the file system.
89pub async fn try_load_battery_energy_intensity() -> Result<MassPerEnergy, MetricError> {
90    let batteries = load_battery_infos()?;
91
92    if batteries.is_empty() {
93        return Err(MetricError::Other(
94            "battery intensity".to_owned(),
95            "no batteries were found".to_owned(),
96        ));
97    }
98
99    let mut total_energy: Energy = Energy::new::<uom::si::energy::joule>(0.0);
100    let mut total_carbon: Mass = Mass::new::<uom::si::mass::gram>(0.0);
101    for battery in batteries {
102        let energy = Joule::try_read_from_path(&battery.energy_path)
103            .await?
104            .get_value();
105        let carbon = CarbonEquivalent::try_read_from_path(&battery.carbon_path)
106            .await?
107            .get_value();
108        debug!(
109            "Loaded tracker data for `{}`: Energy=`{:?}`, Carbon=`{:?}`.",
110            battery.name, energy, carbon
111        );
112        total_energy += energy;
113        total_carbon += carbon;
114    }
115
116    Ok(total_carbon / total_energy)
117}
118
119/// Loads the embodied intensity of the CPU from the file system synchronously.
120pub fn try_load_battery_energy_intensity_sync() -> Result<MassPerEnergy, MetricError> {
121    let batteries = load_battery_infos()?;
122    let mut total_energy: Energy = Energy::new::<uom::si::energy::joule>(0.0);
123    let mut total_carbon: Mass = Mass::new::<uom::si::mass::gram>(0.0);
124    for battery in batteries {
125        let energy = Joule::try_read_from_path_sync(&battery.energy_path)?.get_value();
126        let carbon = CarbonEquivalent::try_read_from_path_sync(&battery.carbon_path)?.get_value();
127        debug!(
128            "Loaded tracker data for `{}`: Energy=`{:?}`, Carbon=`{:?}`.",
129            battery.name, energy, carbon
130        );
131        total_energy += energy;
132        total_carbon += carbon;
133    }
134
135    Ok(total_carbon / total_energy)
136}
137
138fn load_battery_infos() -> Result<Vec<BatteryInfo>, MetricError> {
139    let battery_tracker_path = PathBuf::from(constants::BATTERY_TRACKER_PATH);
140    if !battery_tracker_path.exists() {
141        return Err(MetricError::Other(
142            "battery intensity".to_owned(),
143            "no battery tracker directory found".to_owned(),
144        ));
145    }
146    let paths = std::fs::read_dir(battery_tracker_path)?;
147    let mut battery_data = vec![];
148    for path in paths {
149        let path = path?.path();
150        let energy_path = path.join("energy");
151        let carbon_path = path.join("carbon");
152        if energy_path.exists()
153            && energy_path.is_file()
154            && carbon_path.exists()
155            && carbon_path.is_file()
156        {
157            battery_data.push(BatteryInfo {
158                name: path
159                    .file_name()
160                    .map_or("Unknown", |f| f.to_str().unwrap_or("Unknown"))
161                    .to_owned(),
162                carbon_path,
163                energy_path,
164            })
165        }
166    }
167    Ok(battery_data)
168}
169
170/// FLAGS //////////////////////////////////////////////////
171pub enum FlagType {
172    OnBattery,
173}
174
175impl FlagType {
176    pub(crate) fn to_path(&self) -> &str {
177        match self {
178            FlagType::OnBattery => constants::FLAG_ONBATTERY_PATH,
179        }
180    }
181}
182
183/// Loads the embodied intensity of the CPU from the file system synchronously.
184pub fn try_load_flag_sync(flag: FlagType) -> Result<bool, MetricError> {
185    let path = flag.to_path();
186    Ok(Flag::try_read_from_fs_sync(path)?.get_value())
187}
188
189/// Loads the embodied intensity of the CPU from the file system.
190pub async fn try_load_flag(flag: FlagType) -> Result<bool, MetricError> {
191    let path = flag.to_path();
192    Ok(Flag::try_read_from_fs(path).await?.get_value())
193}