Example uses of the codebase

Pulling net positions from ENTSOE-Transparency

Note

To pull data from the ENTSOE-Transparency you need to set API keys as noted in the configuration guide.

Pulling data from JAO and ENTSOE-Transparency is the first step of the analysis. The data can be pulled from the remote APIs or from the local cache. The local cache is by default stored at ~/.linearisation_data and contains hourly snapshots of data. To pull data from JAO and compute the Net Positions in the Base Case (Common Grid Model Net Positions):

from datetime import date
from fbmc_quality.jao_data.analyse_jao_data import compute_basecase_net_pos
from fbmc_quality.jao_data.fetch_jao_data import fetch_jao_dataframe_timeseries

start = date(2023, 4, 1)
end = date(2023, 5, 1)

dataframe = fetch_jao_dataframe_timeseries(start, end)
if dataframe is None:
    raise RuntimeError("No data")

print(dataframe.head())

basecase_nps = compute_basecase_net_pos(start, end)

if basecase_nps is None:
    raise RuntimeError("No data")

basecase_nps['NO2'].plot()

The dataframe from JAO contains the FBMC results for all CNECs for a given hour. This includes the zonal PTDFs that we use to compute the Base Case Net Positions. These are computed by summing up the cross border flows for each zonal border.

The API for pulling from ENTSOE-Transparency has a very similar API, you pull observed net positions with this snippet:

from fbmc_quality.jao_data import compute_basecase_net_pos
from fbmc_quality.jao_data import fetch_jao_dataframe_timeseries
from datetime import date

start = date(2023, 4, 1)
end = date(2023, 5, 1)

observed_nps = fetch_net_position_from_crossborder_flows(start, end)

if observed_nps is None:
    raise RuntimeError("No data")

observed_nps['NO2'].plot()

And pulling the data from a single cross border CNEC is similarly concise:

from fbmc_quality.entsoe_data import fetch_net_position_from_crossborder_flows, fetch_observed_entsoe_data_for_cnec
from fbmc_quality.enums import BiddingZonesEnum
from datetime import date

start = date(2023, 4, 1)
end = date(2023, 5, 1)

observed_nps = fetch_net_position_from_crossborder_flows(start, end)

if observed_nps is None:
    raise RuntimeError("No data")

observed_nps['NO2'].plot()

no1_no2_observed_flow = fetch_observed_entsoe_data_for_cnec(from_area=BiddingZonesEnum.NO2, to_area=BiddingZonesEnum.NO1, start_date=start, end_date=end)
no1_no2_observed_flow.plot()

Computing the linearisation error using the API

To plot the linearsiation error for a period we need to pull JAO data and observed data to measure against. This section uses the method in the previous sub-section to pull data to compute flow and net positions. Lets begin:

from fbmc_quality.linearisation_analysis import compute_linearisation_error
from fbmc_quality.linearisation_analysis import compute_linearisation_error, compute_linearised_flow
from fbmc_quality.linearisation_analysis import load_data_for_corridor_cnec, fetch_jao_data_basecase_nps_and_observed_nps

from datetime import date
import plotly.express as px
import plotly.io as pio
import pandas as pd

start = date(2023, 4, 1)
end = date(2023, 5, 1)
data = fetch_jao_data_basecase_nps_and_observed_nps(start, end)
cnec_name ='NO3->SE2'
cnec_data = load_data_for_corridor_cnec(
    cnec_name,
    data
)
if cnec_data is None:
    raise ValueError("No data found")

The

fbmc_quality.linearisation_analysis.load_data_for_corridor_cnec(cnecName, jaodata_and_net_positions)

Loads data for a given cnec from its name as it appears in the JAO API

Parameters:
  • cnecName (str) – Name of the CNEC as it appears in the JAO API

  • jao_and_entsoe_data (JaoDataAndNPS) – Data from JAO and Entsoe APIs

Raises:

ValueError – If the cnecName is a border CNEC - raises if no mapping to ENTSOE transparency is found

Returns:

CNEC data if any is found

Return type:

CnecDataAndNPS | None

and,

fbmc_quality.linearisation_analysis.fetch_jao_data_basecase_nps_and_observed_nps(start, end)
Return type:

JaoDataAndNPS

returns Dataclass like NamedTuples. These containers are used in the workflow to pass data in a structured way.

Continuing:

lin_err = compute_linearisation_error(cnec_data.cnecData, cnec_data.observedNPs, cnec_data.observed_flow['flow'])
lin_err_frame = pd.DataFrame(
    {
        'Linearisation Error': lin_err,
        'Observed Flow': cnec_data.observed_flow['flow'],
        'Linearised Flow': compute_linearised_flow(cnec_data.cnecData, cnec_data.observedNPs),
    }
)

pio.templates.default = "ggplot2"
fig = px.density_contour(
    lin_err_frame,
    x='Observed Flow',
    y='Linearised Flow',
    marginal_x='box',marginal_y='box',
    title=cnec_name[10:],
    width=600,
    height=600,
)
fig.update_layout(
    font=dict(
        size=16,
    )
)
fig.update_traces(line={'width': 2})

This plot shows the observed flow, and linearised flow for the border cnec NO3->SE2