Inline MIDAS Scenarios¶
MIDAS is a mosaik-based co-simulation framework for energy grids. Historically, a palaestrAI MIDAS environment referenced a scenario by name and loaded its definition from a file on disk. This document describes the inline MIDAS scenario provider, which embeds the complete scenario inside the run configuration, together with how a MIDAS environment proposes its meta-loop definition interface.
In the current version, the scripted MIDAS example makes one initial
scenario change: the meta agent acts once in episode_start before the
world is created, and the validated candidate is used to start the operating
environment. The operating return is handed back to the meta agent as a
terminal reward/update, but in-place episode resets do not trigger another
scenario decision. A repeated meta-learning loop that adapts the scenario
across episodes is not currently implemented and would require additional
orchestration. See Scripted Simulation Controller and the Definition Meta-Loop for the generic
controller behavior and its current limitations.
Configuration¶
The inline provider is selected with scenario_provider: inline inside the
MIDAS environment’s params, together with a nested scenario mapping:
params:
module: midas_palaestrai:Descriptor
description_func: describe
instance_func: get_world
params:
scenario_provider: inline
config: tests/fixtures/this-file-must-not-exist.yml # ignored
scenario:
name: carl_cigre_base
modules: [store, powergrid]
step_size: 1
start_date: "2020-04-13 12:00:00+0100"
end: 10
with_arl: true
powergrid_params: { ... }
store_params: { ... }
Resolution¶
The MIDAS Descriptor (see Cross-Repository References) detects
scenario_provider == "inline" and resolves the nested mapping through
midas.scenario.configurator.Configurator.load_from_mapping, which owns
subtle behaviour such as parent-scenario merging and unsupported-key handling.
The resolved scenario and its normalised parameters are kept together, and the
resolved_mapping is exposed as ProviderProvenance.
Crucially, inline mode ignores the config path and never falls back to
file mode. A run that accidentally relies on the file path fails, which is
used as an explicit regression signal in the integration test.
The definition override block¶
The Mosaik environment proposes its meta-loop interface from the inline
scenario mapping without constructing the MIDAS descriptor or world. By
default, every finite int/float leaf is exposed, recursively through mappings
and nested lists, not only constant_* leaves (see
Scripted Simulation Controller and the Definition Meta-Loop). Booleans are excluded. The
definition block inside the environment’s inner params optionally
filters discovery or replaces selector metadata:
params:
scenario_provider: inline
scenario: { ... }
definition:
selector_manifest:
- alias: constant_load_p_mw
json_pointer: /powergrid_params/midasmv/grid_params/constant_load_p_mw
space: "Box(low=0.0, high=2.0, shape=(), dtype=np.float64)"
normalization: {center: 0.7, scale: 0.7}
Each manifest entry names:
aliasA short identifier (must be unique, non-empty).
json_pointerAn RFC 6901 JSON Pointer resolved against the scenario mapping.
spaceA run-file string parsed by
palaestrai.types.Space.from_string. Only scalarBoxandDiscretespaces are supported.normalizationAn optional fixed
{center, scale}policy, required for unbounded spaces and used when vectorizing values for the meta agent.
Every manifest selector is exposed as both a sensor and an actuator with
the stable UID <environment-uid>.definition.<alias>. The provider
validates selector aliases, pointers, spaces, and normalisation, and rejects
patches whose pointers are not declared, whose revision or content hash do not
match, or whose application would change the descriptor interface
fingerprint.
Worked Example¶
The repository ships a complete, self-contained example as
tests/fixtures/scripted_inline_midas_meta_loop_run.yml. Its essential
parts, annotated:
schedule:
- scripted_meta_loop:
environments:
- environment:
uid: midas
name: palaestrai_mosaik:MosaikEnvironment
params:
module: midas_palaestrai:Descriptor
description_func: describe
instance_func: get_world
params:
scenario_provider: inline # (1) inline provider
scenario:
name: carl_cigre_base
modules: [store, powergrid]
powergrid_params: { ... } # selector targets
agents:
- name: meta_agent # (2) definition interface
sensors: [midas.definition.constant_load_p_mw]
actuators: [midas.definition.constant_load_p_mw]
- name: operating_agent # (3) environment interface
sensors: [midas.Powergrid-0.0-bus-1.vm_pu, ...]
actuators: [midas.Powergrid-0.0-sgen-0-1.p_mw, ...]
simulation:
name: palaestrai.simulation:ScriptedSimulationController
params:
script:
episode_start: # (4) meta turn
- agents: [meta_agent]
environments: [midas]
loop: # (5) operating turn
- agents: [operating_agent]
environments: [midas]
episode_end: []
The MIDAS environment uses the inline provider; its
scenariomapping is the definition the meta agent will operate on.meta_agentis declared against the definition interface (midas.definition.*), selected by being listed inepisode_start.operating_agentis declared against the real environment interface (midas.Powergrid-*), selected by being listed inloop.The meta agent acts once before the world exists; its action becomes a validated definition patch for this initial scenario change. An in-place reset does not run another meta action.
The operating agent runs the normal act/step loop against the world built from the validated definition. Its terminal reward is handed to the meta agent, but that update does not schedule another definition decision.
The environment discovers
constant_load_p_mwautomatically. The fixture usesdefinition.include: ['^constant_load_p_mw$']to keep this small dummy-agent integration run focused on one parameter; it has no manifest.
Nested load and PV scales¶
The fuller example is tests/fixtures/Train_meta-loop_simple_change.yml,
mirroring midas-palaestrai/tests/unit/fixtures/Train_meta-loop_simple_change.yml.
Its TD3 meta agent observes and controls the nested numeric load/PV scales.
The relevant environment inner params and meta-agent assignments are:
params:
scenario_provider: inline
scenario:
# Other required scenario parameters are in the complete fixture.
comdata_params:
midasmv:
active_mapping:
3: [[Hospital, 0.427]]
4: [[LargeHotel, 0.913]]
der_params:
midasmv:
mapping:
6: [[PV, 2.2]]
definition:
# Optional: focus training on load/PV scales, not timing or topology.
include:
- '^/comdata_params/midasmv/active_mapping/(3|4)/0/1$'
- '^/der_params/midasmv/mapping/6/0/1$'
# In the meta_agent declaration:
sensors:
- midas.definition./comdata_params/midasmv/active_mapping/3/0/1
- midas.definition./comdata_params/midasmv/active_mapping/4/0/1
- midas.definition./der_params/midasmv/mapping/6/0/1
actuators:
- midas.definition./comdata_params/midasmv/active_mapping/3/0/1
- midas.definition./comdata_params/midasmv/active_mapping/4/0/1
- midas.definition./der_params/midasmv/mapping/6/0/1
No aliases or pointers are hand-declared in a selector manifest. Discovery
walks the integer bus key, outer list index 0, and inner list index 1
to reach each numeric value. The path is its alias, so the period immediately
before the leading slash in each UID is intentional. Hospital,
LargeHotel, and PV are strings, not exposed numeric selectors.
Using the same UID-valued actions as the meta agent, the provider path is:
from palaestrai_mosaik.definition import propose_mosaik_definition
from palaestrai.simulation.environment_definition import DefinitionProvider
# inner_params is the fixture's environment.params.params mapping.
proposal = propose_mosaik_definition(inner_params, "midas")
provider = DefinitionProvider(proposal)
baseline = {"midas": proposal.scenario}
readings = provider.vectorize(baseline, None)
assert tuple(readings.values) == (0.427, 0.913, 2.2)
actions = {
"midas.definition./comdata_params/midasmv/active_mapping/3/0/1": 0.5,
"midas.definition./comdata_params/midasmv/active_mapping/4/0/1": 1.0,
"midas.definition./der_params/midasmv/mapping/6/0/1": 2.4,
}
patch = provider.propose_patch(baseline, actions)
candidate = provider.validate(baseline, patch)
changed = candidate.definition["midas"]
assert changed["comdata_params"]["midasmv"]["active_mapping"][3][0][1] == 0.5
assert changed["comdata_params"]["midasmv"]["active_mapping"][4][0][1] == 1.0
assert changed["der_params"]["midasmv"]["mapping"][6][0][1] == 2.4
The scripted controller follows that same provider path after receiving the
meta agent’s ordinary action response. It copies candidate.definition’s
scenario into the request-scoped setup override. The MIDAS inline provider
then resolves that changed mapping when the operating environment is
started, rather than the original values. Provider validation checks declared
spaces, finite values, revision/hash, and interface consistency; it is not a
power-grid feasibility test.
Removing definition.include exposes all finite numeric scenario leaves,
including integer step_size/end, other load/PV scales, and numeric
entries in weather_provider_mapping. Default integer selectors still use
continuous Box spaces; filter structural/timing values out if training must
not alter them. The meta agent receives the complete proposed interface, not
a subset determined by its sensors/actuators lists. Use the optional
filters to control that interface, or a manifest to override individual
selectors’ bounds, aliases, and discrete choices.
A companion fixture, tests/fixtures/simple-end-to-end-meta-loop.yml,
demonstrates the same flow against a synthetic environment without MIDAS.
Cross-Repository References¶
The one-shot definition step and inline MIDAS support span several repositories. This is not a repeated meta-learning loop: adapting the scenario across episodes is not currently implemented and would require additional orchestration. The relevant symbols are referenced from run files by module path, so they must be installed alongside palaestrAI.
- midas-palaestrai
The MIDAS-to-palaestrAI bridge. Provides:
midas_palaestrai.Descriptor— themoduleused bypalaestrai_mosaik.MosaikEnvironment. Implementsdescribeand (async)get_world, and resolves theinlinescenario provider viamidas.scenario.configurator.Configurator.load_from_mapping. RecordsProviderProvenancedescribing the provider, scenario name, resolution path, input keys, and ignored keys.reward/objective classes such as
midas_palaestrai.rewards.GridHealthRewardused by the operating agent.
This package imports MIDAS lazily: the descriptor fails with a clear
ImportErrorat construction if the optionalmidasdependency is not installed.- palaestrai-mosaik
The mosaik co-simulation interface. Provides
palaestrai_mosaik.MosaikEnvironment(name: palaestrai_mosaik:MosaikEnvironmentin run files), which wraps a user-suppliedmodule/description_func/instance_functriple — here the MIDAS descriptor — as a palaestrAI environment. Its config-onlypropose_definitioncallspalaestrai_mosaik.definition.propose_mosaik_definition, which uses palaestrAI’sderive_selectorson the inline scenario. The descriptor is only used later for the real environment interface/world setup.- midas
The co-simulation scenario engine itself. Provides
midas.scenario.configurator.Configurator.load_from_mapping, which the inline provider uses to turn a scenario mapping into a configured, normalised scenario, and theScenario/world objects operated byDescriptor.
The integration test tests/integration/test_scripted_inline_midas_meta_loop.robot
walks this full stack — real worker processes and a real mosaik/MIDAS world —
asserting a zero exit, no traceback, and a completed run artifact, thereby
proving the inline provider, controller, and environment-proposed interface
are all activated by a single run file.