Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
69a3188
Changes by JD before edits of MS
May 13, 2026
78d07bc
Add example yaml for full example
May 13, 2026
de69b53
Add file for analysis-tasks, Add short cut for DSM
May 13, 2026
fb5d32b
Add file for flex-tasks, Add flexibility band generation,
May 13, 2026
bd5eaa8
Add file for grid-tasks, Add timeindex in setup task
May 13, 2026
aa78004
Add file for timeseries-tasks
May 13, 2026
3a28880
Edit uc4 example
May 19, 2026
03f97ea
Merge branch 'dev' into edisgo_run_edisgo
MoritzSchloesser May 19, 2026
b7e7415
Wire overlying-grid data through pipeline runner
May 20, 2026
b55e5a0
Fix OPF result write-back, flex resolution and overlying-grid SOC rei…
May 21, 2026
bb3a720
Changes by JD before edits of MS
May 13, 2026
0c8792e
Add example yaml for full example
May 13, 2026
d3476eb
Add file for analysis-tasks, Add short cut for DSM
May 13, 2026
25ae93e
Add file for flex-tasks, Add flexibility band generation,
May 13, 2026
56dbf72
Add file for grid-tasks, Add timeindex in setup task
May 13, 2026
579aba8
Add file for timeseries-tasks
May 13, 2026
65fa8fc
Edit uc4 example
May 19, 2026
7ad6490
Wire overlying-grid data through pipeline runner
May 20, 2026
5fce257
Redirect to installed julia version
May 21, 2026
d6eddc2
Fix OPF result write-back, flex resolution and overlying-grid SOC rei…
May 21, 2026
fc84a14
Add overly_grid boolean
May 21, 2026
d4672e3
Add full-flex distribution OPF configuration without overlying-grid c…
joda9 May 22, 2026
8d6a947
Add overlying grid enabler and source selection
May 27, 2026
04aad53
Change opf_version
May 27, 2026
f1be570
Add renewables_potential and dispatchable_generator timeseries to Ove…
ClaraBuettner May 27, 2026
e79dceb
Import overlying_grid_data only from csv files when it is not directl…
ClaraBuettner May 27, 2026
f31cf31
Select timesteps in overlying_grid_data that are relevant for eDisGo'…
ClaraBuettner May 27, 2026
58d8f73
Workaround for generators_dispatch stored as pandas.DataFrame
ClaraBuettner May 27, 2026
6382c43
Merge branch 'edisgo_run_edisgo' of github.com:openego/eDisGo into ed…
May 27, 2026
4f28f36
Merge commit '58d8f73e154736d5592e7595a8907259d4cfff37' into edisgo_r…
May 27, 2026
b367c81
Workarround for pd.DataFrames in flex_opt data
ClaraBuettner May 27, 2026
abf7f70
Replace hard-coded year and select it from the timeindex instead
ClaraBuettner Jun 3, 2026
76491a3
make ding0 grid path variable optional
joda9 Jun 3, 2026
0a28784
Merge pull request #635 from openego/features/edisgo_run_edisgo_ego_i…
joda9 Jun 3, 2026
5537672
Revert "Redirect to installed julia version"
Jun 10, 2026
8f9d07d
Merge branch 'dev' into edisgo_run_edisgo
joda9 Jun 23, 2026
74d6bd1
fix: resolve 10 review findings in the run pipeline framework
joda9 Jun 23, 2026
b3ff5ad
Merge branch 'dev' into edisgo_run_edisgo
joda9 Jul 1, 2026
031e743
refactor: declarative task metadata for the validator + shared artifa…
joda9 Jul 1, 2026
9b69608
refactor: shared time-series year-alignment helper + small cleanups
joda9 Jul 1, 2026
5957546
test: add task-level tests for the run pipeline
joda9 Jul 1, 2026
55d273a
Merge branch 'edisgo_run_edisgo' of github.com:openego/eDisGo into ed…
joda9 Jul 1, 2026
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
27 changes: 27 additions & 0 deletions edisgo/edisgo.py
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,33 @@ def config(self):
def config(self, kwargs):
self._config = Config(**kwargs)

def run_pipeline(self, config, overlying_grid_data=None):
"""
Run a YAML/JSON task pipeline on this EDisGo instance.

See :mod:`edisgo.run` for the config schema and task list.

Parameters
----------
config : str, :class:`pathlib.Path`, or dict
Pipeline config as path to a YAML/JSON file or as a dict.
overlying_grid_data : dict, optional
Overlying-grid data (e.g. eTraGo results) consumed by the
``import_overlying_grid_data`` task when
``overlying_grid.source == "etrago"``.

Returns
-------
:class:`~.EDisGo`
The EDisGo instance after the pipeline has run.

"""
from edisgo.run import _run_pipeline_on

return _run_pipeline_on(
self, config, overlying_grid_data=overlying_grid_data
)

