import mikeplus as mp
# Open the copied database
db = mp.open("data/Dyrup_uncalibrated_copy.sqlite")Modifying Data
The previous section focused on reading data from your MIKE+ model database. This section delves into modifying that data programmatically using MIKE+Py. You’ll learn how to perform UPDATE, INSERT, and DELETE operations, enabling you to automate changes to your model setup, parameters, and even create new model elements.
Unlike with MIKE+, there is no ‘undo’ button when making changes programmatically with MIKE+Py. Once a change is executed, it is permanent. Always work on a copy of your MIKE+ .sqlite database to ensure original work remains intact.
First, let’s open our copied MIKE+ project database. We’ll use the db object for all subsequent examples in this section.
Update Operations
The update() method on a table object allows you to change existing data in your MIKE+ database. It creates an UpdateQuery object, which you then refine with conditions (using where() or by_muid()) and the new values. To apply the changes to the database, you must call execute() on the query object.
The execute() method for an update query returns a list of MUIDs for the rows that were updated.
Updating Specific Rows
Let’s update the diameter of two specific pipes in the msm_Link table.
First, we can inspect their current diameters:
(
db.tables.msm_Link
.select(['MUID', 'Diameter'])
.by_muid(['G60F380_G60F360_l1', 'G62F070_G62F060_l1'])
.to_dataframe()
)| MUID | Diameter | |
|---|---|---|
| G60F380_G60F360_l1 | G60F380_G60F360_l1 | 0.8 |
| G62F070_G62F060_l1 | G62F070_G62F060_l1 | 0.55 |
Now, update their diameters. For example, set pipe G60F380_G60F360_l1 and G62F070_G62F060_l1 to 0.6m. The argument passed to update() is a Python dictionary with keys of column names and values matching the desired updated value.
(
db.tables.msm_Link
.update({
db.tables.msm_Link.columns.Diameter : 0.4
})
.by_muid(['G60F380_G60F360_l1', 'G62F070_G62F060_l1'])
.execute() # don't forget this, or nothing happens
)['G60F380_G60F360_l1', 'G62F070_G62F060_l1']
Notice the update() returned MUIDs of the updated rows. Verify the changes:
(
db.tables.msm_Link
.select(['MUID', 'Diameter'])
.by_muid(['G60F380_G60F360_l1', 'G62F070_G62F060_l1'])
.to_dataframe()
)| MUID | Diameter | |
|---|---|---|
| G60F380_G60F360_l1 | G60F380_G60F360_l1 | 0.4 |
| G62F070_G62F060_l1 | G62F070_G62F060_l1 | 0.4 |
Use the return value of update() to confirm the result is what you intended. A common error is to forget calling execute().
Updating All Rows in a Table
You can update all rows in a table by calling all() before execute(). Be extremely careful with this operation.
Let’s update the Description for all nodes in the msm_Node table.
updated_muids = (
db.tables.msm_Node
.update({
'Description': 'Hello from MIKE+Py'
})
.all().execute()
)
len(updated_muids)143
You can verify this change by selecting a few rows or opening the model in MIKE+.
Insert Operations
The insert() method is used to add new rows to a table. You provide a dictionary where keys are column names and values are the data to be inserted. The same default values as MIKE+ are used if a column value is not specified.
By default, insert() executes immediately and returns the MUID of the newly inserted row. If you set execute=False, it returns an InsertQuery object, which you would then need to call execute() on.
MIKE+Py will typically auto-generate an MUID if one is not provided in the values dictionary.
Currently, the insert() method via MIKE+Py is best suited for inserting single rows at a time. For bulk inserts, you need to loop through your data and call insert() for each row.
Inserting a New Simulation Setup
Let’s insert a new simulation setup into the msm_Project table. We’ll define a new MUID and ScenarioName, and set some essential parameters.
db.tables.msm_Project.insert({
db.tables.msm_Project.columns.MUID : 'My_Simulation',
db.tables.msm_Project.columns.Description : 'Simulation setup created by MIKE+Py',
db.tables.msm_Project.columns.Enable_Catchment : 1,
db.tables.msm_Project.columns.Enable_CS : 1,
db.tables.msm_Project.columns.Enable_RR : 1,
db.tables.msm_Project.columns.Enable_HD : 1,
db.tables.msm_Project.columns.Enable_LTS : 1,
})'My_Simulation'
Notice the MUID of the inserted row is returned.
Delete Operations
The delete() method removes rows from a table. Similar to update(), it creates a DeleteQuery. You must specify which rows to delete using where() or by_muid(), or use all() to delete all rows (with extreme caution). Call execute() to perform the deletion.
The execute() method for a delete query returns a list of MUIDs for the rows that were deleted.
Deleting a Specific Row
Let’s delete the simulation setup we just created using its MUID.
(
db.tables.msm_Project
.delete()
.by_muid('My_Simulation')
.execute()
)['My_Simulation']
Deleting Rows with a Filter
You can delete multiple rows that match a specific condition. For instance, to delete all pipes in msm_Link with a Diameter less than 0.2m:
deleted_pipes = (
db.tables.msm_Link
.delete()
.where(f"{db.tables.msm_Link.columns.Diameter} < {mp.to_sql(0.2)}")
.execute()
)
len(deleted_pipes)6
Deleting All Rows in a Table
To delete all rows from a table, use all().execute(). This is a very destructive operation. For example, to delete all simulation setups from msm_Project (use with extreme caution):
db.tables.msm_Project.delete().all().execute()['rainfall']
Practical Example
In the previous section, we identified how to get MUIDs for catchments belonging to a specific selection. Let’s use that to modify the ‘Time of Concentration’ (ModelAConcTime) for all catchments in the “Flow_Meter_B_Catchments” selection, increasing it by 10%.
First, let’s 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']
Next, let’s review the initial times of concentration.
df_toc = (
db.tables.msm_Catchment
.select(['ModelAConcTime'])
.by_muid(catchment_muids_to_modify)
.to_dataframe()
)
df_toc.describe()| ModelAConcTime | |
|---|---|
| count | 32.0 |
| mean | 2500.0 |
| std | 0.0 |
| min | 2500.0 |
| 25% | 2500.0 |
| 50% | 2500.0 |
| 75% | 2500.0 |
| max | 2500.0 |
Now let’s calculate new times of concentration.
df_toc = df_toc * 1.1
df_toc.describe()| ModelAConcTime | |
|---|---|
| count | 32.0 |
| mean | 2750.0 |
| std | 0.0 |
| min | 2750.0 |
| 25% | 2750.0 |
| 50% | 2750.0 |
| 75% | 2750.0 |
| max | 2750.0 |
Finally, we’ll update each catchment with its new time of concentration.
updated_count = 0
for muid, row in df_toc.iterrows():
updated_count += len(
db.tables.msm_Catchment
.update({'ModelAConcTime': row['ModelAConcTime']})
.by_muid(muid)
.execute()
)
print(f"Updated Time of Concentration for {updated_count} catchments.")Updated Time of Concentration for 32 catchments.
Verify the changes.
(
db.tables.msm_Catchment
.select(['ModelAConcTime'])
.by_muid(catchment_muids_to_modify)
.to_dataframe()
.describe()
)| ModelAConcTime | |
|---|---|
| count | 32.0 |
| mean | 2750.0 |
| std | 0.0 |
| min | 2750.0 |
| 25% | 2750.0 |
| 50% | 2750.0 |
| 75% | 2750.0 |
| max | 2750.0 |
This practical example demonstrates a common workflow: selecting data, performing calculations or logic in Python (often with Pandas), writing the modified data back to the MIKE+ database, and verifying everything went okay.