Phase-wise with multiple lockages

../../_images/Krammersluizen.jpg

Overview

Note

This example focuses on performing a phase-wise calculation of many lockages based on an input file. It assumes basic exposure to the Python interface and phase-wise calculation. If you are a first-time user of the ZSF, see the Steady-state calculation and Phase-wise calculation examples.

The purpose of this example is to understand the basic steps to perform a phase-wise calculation of many lockages. The goal is to determine the average transports of water and salt over a 60-day period.

Initializing the lock

When initializing the lock with pyzsf.ZSFUnsteady we pass all parameters that stay constant throughout the 60-day period. This includes the parameters for salt intrusion measures like the bubble screens. Note that the head on the lake side is constant, but that the head on the sea side varies. Furthermore, the salinity on both sides of the lock varies over time as well.

 8lock_parameters = {
 9    "lock_length": 300.0,
10    "lock_width": 25.0,
11    "lock_bottom": -7.0,
12}
13
14constant_boundary_conditions = {
15    "head_lake": 0.0,
16    "temperature_lake": 15.0,
17    "temperature_sea": 15.0,
18}
19
20mitigation_parameters = {
21    "density_current_factor_lake": 0.25,
22    "density_current_factor_sea": 0.25,
23    "distance_door_bubble_screen_lake": 10.0,
24    "distance_door_bubble_screen_sea": 10.0,
25    "flushing_discharge_high_tide": 0.0,
26    "flushing_discharge_low_tide": 0.0,
27    "sill_height_lake": 0.5,
28}
29
30# Initialize the lock
31z = pyzsf.ZSFUnsteady(

Reading the input data

The lockages over the 60-day period are defined in a CSV-file:

Lockages

time

head_sea

routine

salinity_lake

salinity_sea

ship_volume_lake_to_sea

ship_volume_sea_to_lake

t_flushing

t_level

t_open_lake

t_open_sea

2960.0

0.03760156993333333

3

0.8554550242857143

28.529520582857142

300.0

3380.0

0.03760156993333333

4

0.8554550242857143

28.529520582857142

1884.2

420.0

3920.0

0.181197112

1

0.8979030931428571

28.558444978571426

240.0

4280.0

0.181197112

2

0.8979030931428571

28.558444978571426

2482.0

1020.0

5420.0

0.74484631

3

1.023605347

28.62057304

340.0

We read this CSV file using pandas, and convert it to a list of parameter dictionaries:

33)
34
35# Read the lockages from a file

Stepping through all lockages

Just like in the Phase-wise calculation example we step through all phases in the locking cycle. We do this by iterating over all locking-phase entries defined in the input CSV file. Depending on the respective phase, we pass either the leveling time, the door-open duration, or the duration of flushing (with the doors closed).

We store the results of every individual locking phase in a list called all_results, to be aggregated later on in the script:

37lockages = list(df_lockages.to_dict("records"))
38
39# Go through all lockages
40all_results = []
41
42for parameters in lockages:
43    routine = int(parameters.pop("routine"))
44    t_open_lake = parameters.pop("t_open_lake")
45    t_open_sea = parameters.pop("t_open_sea")
46    t_level = parameters.pop("t_level")
47    t_flushing = parameters.pop("t_flushing")
48
49    parameters["ship_volume_sea_to_lake"] = 0.0
50    parameters["ship_volume_lake_to_sea"] = 0.0
51
52    if routine == 1:
53        assert t_level > 0
54        results = z.step_phase_1(t_level, **parameters)
55    elif routine == 2:
56        results = z.step_phase_2(t_open_lake, **parameters)
57    elif routine == 3:
58        assert t_level > 0
59        results = z.step_phase_3(t_level, **parameters)
60    elif routine == 4:
61        results = z.step_phase_4(t_open_sea, **parameters)
62    elif routine in {-2, -4}:
63        results = z.step_flush_doors_closed(t_flushing, **parameters)
64    else:
65        raise Exception(f"Unknown routine '{routine}'")

Aggregating output

For many cases we would only be interested in what happens on the lake side, e.g. the average salt flux in kg/s over a certain period of time. For illustrative purposes we however aggregate all outputs. For volumes and mass transports this means summing them. For discharges and salt fluxes, this means averaging them.

67    all_results.append(results)
68
69# Aggregate results
70duration = 60 * 24 * 3600  # 60 days
71
72overall_results = {}
73overall_mass_to_sea = 0.0
74overall_mass_to_lake = 0.0
75
76for results in all_results:
77    for k, v in results.items():
78        if k.startswith(("volume_", "mass_")):
79            overall_results[k] = overall_results.get(k, 0.0) + v
80
81    overall_mass_to_sea += results["volume_to_sea"] * results["salinity_to_sea"]
82    overall_mass_to_lake += results["volume_to_lake"] * results["salinity_to_lake"]
83
84overall_results["salinity_to_sea"] = overall_mass_to_sea / overall_results["volume_to_sea"]
85overall_results["salinity_to_lake"] = overall_mass_to_lake / overall_results["volume_to_lake"]
86
87overall_discharges = {}
88for k, v in overall_results.items():
89    if k.startswith("volume_"):
90        overall_discharges[f"discharge_{k[7:]}"] = v / duration
91overall_results.update(overall_discharges)