def import_ding0_grid(self, path, legacy_ding0_grids=True):
"""
Import ding0 topology data from csv files in the format as
Expand Down
89 changes: 61 additions & 28 deletions edisgo/io/powermodels_io.py
Original file line number Diff line number Diff line change
Expand Up @@ -311,17 +311,20 @@ def from_powermodels(
]
results = pd.DataFrame(index=timesteps, columns=names, data=data)
if (flex == "gen_nd") & (pm["nw"]["1"]["opf_version"] in [3, 4]):
edisgo_object.timeseries._generators_active_power.loc[:, names] = (
ti = edisgo_object.timeseries.timeindex
edisgo_object.timeseries._generators_active_power.loc[ti, names] = (
edisgo_object.timeseries.generators_active_power.loc[:, names].values
- results[names].values
)
elif flex in ["heatpumps", "electromobility"]:
edisgo_object.timeseries._loads_active_power.loc[:, names] = results[
ti = edisgo_object.timeseries.timeindex
edisgo_object.timeseries._loads_active_power.loc[ti, names] = results[
names
].values
elif flex == "dsm":
edisgo_object.timeseries._loads_active_power.loc[:, names] = (
edisgo_object.timeseries._loads_active_power.loc[:, names].values
ti = edisgo_object.timeseries.timeindex
edisgo_object.timeseries._loads_active_power.loc[ti, names] = (
edisgo_object.timeseries._loads_active_power.loc[ti, names].values
+ results[names].values
)
elif flex == "storage":
Expand All @@ -333,8 +336,9 @@ def from_powermodels(
data=results[names].values,
)
else:
ti = edisgo_object.timeseries.timeindex
edisgo_object.timeseries._storage_units_active_power.loc[
:, names
ti, names
] = results[names].values
except AttributeError:
setattr(
Expand Down Expand Up @@ -366,15 +370,22 @@ def from_powermodels(
# calculate relative error
df2 = deepcopy(df)
for flex in df2.columns:
abs_error = abs(df2[flex].values - hv_flex_dict[flex].values)
rel_error = [
(
if type(hv_flex_dict[flex]) == pd.Series:
abs_error = abs(df2[flex].values - hv_flex_dict[flex].values)
rel_error = [
abs_error[i] / hv_flex_dict[flex].iloc[i]
if ((abs_error > 0.01)[i] & (hv_flex_dict[flex].iloc[i] != 0))
else 0
)
for i in range(len(abs_error))
]
for i in range(len(abs_error))
]
else:
abs_error = abs(df2[flex].values - hv_flex_dict[flex].sum(axis=1).values)
rel_error = [
abs_error[i] / hv_flex_dict[flex].sum(axis=1).iloc[i]
if ((abs_error > 0.01)[i] & (hv_flex_dict[flex].sum(axis=1).iloc[i] != 0))
else 0
for i in range(len(abs_error))
]
df2[flex] = rel_error
# write results to edisgo object
edisgo_object.opf_results.overlying_grid = pd.DataFrame(
Expand Down Expand Up @@ -794,8 +805,8 @@ def _build_branch(edisgo_obj, psa_net, pm, flexible_storage_units, s_base):
# only modify r, x and l values if min value is too small
branches[par] = val.clip(lower=min_value)
logger.warning(
f"Min value of {text} is too small. Lowest {100 * quant}% of {text} values will be set "
f"to {min_value} {unit}"
f"Min value of {text} is too small. Lowest {100 * quant}% of "
f"{text} values will be set to {min_value} {unit}"
)

for branch_i in np.arange(len(branches.index)):
Expand Down Expand Up @@ -940,8 +951,8 @@ def _build_load(
pf, sign = _get_pf(edisgo_obj, pm, idx_bus, "charging_point")
else:
logger.warning(
f"No type specified for load {loads_df.index[load_i]}. Power factor and sign will"
"be set for conventional load."
f"No type specified for load {loads_df.index[load_i]}. "
"Power factor and sign will be set for conventional load."
)
pf, sign = _get_pf(edisgo_obj, pm, idx_bus, "conventional_load")
p_d = psa_net.loads_t.p_set[loads_df.index[load_i]]
Expand Down Expand Up @@ -1018,9 +1029,18 @@ def _build_battery_storage(
"""
branches = pd.concat([psa_net.lines, psa_net.transformers])
if not edisgo_obj.overlying_grid.storage_units_soc.empty:
# Align the SOC series (which may use another year) onto the edisgo
# time index plus one end-of-period step. Uses reindex, so a missing
# step yields NaN instead of a KeyError.
from edisgo.tools.tools import align_series_to_timeindex

