@tgwf/co2
Version:
Work out the co2 of your digital services
250 lines (249 loc) • 11.7 kB
JavaScript
;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var sustainable_web_design_exports = {};
__export(sustainable_web_design_exports, {
SustainableWebDesign: () => SustainableWebDesign,
default: () => sustainable_web_design_default
});
module.exports = __toCommonJS(sustainable_web_design_exports);
var import_constants = require("./constants/index.js");
var import_helpers = require("./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 / import_constants.fileSize.GIGABYTE;
const energyUsage = transferedBytesToGb * import_constants.KWH_PER_GB;
return {
consumerDeviceEnergy: energyUsage * import_constants.END_USER_DEVICE_ENERGY,
networkEnergy: energyUsage * import_constants.NETWORK_ENERGY,
productionEnergy: energyUsage * import_constants.PRODUCTION_ENERGY,
dataCenterEnergy: energyUsage * import_constants.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 = import_constants.GLOBAL_GRID_INTENSITY, options = {}) {
let deviceCarbonIntensity = import_constants.GLOBAL_GRID_INTENSITY;
let networkCarbonIntensity = import_constants.GLOBAL_GRID_INTENSITY;
let dataCenterCarbonIntensity = import_constants.GLOBAL_GRID_INTENSITY;
let globalEmissions = import_constants.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 = import_constants.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: ${carbonIntensity}`
);
}
const co2ValuesbyComponent = this.co2byComponent(
energyBycomponent,
carbonIntensity,
options
);
const co2Values = Object.values(co2ValuesbyComponent);
const co2ValuesSum = co2Values.reduce(
(prevValue, currentValue) => prevValue + currentValue
);
if (segmentResults) {
return { ...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: ${carbonIntensity}`
);
}
const co2ValuesbyComponent = this.co2byComponent(
energyBycomponent,
carbonIntensity,
options
);
const co2Values = Object.values(co2ValuesbyComponent);
const co2ValuesSum = co2Values.reduce(
(prevValue, currentValue) => prevValue + currentValue
);
if (segmentResults) {
return { ...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 = import_constants.FIRST_TIME_VIEWING_PERCENTAGE, returnView = import_constants.RETURNING_VISITOR_PERCENTAGE, dataReloadRatio = import_constants.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[`${key} - first`] = value * firstView;
cacheAdjustedSegmentEnergy[`${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 = import_constants.GLOBAL_GRID_INTENSITY) {
return (0, import_helpers.formatNumber)(energyPerVisit * carbonintensity);
}
annualEnergyInKwh(energyPerVisit, monthlyVisitors = 1e3) {
return energyPerVisit * monthlyVisitors * 12;
}
annualEmissionsInGrams(co2grams, monthlyVisitors = 1e3) {
return co2grams * monthlyVisitors * 12;
}
annualSegmentEnergy(annualEnergy) {
return {
consumerDeviceEnergy: (0, import_helpers.formatNumber)(annualEnergy * import_constants.END_USER_DEVICE_ENERGY),
networkEnergy: (0, import_helpers.formatNumber)(annualEnergy * import_constants.NETWORK_ENERGY),
dataCenterEnergy: (0, import_helpers.formatNumber)(annualEnergy * import_constants.DATACENTER_ENERGY),
productionEnergy: (0, import_helpers.formatNumber)(annualEnergy * import_constants.PRODUCTION_ENERGY)
};
}
}
var sustainable_web_design_default = SustainableWebDesign;
//# sourceMappingURL=sustainable-web-design.js.map