Skip to content

Commit

Permalink
calculate operational and embodied emissions per segment
Browse files Browse the repository at this point in the history
  • Loading branch information
fershad committed May 14, 2024
1 parent 7bb79bd commit a525f33
Show file tree
Hide file tree
Showing 3 changed files with 238 additions and 0 deletions.
11 changes: 11 additions & 0 deletions src/constants/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,16 @@ const FIRST_TIME_VIEWING_PERCENTAGE = 0.75;
const RETURNING_VISITOR_PERCENTAGE = 0.25;
const PERCENTAGE_OF_DATA_LOADED_ON_SUBSEQUENT_LOAD = 0.02;

const SWDV4 = {
OPERATIONAL_KWH_PER_GB_DATACENTER: 0.055,
OPERATIONAL_KWH_PER_GB_NETWORK: 0.059,
OPERATIONAL_KWH_PER_GB_DEVICE: 0.08,
EMBODIED_KWH_PER_GB_DATACENTER: 0.012,
EMBODIED_KWH_PER_GB_NETWORK: 0.013,
EMBODIED_KWH_PER_GB_DEVICE: 0.081,
GLOBAL_GRID_INTENSITY: 494,
};

export {
fileSize,
KWH_PER_GB,
Expand All @@ -36,4 +46,5 @@ export {
FIRST_TIME_VIEWING_PERCENTAGE,
RETURNING_VISITOR_PERCENTAGE,
PERCENTAGE_OF_DATA_LOADED_ON_SUBSEQUENT_LOAD,
SWDV4,
};
138 changes: 138 additions & 0 deletions src/sustainable-web-design-v4.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
"use strict";

/**
* Sustainable Web Design version 4
*
* Updated calculations and figures from
* https://sustainablewebdesign.org/estimating-digital-emissions/
*
*/

import { fileSize, SWDV4 } from "./constants/index.js";
import { formatNumber } from "./helpers/index.js";

const {
OPERATIONAL_KWH_PER_GB_DATACENTER,
OPERATIONAL_KWH_PER_GB_NETWORK,
OPERATIONAL_KWH_PER_GB_DEVICE,
EMBODIED_KWH_PER_GB_DATACENTER,
EMBODIED_KWH_PER_GB_NETWORK,
EMBODIED_KWH_PER_GB_DEVICE,
GLOBAL_GRID_INTENSITY,
} = SWDV4;

class SustainableWebDesign {
constructor(options) {
this.options = options;
}

/**
* Calculate the operational energy of data transfer for each system segment
*
* @param {number} bytes
* @returns {object}
*/
operationalEnergyPerSegment(bytes) {
const transferedBytesToGb = bytes / fileSize.GIGABYTE;
const dataCenter = transferedBytesToGb * OPERATIONAL_KWH_PER_GB_DATACENTER;
const network = transferedBytesToGb * OPERATIONAL_KWH_PER_GB_NETWORK;
const device = transferedBytesToGb * OPERATIONAL_KWH_PER_GB_DEVICE;

return {
dataCenter,
network,
device,
};
}

/**
* Calculate the operational emissions of data transfer for each system segment
*
* @param {number} bytes
* @param {object} options
* @returns {object}
*/
operationalEmissions(bytes, options = {}) {
const { dataCenter, network, device } =
this.operationalEnergyPerSegment(bytes);

let dataCenterGridIntensity = GLOBAL_GRID_INTENSITY;
let networkGridIntensity = GLOBAL_GRID_INTENSITY;
let deviceGridIntensity = GLOBAL_GRID_INTENSITY;

if (options?.gridIntensity) {
const { device, network, dataCenter } = options.gridIntensity;

if (device?.value || device?.value === 0) {
deviceGridIntensity = device.value;
}

if (network?.value || network?.value === 0) {
networkGridIntensity = network.value;
}

if (dataCenter?.value || dataCenter?.value === 0) {
dataCenterGridIntensity = dataCenter.value;
}
}

const dataCenterEmissions = dataCenter * dataCenterGridIntensity;
const networkEmissions = network * networkGridIntensity;
const deviceEmissions = device * deviceGridIntensity;

return {
dataCenter: dataCenterEmissions,
network: networkEmissions,
device: deviceEmissions,
};
}

/**
* Calculate the embodied energy of data transfer for each system segment
*
* @param {number} bytes
* @returns {object}
*/
embodiedEnergyPerSegment(bytes) {
const transferedBytesToGb = bytes / fileSize.GIGABYTE;
const dataCenter = transferedBytesToGb * EMBODIED_KWH_PER_GB_DATACENTER;
const network = transferedBytesToGb * EMBODIED_KWH_PER_GB_NETWORK;
const device = transferedBytesToGb * EMBODIED_KWH_PER_GB_DEVICE;

return {
dataCenter,
network,
device,
};
}

/**
* Calculate the embodied emissions of data transfer for each system segment
*
* @param {number} bytes
* @returns {object}
*/
embodiedEmissions(bytes) {
const { dataCenter, network, device } =
this.embodiedEnergyPerSegment(bytes);

const dataCenterGridIntensity = GLOBAL_GRID_INTENSITY;
const networkGridIntensity = GLOBAL_GRID_INTENSITY;
const deviceGridIntensity = GLOBAL_GRID_INTENSITY;

// NOTE: Per the guidance in the SWDM v4, the grid intensity values for embodied emissions are fixed to the global grid intensity.

const dataCenterEmissions = dataCenter * dataCenterGridIntensity;
const networkEmissions = network * networkGridIntensity;
const deviceEmissions = device * deviceGridIntensity;

return {
dataCenter: dataCenterEmissions,
network: networkEmissions,
device: deviceEmissions,
};
}
}

export { SustainableWebDesign };
export default SustainableWebDesign;
89 changes: 89 additions & 0 deletions src/sustainable-web-design-v4.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import SustainableWebDesign from "./sustainable-web-design-v4.js";
import { MILLION, SWDV4 } from "./constants/test-constants.js";

describe("sustainable web design model version 4", () => {
const swd = new SustainableWebDesign();

describe("operational emissions", () => {
it("returns the expected emissions for 1GB data transfer", () => {
const result = swd.operationalEmissions(1000000000);
expect(result).toEqual(
expect.objectContaining({
dataCenter: expect.any(Number),
network: expect.any(Number),
device: expect.any(Number),
})
);
expect(result.dataCenter).toBeCloseTo(27.17, 3);
expect(result.network).toBeCloseTo(29.146, 3);
expect(result.device).toBeCloseTo(39.52, 3);
});

it("returns the expected emissions for 0bytes data transfer", () => {
const result = swd.operationalEmissions(0);
expect(result).toEqual(
expect.objectContaining({
dataCenter: expect.any(Number),
network: expect.any(Number),
device: expect.any(Number),
})
);
expect(result.dataCenter).toBeCloseTo(0, 3);
expect(result.network).toBeCloseTo(0, 3);
expect(result.device).toBeCloseTo(0, 3);
});

if (
("returns the expected emissions for 1GB data transfer with custom grid intensities",
() => {
const result = swd.operationalEmissions(1000000000, {
gridIntensity: {
dataCenter: { value: 100 },
network: { value: 200 },
device: { value: 300 },
},
});
expect(result).toEqual(
expect.objectContaining({
dataCenter: expect.any(Number),
network: expect.any(Number),
device: expect.any(Number),
})
);
expect(result.dataCenter).toBeCloseTo(5.5, 3);
expect(result.network).toBeCloseTo(11.8, 3);
expect(result.device).toBeCloseTo(24, 3);
})
);
});

describe("embodied emissions", () => {
it("returns the expected emissions for 1GB data transfer", () => {
const result = swd.embodiedEmissions(1000000000);
expect(result).toEqual(
expect.objectContaining({
dataCenter: expect.any(Number),
network: expect.any(Number),
device: expect.any(Number),
})
);
expect(result.dataCenter).toBeCloseTo(5.928, 3);
expect(result.network).toBeCloseTo(6.422, 3);
expect(result.device).toBeCloseTo(40.014, 3);
});

it("returns the expected emissions for 0bytes data transfer", () => {
const result = swd.embodiedEmissions(0);
expect(result).toEqual(
expect.objectContaining({
dataCenter: expect.any(Number),
network: expect.any(Number),
device: expect.any(Number),
})
);
expect(result.dataCenter).toBeCloseTo(0, 3);
expect(result.network).toBeCloseTo(0, 3);
expect(result.device).toBeCloseTo(0, 3);
});
});
});

0 comments on commit a525f33

Please sign in to comment.