Phase-wise with multiple lockages
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:
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)