Skip to content

Commit

Permalink
Merge pull request #55 from ScottLogic/co2-js-intensity
Browse files Browse the repository at this point in the history
Use CO2.js for Carbon Intensity values
  • Loading branch information
mgriffin-scottlogic authored May 8, 2024
2 parents 6a1c7c6 + d176966 commit 9a9cd2f
Show file tree
Hide file tree
Showing 22 changed files with 324 additions and 227 deletions.
11 changes: 7 additions & 4 deletions docs/components.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ This page details the Angular components that are part of the application and ho

```mermaid
flowchart TB
subgraph components
subgraph Components
CarbonEstimator["`CarbonEstimatorComponent
carbon-estimator`"]
CarbonEstimatorForm["`CarbonEstimatorFormComponent
Expand All @@ -20,10 +20,11 @@ flowchart TB
ExpansionPanel["`ExpansionPanelComponent
expansion-panel`"]
end
subgraph services
subgraph Services
CarbonEstimationService
CarbonIntensityService
end
subgraph pipes
subgraph Pipes
FormatCostRangePipe
end
Expand All @@ -32,6 +33,7 @@ flowchart TB
CarbonEstimatorForm --> HelperInfo & Note
CarbonEstimator & CarbonEstimatorForm ---> CarbonEstimationService
CarbonEstimatorForm & CarbonEstimation --> ExpansionPanel
CarbonEstimationService & Assumptions --> CarbonIntensityService
```

## CarbonEstimatorComponent
Expand All @@ -52,7 +54,8 @@ Visualises the Carbon Estimation result.

## AssumptionsAndLimitationComponent

Provides information on the Assumptions and Limitations of the estimation.
Provides information on the Assumptions and Limitations of the estimation.
Uses the [CarbonIntensityService](services.md#carbonintensityservice) to get the latest carbon intensity figures to display.

## HelperInfoComponent

Expand Down
30 changes: 6 additions & 24 deletions docs/estimation.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,13 @@ classDiagram
class estimate-indirect-emissions{
<<module>>
+estimateIndirectEmissions(input: Cloud) IndirectEstimation
+estimateIndirectEmissions(input: Cloud, intensity: gCo2ePerKwh) IndirectEstimation
}
class estimate-downstream-emissions{
<<module>>
+siteTypeInfo: Record~PurposeOfSite, SiteInformation~
+estimateDownstreamEmissions(downstream: Downstream) DownstreamEstimation
+estimateDownstreamEmissions(downstream: Downstream, ...) DownstreamEstimation
}
}
Expand All @@ -51,9 +51,7 @@ classDiagram
class estimate-energy-emissions {
<<module>>
+locationIntensityMap: Record~WorldLocation, KgCo2ePerKwh~
+estimateEnergyEmissions(...) KgCo2e
+getCarbonIntensity(...) gCo2ePerKwh
}
}
Expand Down Expand Up @@ -109,6 +107,7 @@ Estimate emissions from Indirect categories
##### Parameters