Finally, the average fluxes are logged to the console

93assert overall_results.keys() == all_results[0].keys()
94
95# Log to console

The output should show something like:

Overall results (60 day aggregates and averages):
{'discharge_from_lake': 2.626938120554457,
 'discharge_from_sea': 2.9855208663562096,
 'discharge_to_lake': 2.7409909136666557,
 'discharge_to_sea': 2.872713264556223,
 'mass_transport_lake': -215507325.24071646,
 'mass_transport_sea': -215288300.05404428,
 'salinity_to_lake': 16.334431315958305,
 'salinity_to_sea': 13.297877648040927,
 'volume_from_lake': 13618047.216954306,
 'volume_from_sea': 15476940.17119059,
 'volume_to_lake': 14209296.896447944,
 'volume_to_sea': 14892145.563459458}

The whole script

All together, the whole example script is as follows:

 1import pprint
 2
 3import pandas as pd
 4
 5import pyzsf
 6
 7
 8lock_parameters = {
 9    "lock_length": 300.0,
10    "lock_width": 25.0,
11    "lock_bottom": -7.0,
12}
13
14constant_boundary_conditions = {
15    "head_lake": 0.0,
16    "temperature_lake": 15.0,
17    "temperature_sea": 15.0,
18}
19
20mitigation_parameters = {
21    "density_current_factor_lake": 0.25,
22    "density_current_factor_sea": 0.25,
23    "distance_door_bubble_screen_lake": 10.0,
24    "distance_door_bubble_screen_sea": 10.0,
25    "flushing_discharge_high_tide": 0.0,
26    "flushing_discharge_low_tide": 0.0,
27    "sill_height_lake": 0.5,
28}
29
30# Initialize the lock
31z = pyzsf.ZSFUnsteady(
32    15.0, 0.0, **lock_parameters, **constant_boundary_conditions, **mitigation_parameters
33)
34
35# Read the lockages from a file
36df_lockages = pd.read_csv("lockages.csv", index_col=0)
37lockages = list(df_lockages.to_dict("records"))
38
39# Go through all lockages
40all_results = []
41
42for parameters in lockages:
43    routine = int(parameters.pop("routine"))
44    t_open_lake = parameters.pop("t_open_lake")
45    t_open_sea = parameters.pop("t_open_sea")
46    t_level = parameters.pop("t_level")
47    t_flushing = parameters.pop("t_flushing")
48
49    parameters["ship_volume_sea_to_lake"] = 0.0
50    parameters["ship_volume_lake_to_sea"] = 0.0
51
52    if routine == 1:
53        assert t_level > 0
54        results = z.step_phase_1(t_level, **parameters)
55    elif routine == 2:
56        results = z.step_phase_2(t_open_lake, **parameters)
57    elif routine == 3:
58        assert t_level > 0
59        results = z.step_phase_3(t_level, **parameters)
60    elif routine == 4:
61        results = z.step_phase_4(t_open_sea, **parameters)
62    elif routine in {-2, -4}:
63        results = z.step_flush_doors_closed(t_flushing, **parameters)
64    else:
65        raise Exception(f"Unknown routine '{routine}'")
66
67    all_results.append(results)
68
69# Aggregate results
70duration = 60 * 24 * 3600  # 60 days
71
72overall_results = {}
73overall_mass_to_sea = 0.0
74overall_mass_to_lake = 0.0
75
76for results in all_results:
77    for k, v in results.items():
78        if k.startswith(("volume_", "mass_")):
79            overall_results[k] = overall_results.get(k, 0.0) + v
80
81    overall_mass_to_sea += results["volume_to_sea"] * results["salinity_to_sea"]
82    overall_mass_to_lake += results["volume_to_lake"] * results["salinity_to_lake"]
83
84overall_results["salinity_to_sea"] = overall_mass_to_sea / overall_results["volume_to_sea"]
85overall_results["salinity_to_lake"] = overall_mass_to_lake / overall_results["volume_to_lake"]
86
87overall_discharges = {}
88for k, v in overall_results.items():
89    if k.startswith("volume_"):
90        overall_discharges[f"discharge_{k[7:]}"] = v / duration
91overall_results.update(overall_discharges)
92
93assert overall_results.keys() == all_results[0].keys()
94
95# Log to console
96print("Overall results (60 day aggregates and averages):")
97pprint.pprint(overall_results)