Skip to content
This repository has been archived by the owner on Mar 31, 2021. It is now read-only.

- adds support for localized electric emission factors #399

Merged
merged 15 commits into from
May 19, 2020
Merged
122 changes: 122 additions & 0 deletions co2eq/electricity/emissionfactors.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
{
"factors": [
{ "country": "CAN", "state": "QC", "factor": 1.5 },
baywet marked this conversation as resolved.
Show resolved Hide resolved
{ "country": "CAN", "state": "AB", "factor": 800 },
{ "country": "CAN", "state": "BC", "factor": 9.7 },
{ "country": "CAN", "state": "MT", "factor": 2.1 },
{ "country": "CAN", "state": "NB", "factor": 330 },
{ "country": "CAN", "state": "NL", "factor": 40 },
{ "country": "CAN", "state": "NS", "factor": 720 },
{ "country": "CAN", "state": "NT", "factor": 200 },
{ "country": "CAN", "state": "NU", "factor": 790 },
{ "country": "CAN", "state": "ON", "factor": 20 },
{ "country": "CAN", "state": "PE", "factor": 16 },
{ "country": "CAN", "state": "SK", "factor": 710 },
{ "country": "CAN", "state": "YT", "factor": 57 },
{ "country": "AUS", "state": "ACT", "factor": 900 },
{ "country": "AUS", "state": "NSW", "factor": 920 },
{ "country": "AUS", "state": "NT", "factor": 730 },
{ "country": "AUS", "state": "QLD", "factor": 930 },
{ "country": "AUS", "state": "SA", "factor": 610 },
{ "country": "AUS", "state": "TAS", "factor": 220 },
{ "country": "AUS", "state": "VIC", "factor": 1170 },
{ "country": "AUS", "state": "WA", "factor": 750 },
{ "country": "USA", "state": "AK ", "factor": 445.2 },
{ "country": "USA", "state": "AL", "factor": 435.8 },
{ "country": "USA", "state": "AR", "factor": 533.1 },
{ "country": "USA", "state": "AZ", "factor": 443.8 },
{ "country": "USA", "state": "CA", "factor": 215.1 },
{ "country": "USA", "state": "CO", "factor": 700 },
{ "country": "USA", "state": "CT", "factor": 238.5 },
{ "country": "USA", "state": "DC", "factor": 229.4 },
{ "country": "USA", "state": "DE", "factor": 422.4 },
{ "country": "USA", "state": "FL", "factor": 488.6 },
{ "country": "USA", "state": "GA", "factor": 478.5 },
{ "country": "USA", "state": "HI", "factor": 734.6 },
{ "country": "USA", "state": "IA", "factor": 476.7 },
{ "country": "USA", "state": "ID", "factor": 89.7 },
{ "country": "USA", "state": "IL", "factor": 387.5 },
{ "country": "USA", "state": "IN", "factor": 866.7 },
{ "country": "USA", "state": "KS", "factor": 571.9 },
{ "country": "USA", "state": "KY", "factor": 934.6 },
{ "country": "USA", "state": "LA", "factor": 418.9 },
{ "country": "USA", "state": "MA", "factor": 393 },
{ "country": "USA", "state": "MD", "factor": 484.1 },
{ "country": "USA", "state": "ME", "factor": 165.1 },
{ "country": "USA", "state": "MI", "factor": 525.3 },
{ "country": "USA", "state": "MN", "factor": 484.5 },
{ "country": "USA", "state": "MO", "factor": 806.8 },
{ "country": "USA", "state": "MS", "factor": 447.9 },
{ "country": "USA", "state": "MT", "factor": 596.9 },
{ "country": "USA", "state": "NC", "factor": 414.4 },
{ "country": "USA", "state": "ND", "factor": 795.4 },
{ "country": "USA", "state": "NE", "factor": 613 },
{ "country": "USA", "state": "NH", "factor": 150.5 },
{ "country": "USA", "state": "NJ", "factor": 265.8 },
{ "country": "USA", "state": "NM", "factor": 749.6 },
{ "country": "USA", "state": "NV", "factor": 365.5 },
{ "country": "USA", "state": "NY", "factor": 221.2 },
{ "country": "USA", "state": "OH", "factor": 700.6 },
{ "country": "USA", "state": "OK", "factor": 497.8 },
{ "country": "USA", "state": "OR", "factor": 145.5 },
{ "country": "USA", "state": "PA", "factor": 408.4 },
{ "country": "USA", "state": "RI", "factor": 414 },
{ "country": "USA", "state": "SC", "factor": 300.5 },
{ "country": "USA", "state": "SD", "factor": 245.3 },
{ "country": "USA", "state": "TN", "factor": 474.2 },
{ "country": "USA", "state": "TX", "factor": 503 },
{ "country": "USA", "state": "UT", "factor": 776.1 },
{ "country": "USA", "state": "VA", "factor": 389 },
{ "country": "USA", "state": "VT", "factor": 31.7 },
{ "country": "USA", "state": "WA", "factor": 89 },
{ "country": "USA", "state": "WI", "factor": 663.2 },
{ "country": "USA", "state": "WV", "factor": 945.3 },
{ "country": "USA", "state": "WY", "factor": 966.7 },
{ "country": "AUT", "state": "", "factor": 142 },
baywet marked this conversation as resolved.
Show resolved Hide resolved
{ "country": "BEL", "state": "", "factor": 167 },
{ "country": "BGR", "state": "", "factor": 470 },
{ "country": "HRV", "state": "", "factor": 417 },
{ "country": "CYP", "state": "", "factor": 639 },
{ "country": "CZE", "state": "", "factor": 576 },
{ "country": "DNK", "state": "", "factor": 209 },
{ "country": "EST", "state": "", "factor": 875 },
{ "country": "FIN", "state": "", "factor": 143 },
{ "country": "FRA", "state": "", "factor": 47 },
{ "country": "DEU", "state": "", "factor": 469 },
{ "country": "GRC", "state": "", "factor": 567 },
{ "country": "HUN", "state": "", "factor": 314 },
{ "country": "ISL", "state": "", "factor": 0 },
{ "country": "IRL", "state": "", "factor": 393 },
{ "country": "ITA", "state": "", "factor": 327 },
{ "country": "LVA", "state": "", "factor": 313 },
{ "country": "LTU", "state": "", "factor": 362 },
{ "country": "LUX", "state": "", "factor": 201 },
{ "country": "MLT", "state": "", "factor": 761 },
{ "country": "NLD", "state": "", "factor": 457 },
{ "country": "NOR", "state": "", "factor": 11 },
{ "country": "POL", "state": "", "factor": 846 },
{ "country": "PRT", "state": "", "factor": 307 },
{ "country": "ROU", "state": "", "factor": 401 },
{ "country": "RUS", "state": "", "factor": 330.2 },
{ "country": "SVK", "state": "", "factor": 169 },
{ "country": "SVN", "state": "", "factor": 335 },
{ "country": "ESP", "state": "", "factor": 288 },
{ "country": "SWE", "state": "", "factor": 12 },
{ "country": "CHE", "state": "", "factor": 14 },
{ "country": "GBR", "state": "", "factor": 277.3 },
{ "country": "ZAF", "state": "", "factor": 960.6 },
{ "country": "CHN", "state": "", "factor": 623.6 },
{ "country": "HKG", "state": "", "factor": 770 },
{ "country": "IND", "state": "", "factor": 742.9 },
{ "country": "IDN", "state": "", "factor": 755.1 },
{ "country": "JPN", "state": "", "factor": 491.6 },
{ "country": "KOR", "state": "", "factor": 517 },
{ "country": "NZL", "state": "", "factor": 105.1 },
{ "country": "SAU", "state": "", "factor": 717.6 },
{ "country": "TUR", "state": "", "factor": 543.4 },
{ "country": "ARE", "state": "", "factor": 433.3 },
{ "country": "MEX", "state": "", "factor": 464 },
{ "country": "ARG", "state": "", "factor": 358.3 },
{ "country": "BRA", "state": "", "factor": 92.7 }
]
}
64 changes: 64 additions & 0 deletions co2eq/electricity/emissionfactors.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { ACTIVITY_TYPE_ELECTRICITY } from '../../definitions';
import { carbonEmissions } from './worldaverage';

