Time series
Time series modeling
In Powsybl, time series are modeled by:
- A name to uniquely identify a time series inside a store.
- A data type which is either
double
orString
. - A time index to define a list of instants for which data exists. Three different implementations of the time index are available
in the framework, depending on the need:
- Regular index: the time step size is constant
- Irregular index: the time step size varies
- Infinite index: the time series contains only two points, one at instant 0 and another at instant
Long.MAX_VALUE
- Metadata: a list of key/value string data
- Data chunks: an ordered list of data that will be associated to instants of the time index. The data chunks may be compressed or uncompressed.
An uncompressed JSON data chunk looks like:
{
"offset" : 0,
"values" : [ 1.0, 1.0, 1.0, 3.0 ]
}
We can see that an uncompressed data chunk is modeled with a double (or String) array and an offset.
It defines values associated to instants of the time index from offset
to offset + values.length
.
It is possible to compress the data chunks, using for example the RLE. The JSON serialization of compressed data chunks looks like: Output:
{
"offset" : 0,
"uncompressedLength" : 4,
"stepValues" : [ 1.0, 3.0 ],
"stepLengths" : [ 3, 1 ]
}
Time series can be imported from CSV data.
Calculated time series
Starting from a double time series, it is possible to create calculated time series using a Groovy script.
For instance, let us consider the the following example.
Let’s say we have created a first double time series named dts
in a script, it is then possible to create new time series a
and b
by writing:
ts['a'] = ts['dts'] + 1
ts['b'] = ts['a'] * 2
The time series a
and b
, serialized in JSON format, then look like:
[ {
"name" : "a",
"expr" : {
"binaryOp" : {
"op" : "PLUS",
"timeSeriesName" : "dts",
"integer" : 1
}
}
}, {
"name" : "b",
"expr" : {
"binaryOp" : {
"op" : "MULTIPLY",
"binaryOp" : {
"op" : "PLUS",
"timeSeriesName" : "dts",
"integer" : 1
},
"integer" : 2
}
}
} ]
Indeed, the calculated time series are evaluated on the fly during array conversion or iteration (through iterators or streams): only the arithmetic expression is stored.
Here is the list of supported vector operations:
Operator | Purpose | Example |
---|---|---|
+ | addition | ts[‘a’] + ts[‘b’] |
- | substraction | ts[‘a’] - ts[‘b’] |
* | multiplication | ts[‘a’] * ts[‘b’] |
/ | division | ts[‘a’] / ts[‘b’] |
== | 1 if equals, 0 otherwise | ts[‘a’] == ts[‘b’] |
!= | 1 if not equals, 0 otherwise | ts[‘a’] != ts[‘b’] |
< | 1 if less than, 0 otherwise | ts[‘a’] < ts[‘b’] |
<= | 1 if less than or equals to, 0 otherwise | ts[‘a’] <= ts[‘b’] |
> | 1 if greater, 0 otherwise | ts[‘a’] > ts[‘b’] |
>= | 1 if greater than or equals to, 0 otherwise | ts[‘a’] >= ts[‘b’] |
- | negation | -ts[‘a’] |
abs | absolute value | ts[‘a’].abs() |
time | convert to time index vector (epoch) | ts[‘a’].time() |
min | min value | ts[‘a’].min(10) |
max | max value | ts[‘a’].max(10) |
About the Groovy DSL syntax, both timeSeries['a']
and ts['a']
are supported and are equivalent.
To compare a time index vector to a literal date, the time('2018-01-01T00:00:01Z')
function is available. For instance, the
following code create a time series of 0 and 1 values:
a = ts['dts'].time() < time('2018-01-01T00:00:01Z')