stsc-js v1.1.9
Smart Tariff, Smart Comparison Javascript Library
The smart tariff, smart comparison (STSC) project uses smart meter data along with tariffs to calculate cost and transform data to be used in presentation to user interfaces. The objective is to help consumers select the best tariff, track a current tariff forward and understand how in changing their energy consumption may effect the cost of energy.
Release POC3
This library works with a third proof of concept (POC3), supporting the data models for presentation within a larger STSC front end website. These Javascript libraries could be used to embed calculations within your own website and offer some insight into how the backend data is structured.
For this release the use cases are around using half hourly smart meter data from the DCC and a number of hypothetical tariffs taken from public sources or from energy suppliers. The source of data is Hildebrand's Glow data service, providing a binary file of consumption data. Tariffs in scope are of three types: 1. Flat rate tariffs - these are typical single rate tariffs 2. Time of Use (TOU) tariffs - rates (up to 48 per day) that change throughout the day, but are the same for everyday. This mirrors many of the ECO7, ECO10 rates as well as rates targeted at electric vehicle owners. Natural progression would be for different day of the week rates until they begin to look more like truly dynamic rates. 3. Dynamic - each half hour has a specific rate that changes, the rate may only be known a day or two ahead, therefore there is little predictability in the price.
Each of these usually has an additional standing charge expressed in pence per day that is locked in for a longer contract period.
Calculating cost for various time periods is a key output of the library. To do this, it is a relatively simple application of a tariff structure to energy consumption data. This can be used for a number of purposes:
- optimisation (at least historically) of tariff based on lowest cost; a brute force calculation is fairly quick within a browser however becomes more challenging as the number and complexity of the tariff (see calculation complexity for tariff types below)
- tracking/monitoring - possibly save off the results and then come back to track if the savings happened in reality
- scenarios - change the energy consumption and see what happens to the costs
Compute complexity and memory analysis
The implementation of the library is interesting to talk about in having sets of tariff types that will use the same energy input to calculate cost. For instance there may be 10 flat rate tariffs, 20 TOU and 30 dynamic tariffs to evaluate in order to chose the best tariff for the consumer. If the energy profile is changing, say I would like to consider what a smart EV charger may do to my cost given the same set of tariffs to evaluate, fast calculation becomes desirable if not necessary. Very complex and interacting scenarios challenge this further.
Although the scenarios themselves may not be fully coded in the library, the structure of the library should be considerate of the computational challenges and it is a goal to accommodate software like this on top of this library.
Calculating a flat rate tariff usually involves summarising data for a time period, such as one year or for 12 calendar months. The summary operation is an O(n) computation with n probably maximal for 13 months (396 days * 48 values = 19,008). Once the summary is complete, then new tariff calculations are O(1). For 10 flat rates, the complexity is 9 x O(1) + O(n).
TOU is fairly similar in complexity with a first pass of the data to put it into time buckets and then rates applied. In order to be as flexible as possible, we've opted for summarising the time period of interest into 48 half hourly buckets. For example a year's worth of data is summed within its half hourly slot. This allows for simple 48 x O(1) calculations for any TOU tariff.
Dynamic is more challenging in that every half hour has a different rate. There are not really any short cuts for calculation. Each tariff to be evaluated has an O(n) computational cost. The main approach has been to calculate all of the results at once for each tariff half hourly slot; this saves iterating for each tariff.
Timeseries
This is a base class that is used for storing and aggregating time series data. It assumes "perfect" data as higher level classes will know how to fill in the blanks. There are utility functions that calculate statistics within the time buckets so that when the aggregate function is called it knows how to reduce the data.
Note - there is a trick in that it when range is called, it returns a Timeseries, so then the functions are all available.
Full documentation of Timeseries will be done later as it is more important to see how the Timeseries is used in the higher level objects.
TBD: Future implementation may actually call a number of the aggregation functions as the main work is the loop, not the calculation. Presenting the aggregates back (sum, count, mean, variance) as an object is probably more efficient.
TBD: Histograms - for a timeseries as a whole
TBD: Operators for scenarios - come up with parameters that can preserve the energy in the signal, especially for moving consumption when Timeseries is used for the Energy object.
Energy
Energy is a Timeseries that is populated from a binary object sourced from Hildebrand's Glow service. The binary is used because of the size of JSON that would be required, plus benefits in being portable and helping the backend systems service fewer requests.
At the moment the Energy constructor takes an ID which is passed through to a server request. For testing and simplicity, the default consumption.php returns a binary - but will change in the next release to support a JWT token that will compile the binary and serve it back to the request. This populates data that methods can then act on. A simple example:
import { Energy } from './energy';
let myEnergy = new Energy('consumption.php');
myEnergy._initialize().then( () => {
console.log("Max 30min = " + myEnergy.max());
console.log("Min 30min = " + myEnergy.min());
console.log("Mean 30min = " + myEnergy.mean());
});
The private call to initialize is to make sure the data has been loaded from the server request. There is a task to rewrite this with something like a load method so that there is more control over when server requests are made. But forgive the design at the moment!
Max, min and mean are all against the complete set of energy data that has been loaded.
The consumption method is the start of looking deeper. It provides a range and aggregate c
let from = '2020-05-01T00:00:00+00:00';
let to = '2020-06-01T00:00:00+00:00';
myEnergy.consumption({
from: from,
to: to,
interval: 'PT30M',
aggregate: Energy.AGG_SUM,
}).then((newTS) => {
// newTS is a timeseries - you can console.log the output or set up something like
// a chart, assuming you have a Highcharts container div in HTML
chart = Highcharts.stockChart('container', {
rangeSelector: {
selected: 1
},
series: [{
name: 'Energy',
data: newTS.data
}]
});
}
You can set different intervals to aggregate with using the ISO standards. In reality, use the PT not the P type ISO durations. Also make sure to keep the timezone information on the ISO date passed in to the object as you will get strange behaviour with local day light savings without it.
TBD: certificate for energy data - HTTPS but based on a certificate per consumer
Tariff
Tariffs also load from a server. A tariff or set of tariffs can be evaluated with an energy input to give a cost. The Cost class is separated but depends a lot on Tariff structure to drive calculations. There are three Tariff types:
- Flat
- TOU
- Dynamic
And standing charges along with them. The tariff is really a product name or id that then looks up the tariff based on GSP code. These are regional codes that will charge different rates for distribution network charges and therefore may have slightly different unit rates and standing charges.
Also you can set Flat and TOU (in theory Dynamic as well) with object definitions, but normally these are served from a library of tariffs on offer from suppliers. An example of setting up a TOU tariff to illustrate structures is as follows:
let testFlatTariff = new Tariff('productid');
testFlatTariff.flatPrice = { start: '2020-01-01T00:00:00+00:00', rate: 8.234 };
testFlatTariff.standingCharge = 12.243;
let testTouTariff = new Tariff('productid');
testTouTariff.name = 'Test TOU';
testTouTariff.touPrice = { start: 0, end: 18, rate: 3.5 };
testTouTariff.touPrice = { start: 18, end: 40, rate: 8.234 };
testTouTariff.touPrice = { start: 40, end: 48, rate: 3.5 };
testTouTariff.standingCharge = 12.243;
Note you don't have to select a GSP, because the rates are being set.
Flat rate pricing is simply a single unit rate and standing charge. The productid is being set on creating a new instance but has the ability to be looked up via a server call. Using the flatPrice attribute, the tariff type is set to flat.
For TOU, the start and end values are half hourly slots 0 representing the first half hour in a day (00:00 - 00:30) and 47 representing the last (23:30-00:00). There are some non-working methods to take time values and set those rates - currently not working, but will be fixed in a future release. By assigning a price with the touPrice attribute, the tariff type is set to tou. TDB: add a start to tou (like flat)
Standing charge is common to all of the tariff types.
An example of a tariff that is loaded from the server is a Dynamic example, where it is almost impossible to set without a data feed. This is illustrated by:
let testDynamicTariff = new Tariff('agile');
testDynamicTariff.loadDynamic({gsp: 'J', name: 'agile'}).then( () => {
let testDynamicCost = new Cost(testDynamicTariff);
testDynamicCost.init = { energy: newTS };
testDynamicTariff.standingCharge = 21;
console.log("Dynamic total cost = £" + testDynamicCost.total);
console.log("Dynamic total consumption cost = £" + testDynamicCost.consumption);
console.log("Dynamic total standing charge cost = £" +
testDynamicCost.standingCharge.cost +
" over " +
testDynamicCost.standingCharge.days + " days");
});
All tariffs are timeseries, however there is limited practical use of this. It is mostly when the tariff is Dynamic where you may want to calculate the average unit rate over a day or similarly aggregate and analyse the different half hourly prices. TBD: accessor for rate given a time or range of times - this would populate a timeseries object for flat rate, tou or dynamic.
Cost
The above examples showed some examples of cost. Cost as a class is a Timeseries and therefore benefits from aggregation operations. You can use the range method to show the cost for the last 30 days, etc.
There are some other interesting general timeseries methods that are interesting and are used to speed up some of the cost calculations. Note the use of byHHSlot, which returns an aggregate based on slots 0-47, representing the half hour in a day. Day of week costs or consumption can use the byDOW method to calculate the average cost of energy for all Tuesdays.
Build
The build of the project is using webpack and ES6 (generally) syntax in coding as modules. The package.json file has a couple of scripts implemented that rely on you having webpack, babel and jasmine installed.
Typically the index.html file is served up to a webpack webserver that is running to help debug and show examples from index.js
Future is to make index.js properly export the classes rather than running the full code.
git clone https://ijosh@bitbucket.org/LDNSFO/stsc-js.git
cd stsc-js
npm install
npm run build
npm start
This should build and run the samples in a local web server, accessed with
http://localhost:8080/
A Highcharts stock chart should show with some data selected.
Jasmine tests
Broken for the moment - sorry :( will fix soon.
Babel transpile
Not used at the moment.
Webpack
Webpack is used very simply at the moment. There is a webpack.config.js file that has the directories for input/output of files. The bundle build is a development style bundle that should be further optimised for production.
NPM availability
Will be working on making all available via NPM.
Publication
This is published as a part of the BEIS sponsored Smart Tariff, Smart Comparison programme.
Copyright
© Copyright 2019-2021 Hildebrand Technology Limited.
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Glow Service
Please contact Hildebrand to gain access to smart meter data on a B2B basis. As a consumer you should be able to use the public data service to get access to your data.
Glow Forums
If there are particularly interesting discussion points, etc. please join the forum so we can keep questions and answers documented for everyone.
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago