Scenarios

MIKE+Py allows you to programmatically manage scenarios and alternatives within your MIKE+ model database, enabling automation of scenario-based analyses.

import mikeplus as mp

db = mp.open('data/Dyrup_uncalibrated_copy.sqlite')

Concept Overview

MIKE+ uses a system of scenarios and alternatives to manage different model setups and variations.

Scenarios
Represent a complete model configuration for a specific simulation case (e.g., “Current Conditions”, “Future Development with Pipe Upgrade A”). Scenarios can inherit from a parent scenario. Only one scenario may be active at a time.
Alternative Groups
Organize different types of model data that can vary between scenarios (e.g., “CS Network data”). Each group contains one or more alternatives, which can inherit from parent alternatives. Only one alternative may be active at a time within an alternative group.
Alternatives
Represent a specific version of the data within an alternative group (e.g., for “CS Network data”, you might have a “Base Network” alternative and a “Upgraded Pipes” alternative).

For a detailed conceptual understanding of scenarios and alternatives, please refer to the MIKE+ documentation. This section focuses on how to interact with them using MIKE+Py.

Alternative Groups

List the names of all alternative groups. These are the main categories visible from the MIKE+ Scenarios editor.

db.alternative_groups.group_names()
['CS Network data',
 'River network data',
 'Loads and boundaries data',
 'Catchments and hydrology data',
 'Transport data',
 'Control rules data',
 'Long Term Statistics data',
 'Profiles and curves',
 '2D overland',
 '2D boundaries data']

Access a specific alternative group by its name:

network_group = db.alternative_groups["CS Network data"]
network_group
<AlternativeGroup CS Network data>

Each group has a base alternative:

network_group.base
Alternative(CS Network data) <Base Alternative>

And an active alternative (which is part of the active scenario):

network_group.active
Alternative(CS Network data) <Base Alternative>

List the tables associated with an alternative group:

network_group.tables
['msm_Node',
 'msm_Link',
 'msm_Pump',
 'msm_Weir',
 'msm_Orifice',
 'msm_Valve',
 'msm_CurbInlet',
 'msm_OnGrade',
 'msm_OnGradeD']

Alternatives

See all alternatives for the group.

list(network_group)
[Alternative(CS Network data) <Base Alternative>]

Access an alternative within a group by its name:

network_group.by_name("Base Alternative")
Alternative(CS Network data) <Base Alternative>

Create a new alternative. It will be a child of its parent (defaults to the group’s base alternative if not specified):

new_roughness_alt = network_group.create("New Roughness Values", parent=network_group.base)
new_roughness_alt
Alternative(CS Network data) <New Roughness Values>

Alternatives have several properties. For example, change an alternative’s comment:

new_roughness_alt.comment = "Alternative for testing higher roughness."
new_roughness_alt.comment
'Alternative for testing higher roughness.'

Scenarios

Scenarios combine various alternatives to define a complete model state. Access all scenarios in the database:

db.scenarios
ScenarioCollection <1>

Get the currently active scenario:

db.scenarios.active
Scenario <Base>

Get the base scenario:

db.scenarios.base
Scenario <Base>

Access a scenario by its name (returns the first match):

db.scenarios.by_name("Base")
Scenario <Base>

List all scenarios:

list(db.scenarios)
[Scenario <Base>]

Create new scenarios with a name and its parent scenario (defaults to base scenario if not provided)

new_scenario = db.scenarios.create("My Scenario", parent=db.scenarios.base)
list(db.scenarios)
[Scenario <Base>, Scenario <My Scenario>]

Similar to Alternatives, Scenarios have several properties. For example:

new_scenario.comment = "This is a test scenario"

List the alternatives used by a scenario:

new_scenario.alternatives
[Alternative(CS Network data) <Base Alternative>,
 Alternative(River network data) <Base Alternative>,
 Alternative(Loads and boundaries data) <Base Alternative>,
 Alternative(Catchments and hydrology data) <Base Alternative>,
 Alternative(Transport data) <Base Alternative>,
 Alternative(Control rules data) <Base Alternative>,
 Alternative(Long Term Statistics data) <Base Alternative>,
 Alternative(Profiles and curves) <Base Alternative>,
 Alternative(2D overland) <Base Alternative>,
 Alternative(2D boundaries data) <Base Alternative>]

Check if a scenario is active

new_scenario.is_active
False

Activate a scenario. This makes it the current context for data modifications and simulations.

new_scenario.activate()
new_scenario.is_active
True

Set a specific alternative for a scenario. This will replace any existing alternative from the same group.

Let’s use the new_roughness_alt created earlier and assign it to new_scenario.

new_scenario.set_alternative(new_roughness_alt)
list(new_scenario.alternatives)
[Alternative(CS Network data) <New Roughness Values>,
 Alternative(River network data) <Base Alternative>,
 Alternative(Loads and boundaries data) <Base Alternative>,
 Alternative(Catchments and hydrology data) <Base Alternative>,
 Alternative(Transport data) <Base Alternative>,
 Alternative(Control rules data) <Base Alternative>,
 Alternative(Long Term Statistics data) <Base Alternative>,
 Alternative(Profiles and curves) <Base Alternative>,
 Alternative(2D overland) <Base Alternative>,
 Alternative(2D boundaries data) <Base Alternative>]

Delete a scenario. You cannot delete the base scenario.

db.scenarios.delete(new_scenario)
list(db.scenarios)
[Scenario <Base>]

Practical Example

Let’s continue with the catchment examples of the previous section. We would like to test two different scenarios:

  1. Time of concentration = 2000s for all catchments draining to Flow Meter B
  2. Time of concentration = 3000s for all catchments draining to Flow Meter B
Note

Recall that the initial time of concentration for these catchments was 2500 seconds.

First, get a list of the catchment MUIDs again.

selection_name = "Flow_Meter_B_Catchments"
catchment_muids_to_modify = (
    db.tables.m_Selection
        .select(['ItemMUID'])
        .where(f"{db.tables.m_Selection.columns.SelectionID} = {mp.to_sql(selection_name)}")
        .where(f"{db.tables.m_Selection.columns.TableName} = {mp.to_sql(db.tables.msm_Catchment.name)}")
        .to_dataframe()['ItemMUID']
        .tolist()
)
catchment_muids_to_modify[:3] # Show first few MUIDs
['G61F180_7321', 'G62F060_7424', 'G62F070_7425']

Our catchments are in table msm_Catchment, which is part of the Catchments and hydrology data Alternative Group. Let’s create two new alternatives.

alternative_2000 = db.alternative_groups["Catchments and hydrology data"].create("Time of Concentration = 2000s")
alternative_3000 = db.alternative_groups["Catchments and hydrology data"].create("Time of Concentration = 3000s")
list(db.alternative_groups["Catchments and hydrology data"])
[Alternative(Catchments and hydrology data) <Base Alternative>,
 Alternative(Catchments and hydrology data) <Time of Concentration = 2000s>,
 Alternative(Catchments and hydrology data) <Time of Concentration = 3000s>]

Let’s create two new scenarios, and apply the alternatives to them.

scenario_2000 = db.scenarios.create("Time of Concentration = 2000s")
scenario_2000.set_alternative(alternative_2000)

scenario_3000 = db.scenarios.create("Time of Concentration = 3000s")
scenario_3000.set_alternative(alternative_3000)

list(db.scenarios)
[Scenario <Base>,
 Scenario <Time of Concentration = 2000s>,
 Scenario <Time of Concentration = 3000s>]

Now let’s activate each scenario and make changes to the new alternative.

scenario_2000.activate()
updated = (
    db.tables.msm_Catchment
        .update({
            db.tables.msm_Catchment.columns.ModelAConcTime : 2000
        })
        .by_muid(catchment_muids_to_modify)
        .execute()
)
len(updated)
32
scenario_3000.activate()
updated = (
    db.tables.msm_Catchment
        .update({
            db.tables.msm_Catchment.columns.ModelAConcTime : 3000
        })
        .by_muid(catchment_muids_to_modify)
        .execute()
)
len(updated)
32

Now let’s verify by activating each scenario and checking time of concentration values.

import pandas as pd

# Check base scenario
db.scenarios.base.activate()
df_base = (
    db.tables.msm_Catchment
        .select([db.tables.msm_Catchment.columns.ModelAConcTime])
        .by_muid(catchment_muids_to_modify)
        .to_dataframe()
)
df_base.columns = ["Base Tc"]

# Check Tc = 2000
db.scenarios.by_name("Time of Concentration = 2000s").activate()
df_2000 = (
    db.tables.msm_Catchment
        .select([db.tables.msm_Catchment.columns.ModelAConcTime])
        .by_muid(catchment_muids_to_modify)
        .to_dataframe()
)
df_2000.columns = ["Tc = 2000s"]

# Check Tc = 3000
db.scenarios.by_name("Time of Concentration = 3000s").activate()
df_3000 = (
    db.tables.msm_Catchment
        .select([db.tables.msm_Catchment.columns.ModelAConcTime])
        .by_muid(catchment_muids_to_modify)
        .to_dataframe()
)
df_3000.columns = ["Tc = 3000s"]

# Concatenate into common DataFrame for comparison
df_compare = pd.concat([df_base, df_2000, df_3000], axis=1)
df_compare.describe()
Base Tc Tc = 2000s Tc = 3000s
count 32.0 32.0 32.0
mean 2500.0 2000.0 3000.0
std 0.0 0.0 0.0
min 2500.0 2000.0 3000.0
25% 2500.0 2000.0 3000.0
50% 2500.0 2000.0 3000.0
75% 2500.0 2000.0 3000.0
max 2500.0 2000.0 3000.0


This section demonstrated how to view, create, and manage scenarios and alternatives. In the next section, you’ll learn how to run MIKE+ simulations.