describe('model uses emission factor', () => {
const wattHours = 1000;
test(`with default`, () => {
const activity = {
activityType: ACTIVITY_TYPE_ELECTRICITY,
energyWattHours: wattHours,
};

expect(carbonEmissions(activity)).toBe(0.475);
});
test(`with Montreals factor`, () => {
const activity = {
activityType: ACTIVITY_TYPE_ELECTRICITY,
energyWattHours: wattHours,
locationLat: 45.5124,
locationLon: -73.55468,
};

expect(carbonEmissions(activity)).toBe(0.0015);
});
test(`with Torontos factor`, () => {
const activity = {
activityType: ACTIVITY_TYPE_ELECTRICITY,
energyWattHours: wattHours,
locationLat: 43.761539,
locationLon: -79.411079,
};

expect(carbonEmissions(activity)).toBe(0.02);
});
test(`with New Yorks factor`, () => {
const activity = {
activityType: ACTIVITY_TYPE_ELECTRICITY,
energyWattHours: wattHours,
locationLat: 40.73061,
locationLon: -73.935242,
};

expect(carbonEmissions(activity)).toBeCloseTo(0.2212); // roundup issue here
});
test(`with Sidneys factor`, () => {
const activity = {
activityType: ACTIVITY_TYPE_ELECTRICITY,
energyWattHours: wattHours,
locationLat: -33.865143,
locationLon: 151.2099,
};

expect(carbonEmissions(activity)).toBe(0.92);
});
test(`with Londons factor`, () => {
const activity = {
activityType: ACTIVITY_TYPE_ELECTRICITY,
energyWattHours: wattHours,
locationLat: 51.509865,
locationLon: -0.118092,
};

expect(carbonEmissions(activity)).toBe(0.2773);
});
});
50 changes: 48 additions & 2 deletions co2eq/electricity/worldaverage.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import { featuresContaining } from '@ideditor/country-coder';
import { lookup } from 'reverse-geocode';
import { ELECTRICITY_ACTIVITIES } from '../../definitions';
import { factors } from './emissionfactors.json';

