UNPKG

@tgwf/co2

Version:
260 lines (259 loc) 11.5 kB
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 };