`input:`[`Cloud`](types.md#estimatorvalues) - The inputs relevant to cloud.
`intensity:`[`gCo2ePerKwh`](types.md#units) - The Carbon intensity of the cloud region.

##### Returns

Expand Down Expand Up @@ -140,6 +139,7 @@ Estimate emissions from Downstream categories
##### Parameters

`downstream:`[`Downstream`](types.md#estimatorvalues) - The inputs relevant to downstream emissions.
`intensity:`[`gCo2ePerKwh`](types.md#units) - The Carbon intensity of the downstream region.

##### Returns

Expand Down Expand Up @@ -290,7 +290,7 @@ Creates device usage without exposing the exact method of calculation.

`type:`[`DeviceType`](#devicetype) - The type of device being used.
`category:`[`DeviceCategory`](types#devicecategory) - The category the device usage falls under.
`location:`[`WorldLocation`](types#estimatorvalues) - The location the devices are being used in.
`intensity:`[`gCo2ePerKwh`](types#units) - The carbon intensity of the region the devices are being used in.
`count: number` - The number of devices being used.
`pue?: number` - The Power Usage Effectiveness of the device usage (optional, defaults to 1).

Expand All @@ -309,26 +309,8 @@ Estimate emissions from energy used in a location.
##### Parameters

`energy:`[`KilowattHour`](types.md#units) - Amount of energy used.
`location:`[`WorldLocation`](types.md#estimatorvalues) - The World Location where the energy was used for Carbon Intensity.
`intensity:`[`gCo2ePerKwh`](types.md#units) - The Carbon Intensity of the region where the energy was used.

##### Returns

[`KgCo2e`](types.md#units) - Kg of CO2e emitted via energy use.

#### `getCarbonIntensity()`

Exported to get Carbon Intensity in unit that [CO2.js](https://www.thegreenwebfoundation.org/co2-js/) library requires.

##### Parameters

`location:`[`WorldLocation`](types.md#estimatorvalues) - The World Location for Carbon Intensity.

##### Returns

[`gCo2ePerKwh`](types.md#units) - g of CO2e emitted per kWh of energy used.

### Exported variables

#### `locationIntensityMap: Record<WorldLocation, KgCo2ePerKwh>`

Exported to allow use in assumptions component.
32 changes: 29 additions & 3 deletions docs/services.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,24 @@ This page details the Angular services that are part of the application.
classDiagram
class CarbonEstimationService{
<<service>>
-loggingService: LoggingService
-carbonIntensityService: CarbonIntensityService
-loggingService: LoggingService
+calculateCarbonEstimation(formValue: EstimatorValues) CarbonEstimation
+estimateServerCount(formValue: EstimatorValues) number
-estimateDeviceUsage(formValue: EstimatorValues) DeviceUsage[]
}
class CarbonIntensityService{
<<service>>
+getCarbonIntensity(location: WorldLocation) gCo2ePerKwh
}
class LoggingService{
<<service>>
+log(...output: any[])
}
CarbonEstimationService --> "-carbonIntensityService" CarbonIntensityService
CarbonEstimationService --> "-loggingService" LoggingService
```

Expand All @@ -30,6 +37,7 @@ The main service responsible for producing a carbon estimate.

Takes input form values and uses them to calculate a carbon estimation.
Uses [LoggingService](#loggingservice) to output intermediate parts of the calculation.
Uses [CarbonIntensityService](#carbonintensityservice) to get the carbon intensity of input locations.
Returns estimation as percentages.
Uses functions in other modules to perform the calculation.

Expand Down Expand Up @@ -57,13 +65,13 @@ classDiagram
class estimate-indirect-emissions{
<<module>>
+estimateIndirectEmissions(Cloud input) IndirectEstimation
+estimateIndirectEmissions(input: Cloud, intensity: gCo2ePerKwh) IndirectEstimation
}
class estimate-downstream-emissions{
<<module>>
+Record~PurposeOfSite, SiteInformation~ siteTypeInfo
+estimateDownstreamEmissions(Downstream downstream) DownstreamEstimation
+estimateDownstreamEmissions(Downstream downstream, intensity: gCo2ePerKwh) DownstreamEstimation
}
}
Expand Down Expand Up @@ -93,6 +101,24 @@ Method is used as part of [`calculateCarbonEstimation()`](#calculatecarbonestima

`number` - The estimated server count given the current input.

## CarbonIntensityService

Currently a simple service to wrap the usage of the CO2.js library to reduce dependencies and allow a switch to a different provider in future.

### Public Methods

#### `getCarbonIntensity()`

Gets a carbon intensity figure given a region.

##### Parameters

`location:`[`WorldLocation`](types.md#estimatorvalues) - The location to get the carbon intensity for.

##### Returns

[`gCo2ePerKwh`](types.md#units) - The carbon intensity of the location in grams of CO2 equivalent per Kilowatt hour of energy consumed.

## LoggingService

Currently a simple service to wrap console logging.
Expand Down
19 changes: 9 additions & 10 deletions docs/types.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,14 @@ classDiagram
class WorldLocation{
<<union>>
'global'
'uk'
'europe'
'northAmerica'
'asia'
'africa'
'oceania'
'latinAmerica'
'WORLD'
'GBR'
'EUROPE'
'NORTH AMERICA'
'ASIA'
'AFRICA'
'OCEANIA'
'LATIN AMERICA AND CARIBBEAN'
}
class CostRange {
Expand Down Expand Up @@ -172,9 +172,8 @@ export type Watt = number;
export type Hour = number;
export type Year = number;
export type KilowattHour = number;
export type KgCo2ePerKwh = number;
export type gCo2ePerKwh = number;
export type KgCo2e = number;
export type gCo2ePerKwh = number;
export type Gb = number;
```

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,24 +94,27 @@ <h4 class="tce-text-lg">Power consumption</h4>
</p>
<h4 class="tce-text-lg">Carbon Intensity</h4>
<p class="tce-text-sm">
We make use of the latest available Carbon Intensity figures from the
<a class="tce-underline" href="https://ember-climate.org/data/data-tools/data-explorer/">Ember Data Explorer</a> for
the regions we make available. At present these are:
We make use of the latest available Carbon Intensity figures from
<a class="tce-underline" href="https://ember-climate.org/data/data-tools/data-explorer/">Ember</a> via the
<a class="tce-underline" href="https://www.thegreenwebfoundation.org/co2-js/">CO2.js</a> library. We limit the range
of regions that can be selected, which are currently:
</p>
<table class="tce-w-fit">
<thead>
<tr>
<th class="tce-text-sm tce-border tce-border-slate-300 tce-px-2 tce-py-1 tce-font-medium">World Location</th>
<th class="tce-text-sm tce-border tce-border-slate-300 tce-px-2 tce-py-1 tce-font-medium">
Carbon Intensity (Kg CO2e/kWh)
Carbon Intensity (g CO2e/kWh)
</th>
</tr>
</thead>
<tbody>
@for (item of locationCarbonInfo; track $index) {
<tr>
<td class="tce-text-sm tce-border tce-border-slate-300 tce-px-2 tce-py-1">{{ item.location }}</td>
<td class="tce-text-sm tce-border tce-border-slate-300 tce-px-2 tce-py-1">{{ item.carbonIntensity }}</td>
<td class="tce-text-sm tce-border tce-border-slate-300 tce-px-2 tce-py-1">
{{ item.carbonIntensity | number: '0.0-0' }}
</td>
</tr>
}
</tbody>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { CLOUD_AVERAGE_PUE, ON_PREMISE_AVERAGE_PUE } from '../estimation/constan
import { siteTypeInfo } from '../estimation/estimate-downstream-emissions';
import { PurposeOfSite, WorldLocation, locationArray, purposeOfSiteArray } from '../types/carbon-estimator';
import { DecimalPipe } from '@angular/common';
import { locationIntensityMap } from '../estimation/estimate-energy-emissions';
import { CarbonIntensityService } from '../services/carbon-intensity.service';

const purposeDescriptions: Record<PurposeOfSite, string> = {
information: 'Information',
Expand All @@ -14,14 +14,14 @@ const purposeDescriptions: Record<PurposeOfSite, string> = {
};

const locationDescriptions: Record<WorldLocation, string> = {
global: 'Global',
northAmerica: 'North America',
europe: 'Europe',
uk: 'United Kingdom',
asia: 'Asia',
africa: 'Africa',
oceania: 'Oceania',
latinAmerica: 'Latin America and Caribbean',
WORLD: 'Global',
'NORTH AMERICA': 'North America',
EUROPE: 'Europe',
GBR: 'United Kingdom',
ASIA: 'Asia',
AFRICA: 'Africa',
OCEANIA: 'Oceania',
'LATIN AMERICA AND CARIBBEAN': 'Latin America and Caribbean',
};

@Component({
Expand All @@ -39,13 +39,17 @@ export class AssumptionsAndLimitationComponent implements AfterContentInit {
time: siteTypeInfo[purpose].averageMonthlyUserTime,
data: siteTypeInfo[purpose].averageMonthlyUserData,
}));
readonly locationCarbonInfo = locationArray.map(location => ({
location: locationDescriptions[location],
carbonIntensity: locationIntensityMap[location],
}));
readonly locationCarbonInfo;

@ViewChild('assumptionsLimitation', { static: true }) public assumptionsLimitation!: ElementRef<HTMLDivElement>;

constructor(private intensityService: CarbonIntensityService) {
this.locationCarbonInfo = locationArray.map(location => ({
location: locationDescriptions[location],
carbonIntensity: this.intensityService.getCarbonIntensity(location),
}));
}

public ngAfterContentInit(): void {
this.assumptionsLimitation.nativeElement.focus();
}
Expand Down
20 changes: 10 additions & 10 deletions src/app/carbon-estimator-form/carbon-estimator-form.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@ import { FormatCostRangePipe } from '../pipes/format-cost-range.pipe';
import { HelperInfoComponent } from '../helper-info/helper-info.component';

const locationDescriptions: Record<WorldLocation, string> = {
global: 'Globally',
northAmerica: 'in North America',
europe: 'in Europe',
uk: 'in the UK',
asia: 'in Asia',
africa: 'in Africa',
oceania: 'in Oceania',
latinAmerica: 'in Latin America or the Caribbean',
WORLD: 'Globally',
'NORTH AMERICA': 'in North America',
EUROPE: 'in Europe',
GBR: 'in the UK',
ASIA: 'in Asia',
AFRICA: 'in Africa',
OCEANIA: 'in Oceania',
'LATIN AMERICA AND CARIBBEAN': 'in Latin America or the Caribbean',
};

@Component({
Expand Down Expand Up @@ -150,10 +150,10 @@ export class CarbonEstimatorFormComponent implements OnInit {
public handleSubmit() {
const formValue = this.estimatorForm.getRawValue();
if (formValue.onPremise.serverLocation === 'unknown') {
formValue.onPremise.serverLocation = 'global';
formValue.onPremise.serverLocation = 'WORLD';
}
if (formValue.cloud.cloudLocation === 'unknown') {
formValue.cloud.cloudLocation = 'global';
formValue.cloud.cloudLocation = 'WORLD';
}
this.formSubmit.emit(formValue as EstimatorValues);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,22 +19,22 @@ export const defaultValues: Required<EstimatorValues> = {
upstream: {
headCount: 100,
desktopPercentage: 50,
employeeLocation: 'global',
employeeLocation: 'WORLD',
},
onPremise: {
estimateServerCount: false,
serverLocation: 'global',
serverLocation: 'WORLD',
numberOfServers: 10,
},
cloud: {
noCloudServices: false,
cloudLocation: 'global',
cloudLocation: 'WORLD',
cloudPercentage: 50,
monthlyCloudBill: costRanges[0],
},
downstream: {
noDownstream: false,
customerLocation: 'global',
customerLocation: 'WORLD',
monthlyActiveUsers: 100,
mobilePercentage: 50,
purposeOfSite: 'average',
Expand Down
Loading

0 comments on commit 9a9cd2f

Please sign in to comment.