// ** modelName must not be changed. If changed then old activities will not be re-calculated **
export const modelName = 'electricity-world-average';
Expand All @@ -11,12 +14,17 @@ export const explanation = {
label: 'IEA (2019)',
href: 'https://www.iea.org/reports/global-energy-co2-status-report-2019/emissions',
},
{
label: 'Carbon footprint',
href:
'https://www.carbonfootprint.com/docs/2019_06_emissions_factors_sources_for_2019_electricity.pdf',
},
],
};

const worldCarbonIntensity = 475; // g/kWh

export const modelCanRunVersion = 1;
export const modelCanRunVersion = 2;
export function modelCanRun(activity) {
const { energyWattHours, activityType } = activity;
const isElectricActivity = ELECTRICITY_ACTIVITIES.includes(activityType);
Expand All @@ -26,12 +34,50 @@ export function modelCanRun(activity) {
function correctWithParticipants(footprint, participants) {
return footprint / (participants || 1);
}
const countriesWithStateData = ['CA', 'US', 'AU'];
function getCarbonFactor(locationLat, locationLon) {
const features = featuresContaining([locationLon, locationLat]); // order of parameters for this lib goes against conventions
if (!features || features.length === 0) {
return undefined;
}
const countries = features.filter(x => x.properties.level === 'country');
if (countries.length === 0) {
return undefined;
}
const country = countries[0];
const potentialFactors = factors.filter(x => x.country === country.properties.iso1A3);
if (potentialFactors.length === 1) {
return potentialFactors[0].factor;
}
if (
potentialFactors.length > 1 &&
countriesWithStateData.indexOf(country.properties.iso1A2) !== -1
) {
const reverseInformation = lookup(locationLat, locationLon, country.properties.iso1A2);
if (reverseInformation && reverseInformation.state_abbr) {
const stateCode = reverseInformation.state_abbr.toUpperCase();
const potentialStateFactors = potentialFactors.filter(x => x.state === stateCode);
return potentialStateFactors.length === 0
? potentialFactors[0].factor
: potentialStateFactors[0].factor;
}
return potentialFactors[0].factor;
}
return undefined;
}

/*
Carbon emissions of an activity (in kgCO2eq)
*/
function carbonEmissionsWithoutParticipants(activity) {
return ((activity.energyWattHours / 1000.0) * worldCarbonIntensity) / 1000.0;
let carbonFactor = worldCarbonIntensity;
if (activity.locationLon && activity.locationLat) {
const preciseFactor = getCarbonFactor(activity.locationLat, activity.locationLon);
if (preciseFactor) {
carbonFactor = preciseFactor;
}
}
return ((activity.energyWattHours / 1000.0) * carbonFactor) / 1000.0;
}

export function carbonEmissions(activity) {
Expand Down
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@
"main": "server.js",
"license": "MIT",
"dependencies": {
"@ideditor/country-coder": "^3.2.0",
"buffer": "^5.2.1",
"crypto-js": "^3.1.9-1",
"d3-geo": "^1.11.6",
"lodash": "^4.17.11",
"moment": "^2.23.0",
"moment-timezone": "^0.5.28",
"oauth-1.0a": "^2.2.5",
"reverse-geocode": "^1.3.3",
"superagent": "^4.1.0",
"tiny-hashes": "^1.0.1",
"uuid": "^3.3.2",
Expand All @@ -22,6 +24,7 @@
"@babel/plugin-proposal-class-properties": "^7.5.5",
"@babel/plugin-transform-runtime": "^7.6.2",
"@babel/preset-env": "^7.6.2",
"@types/jest": "^25.2.2",
"babel-eslint": "^10.0.1",
"babel-plugin-convert-to-json": "^0.1.0",
"babel-plugin-wildcard": "^5.0.0",
Expand Down
Loading