Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Doc: Index + Timeseries #3035

Merged
merged 23 commits into from
Jun 5, 2024
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 36 additions & 1 deletion docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,39 @@ To build the docs locally, run the following commands:
pip install -r docs/requirements.txt
sphinx-build -a docs ./build-docs
~~~
Then open `build-docs/index.html` in your browser.
Then open `build-docs/index.html` in your browser.

If you want to add links to another documentation, add the corresponding repository to the `conf.py` file.
In order to automatically get the version specified in the `pom.xml`, please use the same naming as the version: if you define the
Groovy version with `<groovy.version>`, then use `groovy` as key.
For example, to add a link to the documentation of Sphinx, you need to add the following lines:
~~~python
# This parameter might already be present, just add the new value
intersphinx_mapping = {
"sphinx": ("https://www.sphinx-doc.org/en/master/", None),
}
~~~

Then in your documentation file, you can add links to PowSyBl-Core documentation. If you want to link to a whole page,
use one of the following example:
~~~Markdown
- {doc}`sphinx:usage/extensions/intersphinx`
- {doc}`Intersphinx <sphinx:usage/extensions/intersphinx>`
- [Intersphinx](inv:sphinx:std:doc#usage/extensions/intersphinx).
~~~

If you want to link a specific part of a page, use one of those examples:
~~~Markdown
- [Intersphinx roles](inv:#ref-role).
- [Intersphinx roles](inv:sphinx#ref-role).
- [Intersphinx roles](inv:sphinx:std:label:#ref-role).
- [Intersphinx roles](inv:sphinx:*:*:#ref-role).
~~~
*Note: for the last examples to work, there need to be a corresponding reference in the external documentation.
For those examples, `(ref-role)=` has been added right before the corresponding title
in the [Cross-referencing syntax page](inv:sphinx:std:doc#usage/referencing).*

*Note²: if the build fails, try with the `-E` option to clear the cache:*
~~~bash
sphinx-build -a -E docs ./build-docs
~~~
101 changes: 101 additions & 0 deletions docs/_templates/page.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
<!--
~ Copyright (c) 2024, RTE (http://www.rte-france.com)
~ This Source Code Form is subject to the terms of the Mozilla Public
~ License, v. 2.0. If a copy of the MPL was not distributed with this
~ file, You can obtain one at http://mozilla.org/MPL/2.0/.
~ SPDX-License-Identifier: MPL-2.0
-->

{% extends "furo/page.html" %}

{% block footer %}
<div class="related-pages">
{% if next -%}
<a class="next-page" href="{{ next.link }}">
<div class="page-info">
<div class="context">
<span>{{ _("Next") }}</span>
</div>
<div class="title">{{ next.title }}</div>
</div>
<svg class="furo-related-icon"><use href="#svg-arrow-right"></use></svg>
</a>
{%- endif %}
{% if prev -%}
<a class="prev-page" href="{{ prev.link }}">
<svg class="furo-related-icon"><use href="#svg-arrow-right"></use></svg>
<div class="page-info">
<div class="context">
<span>{{ _("Previous") }}</span>
</div>
{% if prev.link == pathto(master_doc) %}
<div class="title">{{ _("Home") }}</div>
{% else %}
<div class="title">{{ prev.title }}</div>
{% endif %}
</div>
</a>
{%- endif %}
</div>
<div class="bottom-of-page">
<div class="left-details">
{%- if show_copyright %}
<div class="copyright">
{%- if hasdoc('copyright_year') %}
{% trans path=pathto('copyright_year'), copyright_year=copyright_year|e -%}
<p class="text-justify">
<a href="{{ path }}">Copyright</a> &#169; {{ copyright_year }}Authors of <a href="{{ github_repository }}">PowSyBl documentation</a>.
Unless otherwise indicated, content is licensed under <a rel="license" href="http://creativecommons.org/licenses/by/4.0/">CC-BY-4.0</a>.
</p>
<p class="text-justify">
The Linux Foundation has registered trademarks and uses trademarks. For a list of trademarks of The
Linux Foundation, please see our <a href="https://www.linuxfoundation.org/trademark-usage">Trademark
Usage</a> page. Linux is a registered trademark of Linus Torvalds.
</p>
{%- endtrans %}
{%- else %}
{% trans copyright=copyright|e -%}
<p class="text-justify">
Copyright &#169; {{ copyright_year }} Authors of <a href="{{ github_repository }}">PowSyBl documentation</a>.
Unless otherwise indicated, content is licensed under <a rel="license" href="http://creativecommons.org/licenses/by/4.0/">CC-BY-4.0</a>.
</p>
<p class="text-justify">
The Linux Foundation has registered trademarks and uses trademarks. For a list of trademarks of The
Linux Foundation, please see our <a href="https://www.linuxfoundation.org/trademark-usage">Trademark
Usage</a> page. Linux is a registered trademark of Linus Torvalds.
</p>
{%- endtrans %}
{%- endif %}
</div>
{%- endif %}
{% trans %}Made with {% endtrans -%}
{%- if show_sphinx -%}
{% trans %}<a href="https://www.sphinx-doc.org/">Sphinx</a> and {% endtrans -%}
<a class="muted-link" href="https://pradyunsg.me">@pradyunsg</a>'s
{% endif -%}
{% trans %}
<a href="https://github.com/pradyunsg/furo">Furo</a>
{% endtrans %}
{%- if last_updated -%}
<div class="last-updated">
{% trans last_updated=last_updated|e -%}
Last updated on {{ last_updated }}
{%- endtrans -%}
</div>
{%- endif %}
</div>
<div class="right-details">
{% if theme_footer_icons or READTHEDOCS -%}
<div class="icons">
{% if theme_footer_icons -%}
{% for icon_dict in theme_footer_icons -%}
<a class="muted-link {{ icon_dict.class }}" href="{{ icon_dict.url }}" aria-label="{{ icon_dict.name }}">
{{- icon_dict.html -}}
</a>
{% endfor %}
{%- endif %}
</div>
{%- endif %}
</div>
</div>
{% endblock footer %}
25 changes: 25 additions & 0 deletions docs/_templates/sidebar/brand.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{#-
Overrides furo's brand.html to customize links:
- The logo links to a custom page (set sidebar_logo_href option in html_context)
- The title links to the subproject's main page
-#}
<a class="sidebar-brand{% if logo %} centered{% endif %}" href="{% if sidebar_logo_href %} {{ sidebar_logo_href }} {% else %} {{ pathto(master_doc) }} {% endif %}">
{% block brand_content %}
{%- if logo_url %}
<div class="sidebar-logo-container">
<img class="sidebar-logo" src="{{ logo_url }}" alt="Logo"/>
</div>
{%- endif %}
{%- if theme_light_logo and theme_dark_logo %}
<div class="sidebar-logo-container">
<img class="sidebar-logo only-light" src="{{ pathto('_static/' + theme_light_logo, 1) }}" alt="Light Logo"/>
<img class="sidebar-logo only-dark" src="{{ pathto('_static/' + theme_dark_logo, 1) }}" alt="Dark Logo"/>
</div>
{%- endif %}
{% endblock brand_content %}
</a>
{% if not theme_sidebar_hide_name %}
<a class="sidebar-brand{% if logo %} centered{% endif %}" href="{{ pathto(master_doc) }}">
<span class="sidebar-brand-text">{{ docstitle if docstitle else project }}</span>
</a>
{%- endif %}
54 changes: 45 additions & 9 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
import datetime
import os
import re
import sys

# Path to python sources, for doc generation on readthedocs
Expand All @@ -21,9 +23,40 @@

# -- Project information -----------------------------------------------------

project = 'powsybl core'
copyright = '2024, RTE (http://www.rte-france.com)'

# Only those 4 parameters have to be modified for each specific repository
project = 'PowSyBl Core'
flo-dup marked this conversation as resolved.
Show resolved Hide resolved
module_name = "powsybl-core"
github_repository = "https://github.com/powsybl/powsybl-core/"

# Build year for the copyright
copyright_year = f'2018-{ datetime.datetime.now().year }'

# Find the version and release information.
# We have a single source of truth for our version number: the project's pom.xml file.
# This next bit of code reads from it.
file_with_version = os.path.join(source_path, "pom.xml")
with open(file_with_version) as f:
next_line_contains_version = False
for line in f:
if next_line_contains_version == False:
m = re.match(r'^ {4}\<artifactId\>' + module_name + r'\<\/artifactId\>', line)
if m:
next_line_contains_version = True
else:
m = re.match(r'^ {4}\<version\>(.*)\<\/version\>', line)
if m:
__version__ = m.group(1)
# The short X.Y version.
version = ".".join(__version__.split(".")[:2])
# The full version, including alpha/beta/rc tags.
release = __version__
break
else: # AKA no-break
version = release = "dev"


# Short project name
short_project = project.split(" ", 1)[1] if project.split(" ", 1)[0] == "PowSyBl" else project

# -- General configuration ---------------------------------------------------

Expand All @@ -38,7 +71,9 @@
'sphinx.ext.todo',
'sphinx.ext.intersphinx',
'sphinx_tabs.tabs',
'myst_parser']
'myst_parser',
# Extension used to add a "copy" button on code blocks
'sphinx_copybutton']
myst_enable_extensions = [
"amsmath",
"colon_fence",
Expand All @@ -63,20 +98,21 @@
#
html_theme = "furo"

html_title = 'core'
html_short_title = 'core'
html_title = f"{project} v{release}"
html_short_title = f'{short_project} v{release}'

html_logo = '_static/logos/logo_lfe_powsybl.svg'
html_favicon = "_static/favicon.ico"

html_context = {
# TODO : replace next option with "https://powsybl.readthedocs.org" when website is published
"sidebar_logo_href": "https://www.powsybl.org/"
"copyright_year": copyright_year,
"sidebar_logo_href": "https://powsybl.readthedocs.io/",
"github_repository": github_repository
}

html_theme_options = {
# the following 3 lines enable edit button
"source_repository": "https://github.com/powsybl/powsybl-core/",
"source_repository": github_repository,
"source_branch": "main",
"source_directory": "docs/",
}
Expand Down
125 changes: 125 additions & 0 deletions docs/data/timeseries.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
---
layout: default
---

(timeseries)=
# 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` or `String`.
- 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](https://en.wikipedia.org/wiki/Unevenly_spaced_time_series): 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:
```json
{
"offset" : 0,
"values" : [ 1.0, 1.0, 1.0, 3.0 ]
}
```
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](https://fr.wikipedia.org/wiki/Run-length_encoding).
The JSON serialization of compressed data chunks looks like:
Output:
```json
{
"offset" : 0,
"uncompressedLength" : 4,
"stepValues" : [ 1.0, 3.0 ],
"stepLengths" : [ 3, 1 ]
}
```

Time series can be imported from CSV data:
```java
// Creating a map of TimeSeries per version by parsing a CSV file
Map<Integer, List<TimeSeries>> timeSeriesPerVersion = TimeSeries.parseCsv(pathToCSV);

// Creating a map of TimeSeries per version by parsing the CSV string with a specified configuration
TimeSeriesCsvConfig timeSeriesCsvConfig = new TimeSeriesCsvConfig(';', true, TimeFormat.MILLIS);
Map<Integer, List<TimeSeries>> timeSeriesPerVersion = TimeSeries.parseCsv(csvAsString, timeSeriesCsvConfig);
```

(calculated-timeseries)=
## Calculated time series

Starting from a double time series, it is possible to create calculated time series using a [Groovy](http://groovy-lang.org/)
script.

For instance, let us consider 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:

```groovy
ts['a'] = ts['dts'] + 1
ts['b'] = ts['a'] * 2
```
The time series `a` and `b`, serialized in JSON format, then look like:
```json
[ {
"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
}
}
} ]
```
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 | Return type |
|----------|---------------------------------------------------------------------------------|-------------------------|-------------|
| `+` | addition | `ts['a'] + ts['b']` | Numerical |
| `-` | substraction | `ts['a'] - ts['b']` | Numerical |
| `*` | multiplication | `ts['a'] * ts['b']` | Numerical |
| `/` | division | `ts['a'] / ts['b']` | Numerical |
| `==` | 1 if equals, 0 otherwise | `ts['a'] == ts['b']` | Boolean |
| `!=` | 1 if not equals, 0 otherwise | `ts['a'] != ts['b']` | Boolean |
| `<` | 1 if less than, 0 otherwise | `ts['a'] < ts['b']` | Boolean |
| `<=` | 1 if less than or equals to, 0 otherwise | `ts['a'] <= ts['b']` | Boolean |
| `>` | 1 if greater, 0 otherwise | `ts['a'] > ts['b']` | Boolean |
| `>=` | 1 if greater than or equals to, 0 otherwise | `ts['a'] >= ts['b']` | Boolean |
| `-` | negation | `-ts['a']` | Numerical |
| `abs` | absolute value | `ts['a'].abs()` | Numerical |
| `time` | convert to time index vector ([epoch](https://en.wikipedia.org/wiki/Unix_time)) | `ts['a'].time()` | Numerical |
| `min` | min value | `ts['a'].min(10)` | Boolean |
| `max` | max value | `ts['a'].max(10)` | Boolean |
| `min` | point-to-point min values between timeseries | `min(ts['a'], ts['b'])` | Numerical |
| `max` | point-to-point max values between timeseries | `max(ts['a'], ts['b'])` | Numerical |

In 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:
```groovy
a = ts['dts'].time() < time('2018-01-01T00:00:01Z')
```

Loading