soc_aligned = align_series_to_timeindex(
edisgo_obj.overlying_grid.storage_units_soc,
edisgo_obj.timeseries.timeindex,
extra_step=True,
)
data = pd.concat(
[edisgo_obj.overlying_grid.storage_units_soc]
* len(edisgo_obj.topology.storage_units_df),
[soc_aligned] * len(edisgo_obj.topology.storage_units_df),
axis=1,
).values
else:
Expand Down Expand Up @@ -1226,9 +1246,10 @@ def _build_heatpump(psa_net, pm, edisgo_obj, s_base, flexible_hps):
comparison = (heat_df2[hp_p_nom.index] > hp_cop * hp_p_nom.squeeze()).any()
if comparison.any():
logger.warning(
"Heat demand is higher than rated heatpump power"
f" of heatpumps: {comparison.index[comparison.values].values}. Demand can not be covered if no sufficient"
" heat storage capacities are available."
"Heat demand is higher than rated heatpump power of heatpumps: "
f"{comparison.index[comparison.values].values}. "
"Demand can not be covered if no sufficient heat storage "
"capacities are available."
)
for hp_i in np.arange(len(heat_df.index)):
idx_bus = _mapping(psa_net, edisgo_obj, heat_df.bus.iloc[hp_i])
Expand Down Expand Up @@ -1595,11 +1616,18 @@ def _build_hv_requirements(
)

for i in np.arange(len(opf_flex)):
pm["HV_requirements"][str(i + 1)] = {
"P": hv_flex_dict[opf_flex[i]].iloc[0],
"name": opf_flex[i],
"count": count,
}
if type(hv_flex_dict[opf_flex[i]]) == pd.DataFrame:
pm["HV_requirements"][str(i + 1)] = {
"P": hv_flex_dict[opf_flex[i]].sum(axis=1).iloc[0],
"name": opf_flex[i],
"count": count,
}
else:
pm["HV_requirements"][str(i + 1)] = {
"P": hv_flex_dict[opf_flex[i]].iloc[0],
"name": opf_flex[i],
"count": count,
}


def _build_timeseries(
Expand Down Expand Up @@ -1929,9 +1957,14 @@ def _build_component_timeseries(

if (kind == "HV_requirements") & (pm["opf_version"] in [3, 4]):
for i in np.arange(len(opf_flex)):
pm_comp[(str(i + 1))] = {
"P": hv_flex_dict[opf_flex[i]].round(20).tolist(),
}
if type(hv_flex_dict[opf_flex[i]])==pd.DataFrame:
pm_comp[(str(i + 1))] = {
"P": hv_flex_dict[opf_flex[i]].sum(axis=1).round(20).tolist(),
}
else:
pm_comp[(str(i + 1))] = {
"P": hv_flex_dict[opf_flex[i]].round(20).tolist(),
}

pm["time_series"][kind] = pm_comp

Expand Down
14 changes: 14 additions & 0 deletions edisgo/network/overlying_grid.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,17 @@ def __init__(self, **kwargs):
"feedin_district_heating", pd.DataFrame(dtype="float64")
)

self.dispatchable_generators_active_power = kwargs.get(
"dispatchable_generators_active_power", pd.DataFrame(dtype="float64")
)

self.dispatchable_generators_reactive_power = kwargs.get(
"dispatchable_generators_reactive_power", pd.DataFrame(dtype="float64")
)

self.renewables_potential = kwargs.get(
"renewables_potential", pd.Series(dtype="float64")
)
@property
def _attributes(self):
return [
Expand All @@ -108,6 +119,9 @@ def _attributes(self):
"heat_pump_central_active_power",
"thermal_storage_units_central_soc",
"feedin_district_heating",
"dispatchable_generators_active_power",
"dispatchable_generators_reactive_power",
"renewables_potential",
]

def reduce_memory(self, attr_to_reduce=None, to_type="float32"):
Expand Down
31 changes: 31 additions & 0 deletions edisgo/run/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
"""
YAML/JSON-driven pipeline runner for eDisGo.

Two entry points share the same core:

from edisgo.run import run_edisgo
edisgo = run_edisgo("presets/uc2_flex_opf.yaml")

# or, on an existing EDisGo instance:
edisgo = EDisGo(ding0_grid="30879")
edisgo.run_pipeline("my_run.yaml")

Pipelines are lists of named tasks from :mod:`edisgo.run.tasks`. Each step
is either a string (``worst_case_ts``) or a single-key mapping with
parameters (``import_electromobility: {charging_strategy: dumb}``). Tasks
can be grouped into ordered ``stages`` that can save artifacts and reload
them with ``load_from``, enabling two-phase workflows (base reinforce +
per-scenario reinforce).
"""

from edisgo.run.context import RunContext
from edisgo.run.registry import known_tasks, register_task
from edisgo.run.runner import _run_pipeline_on, run_edisgo

__all__ = [
"RunContext",
"_run_pipeline_on",
"known_tasks",
"register_task",
"run_edisgo",
]
Loading
Loading