@tgwf/co2
Version:
Work out the co2 of your digital services
260 lines (259 loc) • 11.5 kB
JavaScript
var __defProp = Object.defineProperty;
var __defProps = Object.defineProperties;
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __propIsEnum = Object.prototype.propertyIsEnumerable;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __spreadValues = (a, b) => {
for (var prop in b || (b = {}))
if (__hasOwnProp.call(b, prop))
__defNormalProp(a, prop, b[prop]);
if (__getOwnPropSymbols)
for (var prop of __getOwnPropSymbols(b)) {
if (__propIsEnum.call(b, prop))
__defNormalProp(a, prop, b[prop]);
}
return a;
};
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
import {
fileSize,
KWH_PER_GB,
END_USER_DEVICE_ENERGY,
NETWORK_ENERGY,
DATACENTER_ENERGY,
PRODUCTION_ENERGY,
GLOBAL_GRID_INTENSITY,
RENEWABLES_GRID_INTENSITY,
FIRST_TIME_VIEWING_PERCENTAGE,
RETURNING_VISITOR_PERCENTAGE,
PERCENTAGE_OF_DATA_LOADED_ON_SUBSEQUENT_LOAD
} from "./constants/index.js";
import { formatNumber } from "./helpers/index.js";
class SustainableWebDesign {
constructor(options) {
this.options = options;
}
/**
* Accept a figure for bytes transferred and return an object representing
* the share of the total enrgy use of the entire system, broken down
* by each corresponding system component
*
* @param {number} bytes - the data transferred in bytes
* @return {object} Object containing the energy in kilowatt hours, keyed by system component
*/
energyPerByteByComponent(bytes) {
const transferedBytesToGb = bytes / fileSize.GIGABYTE;
const energyUsage = transferedBytesToGb * KWH_PER_GB;
return {
consumerDeviceEnergy: energyUsage * END_USER_DEVICE_ENERGY,
networkEnergy: energyUsage * NETWORK_ENERGY,
productionEnergy: energyUsage * PRODUCTION_ENERGY,
dataCenterEnergy: energyUsage * DATACENTER_ENERGY
};
}
/**
* Accept an object keys by the different system components, and
* return an object with the co2 figures key by the each component
*
* @param {object} energyByComponent - energy grouped by the four system components
* @param {number} [carbonIntensity] - carbon intensity to apply to the datacentre values
* @return {number} the total number in grams of CO2 equivalent emissions
*/
co2byComponent(energyByComponent, carbonIntensity = GLOBAL_GRID_INTENSITY, options = {}) {
let deviceCarbonIntensity = GLOBAL_GRID_INTENSITY;
let networkCarbonIntensity = GLOBAL_GRID_INTENSITY;
let dataCenterCarbonIntensity = GLOBAL_GRID_INTENSITY;
let globalEmissions = GLOBAL_GRID_INTENSITY;
if (options == null ? void 0 : options.gridIntensity) {
const { device, network, dataCenter } = options.gridIntensity;
if ((device == null ? void 0 : device.value) || (device == null ? void 0 : device.value) === 0) {
deviceCarbonIntensity = device.value;
}
if ((network == null ? void 0 : network.value) || (network == null ? void 0 : network.value) === 0) {
networkCarbonIntensity = network.value;
}
if ((dataCenter == null ? void 0 : dataCenter.value) || (dataCenter == null ? void 0 : dataCenter.value) === 0) {
dataCenterCarbonIntensity = dataCenter.value;
}
}
if (carbonIntensity === true) {
dataCenterCarbonIntensity = RENEWABLES_GRID_INTENSITY;
}
const returnCO2ByComponent = {};
for (const [key, value] of Object.entries(energyByComponent)) {
if (key.startsWith("dataCenterEnergy")) {
returnCO2ByComponent[key.replace("Energy", "CO2")] = value * dataCenterCarbonIntensity;
} else if (key.startsWith("consumerDeviceEnergy")) {
returnCO2ByComponent[key.replace("Energy", "CO2")] = value * deviceCarbonIntensity;
} else if (key.startsWith("networkEnergy")) {
returnCO2ByComponent[key.replace("Energy", "CO2")] = value * networkCarbonIntensity;
} else {
returnCO2ByComponent[key.replace("Energy", "CO2")] = value * globalEmissions;
}
}
return returnCO2ByComponent;
}
/**
* Accept a figure for bytes transferred and return a single figure for CO2
* emissions. Where information exists about the origin data is being
* fetched from, a different carbon intensity figure
* is applied for the data centre share of the carbon intensity.
*
* @param {number} bytes - the data transferred in bytes
* @param {boolean} carbonIntensity - a boolean indicating whether the data center is green or not
* @param {boolean} segmentResults - a boolean indicating whether to return the results broken down by component
* @param {object} options - an object containing the grid intensity and first/return visitor values
* @return {number|object} the total number in grams of CO2 equivalent emissions, or an object containing the breakdown by component
*/
perByte(bytes, carbonIntensity = false, segmentResults = false, options = {}) {
if (bytes < 1) {
bytes = 0;
}
const energyBycomponent = this.energyPerByteByComponent(bytes, options);
if (typeof carbonIntensity !== "boolean") {
throw new Error(
"perByte expects a boolean for the carbon intensity value. Received: ".concat(carbonIntensity)
);
}
const co2ValuesbyComponent = this.co2byComponent(
energyBycomponent,
carbonIntensity,
options
);
const co2Values = Object.values(co2ValuesbyComponent);
const co2ValuesSum = co2Values.reduce(
(prevValue, currentValue) => prevValue + currentValue
);
if (segmentResults) {
return __spreadProps(__spreadValues({}, co2ValuesbyComponent), { total: co2ValuesSum });
}
return co2ValuesSum;
}
/**
* Accept a figure for bytes transferred and return a single figure for CO2
* emissions. This method applies caching assumptions from the original Sustainable Web Design model.
*
* @param {number} bytes - the data transferred in bytes
* @param {boolean} carbonIntensity - a boolean indicating whether the data center is green or not
* @param {boolean} segmentResults - a boolean indicating whether to return the results broken down by component
* @param {object} options - an object containing the grid intensity and first/return visitor values
* @return {number|object} the total number in grams of CO2 equivalent emissions, or an object containing the breakdown by component
*/
perVisit(bytes, carbonIntensity = false, segmentResults = false, options = {}) {
const energyBycomponent = this.energyPerVisitByComponent(bytes, options);
if (typeof carbonIntensity !== "boolean") {
throw new Error(
"perVisit expects a boolean for the carbon intensity value. Received: ".concat(carbonIntensity)
);
}
const co2ValuesbyComponent = this.co2byComponent(
energyBycomponent,
carbonIntensity,
options
);
const co2Values = Object.values(co2ValuesbyComponent);
const co2ValuesSum = co2Values.reduce(
(prevValue, currentValue) => prevValue + currentValue
);
if (segmentResults) {
return __spreadProps(__spreadValues({}, co2ValuesbyComponent), { total: co2ValuesSum });
}
return co2ValuesSum;
}
/**
* Accept a figure for bytes transferred and return the number of kilowatt hours used
* by the total system for this data transfer
*
* @param {number} bytes
* @return {number} the number of kilowatt hours used
*/
energyPerByte(bytes) {
const energyByComponent = this.energyPerByteByComponent(bytes);
const energyValues = Object.values(energyByComponent);
return energyValues.reduce(
(prevValue, currentValue) => prevValue + currentValue
);
}
/**
* Accept a figure for bytes transferred, and return an object containing figures
* per system component, with the caching assumptions applied. This tries to account
* for webpages being loaded from a cache by browsers, so if you had a thousand page views,
* and tried to work out the energy per visit, the numbers would reflect the reduced amounts
* of transfer.
*
* @param {number} bytes - the data transferred in bytes for loading a webpage
* @param {number} firstView - what percentage of visits are loading this page for the first time
* @param {number} returnView - what percentage of visits are loading this page for subsequent times
* @param {number} dataReloadRatio - what percentage of a page is reloaded on each subsequent page view
*
* @return {object} Object containing the energy in kilowatt hours, keyed by system component
*/
energyPerVisitByComponent(bytes, options = {}, firstView = FIRST_TIME_VIEWING_PERCENTAGE, returnView = RETURNING_VISITOR_PERCENTAGE, dataReloadRatio = PERCENTAGE_OF_DATA_LOADED_ON_SUBSEQUENT_LOAD) {
if (options.dataReloadRatio || options.dataReloadRatio === 0) {
dataReloadRatio = options.dataReloadRatio;
}
if (options.firstVisitPercentage || options.firstVisitPercentage === 0) {
firstView = options.firstVisitPercentage;
}
if (options.returnVisitPercentage || options.returnVisitPercentage === 0) {
returnView = options.returnVisitPercentage;
}
const energyBycomponent = this.energyPerByteByComponent(bytes);
const cacheAdjustedSegmentEnergy = {};
const energyValues = Object.values(energyBycomponent);
for (const [key, value] of Object.entries(energyBycomponent)) {
cacheAdjustedSegmentEnergy["".concat(key, " - first")] = value * firstView;
cacheAdjustedSegmentEnergy["".concat(key, " - subsequent")] = value * returnView * dataReloadRatio;
}
return cacheAdjustedSegmentEnergy;
}
/**
* Accept a figure for bytes, and return the total figure for energy per visit
* using the default caching assumptions for loading a single website
*
* @param {number} bytes
* @return {number} the total energy use for the visit, after applying the caching assumptions
*/
energyPerVisit(bytes) {
let firstVisits = 0;
let subsequentVisits = 0;
const energyBycomponent = Object.entries(
this.energyPerVisitByComponent(bytes)
);
for (const [key, val] of energyBycomponent) {
if (key.indexOf("first") > 0) {
firstVisits += val;
}
}
for (const [key, val] of energyBycomponent) {
if (key.indexOf("subsequent") > 0) {
subsequentVisits += val;
}
}
return firstVisits + subsequentVisits;
}
emissionsPerVisitInGrams(energyPerVisit, carbonintensity = GLOBAL_GRID_INTENSITY) {
return formatNumber(energyPerVisit * carbonintensity);
}
annualEnergyInKwh(energyPerVisit, monthlyVisitors = 1e3) {
return energyPerVisit * monthlyVisitors * 12;
}
annualEmissionsInGrams(co2grams, monthlyVisitors = 1e3) {
return co2grams * monthlyVisitors * 12;
}
annualSegmentEnergy(annualEnergy) {
return {
consumerDeviceEnergy: formatNumber(annualEnergy * END_USER_DEVICE_ENERGY),
networkEnergy: formatNumber(annualEnergy * NETWORK_ENERGY),
dataCenterEnergy: formatNumber(annualEnergy * DATACENTER_ENERGY),
productionEnergy: formatNumber(annualEnergy * PRODUCTION_ENERGY)
};
}
}
var sustainable_web_design_default = SustainableWebDesign;
export {
SustainableWebDesign,
sustainable_web_design_default as default
};