diff --git a/src/__tests__/if-run/builtins/sci-embodied.test.ts b/src/__tests__/if-run/builtins/sci-embodied.test.ts index a1865f4d..9014a098 100644 --- a/src/__tests__/if-run/builtins/sci-embodied.test.ts +++ b/src/__tests__/if-run/builtins/sci-embodied.test.ts @@ -43,13 +43,13 @@ describe('builtins/sci-embodied:', () => { timestamp: '2021-01-01T00:00:00Z', duration: 3600, vCPUs: 2, - 'embodied-carbon': 5.707762557077626, + 'embodied-carbon': 31.39269406392694, }, { timestamp: '2021-01-01T00:00:00Z', duration: 3600, vCPUs: 4, - 'embodied-carbon': 14.269406392694064, + 'embodied-carbon': 37.10045662100457, }, ]); }); @@ -79,13 +79,13 @@ describe('builtins/sci-embodied:', () => { { timestamp: '2021-01-01T00:00:00Z', duration: 3600, - 'embodied-carbon': 2.497146118721461, + 'embodied-carbon': 28.538812785388128, }, { timestamp: '2021-01-01T00:00:00Z', duration: 3600, 'device/cpu-cores': 2, - 'embodied-carbon': 5.707762557077626, + 'embodied-carbon': 31.39269406392694, }, ]); }); @@ -115,44 +115,13 @@ describe('builtins/sci-embodied:', () => { { timestamp: '2021-01-01T00:00:00Z', duration: 3600, - carbon: 2.497146118721461, + carbon: 28.538812785388128, }, { timestamp: '2021-01-01T00:00:00Z', duration: 3600, 'device/cpu-cores': 2, - carbon: 2.497146118721461, - }, - ]); - }); - - it('returns a result with `vCPUs` in input and `total-vcpus` in config.', async () => { - const sciEmbodied = SciEmbodied( - { - 'total-vcpus': 1, - lifespan: 60 * 60 * 24 * 365 * 4, - }, - parametersMetadata, - {} - ); - const inputs = [ - { - timestamp: '2021-01-01T00:00:00Z', - duration: 60 * 60 * 24 * 30, - vCPUs: 1, - }, - ]; - - const result = await sciEmbodied.execute(inputs); - - expect.assertions(1); - - expect(result).toStrictEqual([ - { - timestamp: '2021-01-01T00:00:00Z', - duration: 60 * 60 * 24 * 30, - vCPUs: 1, - 'embodied-carbon': 1797.945205479452, + carbon: 28.538812785388128, }, ]); }); diff --git a/src/if-run/builtins/sci-embodied/README.md b/src/if-run/builtins/sci-embodied/README.md index c80462db..8363abbe 100644 --- a/src/if-run/builtins/sci-embodied/README.md +++ b/src/if-run/builtins/sci-embodied/README.md @@ -4,6 +4,9 @@ Software systems cause emissions through the hardware that they operate on, both Read more on [embodied carbon](https://github.com/Green-Software-Foundation/sci/blob/main/Software_Carbon_Intensity/Software_Carbon_Intensity_Specification.md#embodied-emissions). +Our plugin follows the Cloud Carbon Footprint methodology for calculating embodied carbon and extends it to scale down the total embodied carbon for a poiece of hardware by the portion of it that should be allocated to a particular application, using a `usage-ratio` and `time`. The `usage-ratio` is a term that can be used to scale by, for example, the storage you actually use on a shared server, rather than the total storage available fir that hardware, or the time you are active compared to the hardware lifespan. + + ## Parameters ### Plugin Configuration @@ -36,19 +39,38 @@ sci-embodied: 'parameter-name-in-the-plugin': 'parameter-name-in-the-input' ``` +### Config + +`baseline-vcpus`: the number of CPUs to use in the baseline server, defaults tothe CCF value of 1, +`baseline-memory`: the amount of memory to use in the baseline server, defaults tothe CCF value of 16, +`baseline-emissions`: the embodied carbon assumed to represent a baseline server, in g +`lifespan`: the lifespan of the device, in seconds. Defaults to 4 years (126144000 seconds), +`time`: the time to consider when scaling the total device embodied carbon, if not given defaults to `duration` +`vcpu-emissions-constant`: emissions for a CPU in gCO2e. Defaults tothe CCF value (100000), +`memory-emissions-constant`: value used in calculating emissions due to memory, defaults to the CCf value of 533/384 +`ssd-emissions-constant`: emissions for a SSD in gCO2e. Defaults tothe CCF value (50000), +`hdd-emissions-constant`: emissions for a CPU in gCO2e. Defaults tothe CCF value (100000), +`gpu-emissions-constant`: emissions for a GPU in gCO2e. Defaults tothe CCF value (150000), +`output-parameter`: name to give the output value, defaults to `embodied-carbon` + +Note that if you do not provide any config at all, we will fallback to defaults for everything, equivalen to setting the baseline server equal to the CCF version, which has 1000000g of embnodied emissions. + ### Inputs -- `device/emissions-embodied`: The total embodied emissions for the component, measured in gCO2e. -- `device/expected-lifespan`: The expected lifespan of the component, in seconds. -- `resources-reserved`: The number of resources reserved for use by the software. -- `resources-total`: The total number of resources available. -- `vcpus-allocated`: The number of virtual CPUs allocated to a particular resource. -- `vcpus-total`: The total number of virtual CPUs available on a particular resource. +- `vCPUs`: number of CPUs available on device +- `memory`: amount of RAM available on device, in GB +- `ssd`: number of SSD drives mounted on device +- `hdd`: number of HDD drives mounted on device +- `gpu`: number of GPUs available on device - `duration`: The length of time the hardware is reserved for use by the software, in seconds. +- `time`: the time to use for scalign the total embodied carbon per timestap, if you do not want to use `duration` +- `usage-ratio`: the ratio by which to scale down the total embodied carbon according to your usage, e.g. for a shared storage server the total storage divided by your actual storage. + +Note that if you do not provide any inputs at all, we fall back to defaults that are equivalent to using the full resources of the baseline server, scaled only by `duration`. ### Outputs -- `carbon-embodied`: The total embodied emissions for the component, measured in gCO2e. +- `carbon-embodied`: The total embodied emissions for the component, measured in gCO2e, per timestep. ## Calculation @@ -57,7 +79,7 @@ The plugin calculates the total embodied carbon emissions using the following st - CPU emissions (`cpuE`) are calculated based on the difference between allocated vCPUs and baseline vCPUs. - Memory emissions (`memoryE`) are calculated based on the difference between allocated memory and baseline memory. - Emissions for HDD, SSD, and GPU are also calculated based on their respective differences from baseline values. - - The total embodied emissions are calculated by summing the baseline emissions with the above components and adjusting for the lifespan and time duration. + - The total embodied emissions are calculated by summing the baseline emissions with the above components scaling by the usage ratio and time. ## Implementation @@ -75,7 +97,6 @@ const results = await sciEmbodied.execute([ ssd: 100, // allocated SSD storage in GB hdd: 1000, // allocated HDD storage in GB gpu: 1, // allocated GPUs - 'total-vcpus': 8, // total available vCPUs }, ]); diff --git a/src/if-run/builtins/sci-embodied/index.ts b/src/if-run/builtins/sci-embodied/index.ts index a8f51106..c162df1a 100644 --- a/src/if-run/builtins/sci-embodied/index.ts +++ b/src/if-run/builtins/sci-embodied/index.ts @@ -50,9 +50,16 @@ export const SciEmbodied = ( unit: 'GPUs', 'aggregation-method': 'copy', }, - 'total-vcpus': { - description: 'total number of CPUs or vCPUs available for a resource', - unit: 'CPUs', + 'usage-ratio': { + description: + 'a scaling factor that can be used to describe the ratio of actual resource usage comapred to real device usage, e.g. 0.25 if you are using 2 out of 8 vCPUs, 0.1 if you are responsible for 1 out of 10 GB of storage, etc', + unit: 'dimensionless', + 'aggregation-method': 'copy', + }, + time: { + description: + 'a time unit to scale the embodied carbon by, in seconds. If not provided,time defaults to the value of the timestep duration.', + unit: 'seconds', 'aggregation-method': 'copy', }, } as ParameterMetadata), @@ -76,7 +83,6 @@ export const SciEmbodied = ( 'baseline-memory': z.number().gte(0).default(16), 'baseline-emissions': z.number().gte(0).default(1000000), lifespan: z.number().gt(0).default(126144000), - time: z.number().gt(0).optional(), 'vcpu-emissions-constant': z.number().gte(0).default(100000), 'memory-emissions-constant': z .number() @@ -107,7 +113,8 @@ export const SciEmbodied = ( ssd: z.number().gte(0).default(0), hdd: z.number().gte(0).default(0), gpu: z.number().gte(0).default(0), - 'total-vcpus': z.number().gte(0).default(8), + 'usage-ratio': z.number().gt(0).default(1), + time: z.number().gt(0).optional(), }); return validate>(schema as ZodType, input); @@ -130,27 +137,31 @@ export const SciEmbodied = ( safeConfig['vcpu-emissions-constant']; const memoryE = (safeInput.memory - safeConfig['baseline-memory']) * - safeConfig['memory-emissions-constant']; - const hddE = safeInput.hdd - safeConfig['hdd-emissions-constant']; - const gpuE = safeInput.gpu - safeConfig['gpu-emissions-constant']; - const ssdE = safeInput.ssd - safeConfig['ssd-emissions-constant']; - const time = safeConfig['time'] || safeInput.duration; + ((safeConfig['memory-emissions-constant'] * + safeConfig['baseline-memory']) / + 16) * + 1000; + // (safeInput.memory - safeConfig['baseline-memory']) * + // safeConfig['memory-emissions-constant']; + const hddE = safeInput.hdd * safeConfig['hdd-emissions-constant']; + const gpuE = safeInput.gpu * safeConfig['gpu-emissions-constant']; + const ssdE = safeInput.ssd * safeConfig['ssd-emissions-constant']; + const time = safeInput['time'] || safeInput.duration; const totalEmbodied = - (safeConfig['baseline-emissions'] + - cpuE + - memoryE + - ssdE + - hddE + - gpuE) * - (safeInput.vCPUs / safeInput['total-vcpus']) * - (time / safeConfig['lifespan']); + safeConfig['baseline-emissions'] + cpuE + memoryE + ssdE + hddE + gpuE; + + const totalEmbodiedScaledByUsage = + totalEmbodied * safeInput['usage-ratio']; + + const totalEmbodiedScaledByUsageAndTime = + totalEmbodiedScaledByUsage * (time / safeConfig['lifespan']); const embodiedCarbonKey = safeConfig['output-parameter'] || 'embodied-carbon'; const result = { ...input, - [embodiedCarbonKey]: totalEmbodied, + [embodiedCarbonKey]: totalEmbodiedScaledByUsageAndTime, }; return mapOutputIfNeeded(result, mapping);