Basic tunnel modelling#

This example is available in the following formats.

#Necessary imports used in all examples
import pandas as pd
from pyshop import ShopSession
pd.options.plotting.backend = "plotly"

#Functions used in this example for building a tunnel model, adding a gate to a tunnel and running the optimization
from tunnel_model import build_model, add_gate, run_model

We create a system with three reservoirs, three tunnels and one plant. In the first model, shop_default, there are no gates to optimize, and the flow simply follows the physical laws of the tunnel system. Both the start level and the inflow in Reservoir3, the closest reservoir to the plant, is higher than in the other reservoirs. This means, that some of this extra water in Reservoir3 will naturally flow into the other reservoirs.

#Create a standard ShopSession
shop_default=ShopSession()
#Build a simple tunnel model without tunnel gates by calling function "build_model" in tunnel_model.py
build_model(shop_default)
#Optimize model by calling "run_model" in tunnel_model.py
run_model(shop_default)
#Display topology to the screen
display(shop_default.model.build_connection_tree())
#Display results for reservoir levels
pd.concat([obj.head.get().rename(obj.get_name()) for obj in shop_default.model.reservoir], axis=1).plot(title="Reservoir level")
#Display results for tunnel flows
pd.concat([obj.flow.get().rename(obj.get_name()) for obj in shop_default.model.tunnel], axis=1).plot(title="Tunnel flow")
../../_images/29bdd20b1a11ab267b4512fe591b0d7c2580751137ac908a5264440b76094bbf.svg

The second model is allowed to adjust the gate opening in the tunnel between Reservoir2 and Reservoir3. As we see in the results, SHOP is able to keep more water in Reservoir3, and thus get more energy from the plant, by closing this gate for most of the period. Only at the end of the period it is partially opened to prevent spillage from Reservoir3.

#Create a standard ShopSession
shop_optimized=ShopSession()
#Build a simple tunnel model without gates by calling function "build_model" in tunnel_model.py
build_model(shop_optimized)
#Add a gate between Reservoir2 and Reservoir3 by calling function "add_gate" in tunnel_model.py
add_gate(shop_optimized)
#Optimize model by calling "run_model" in tunnel_model.py
run_model(shop_optimized)
#Display results for reservoir levels
pd.concat([obj.head.get().rename(obj.get_name()) for obj in shop_optimized.model.reservoir], axis=1).plot(title="Reservoir level")
#Display results for tunnel flows
pd.concat([obj.flow.get().rename(obj.get_name()) for obj in shop_optimized.model.tunnel], axis=1).plot(title="Tunnel flow")
#Display results for tunnel opening
pd.concat([obj.gate_opening.get().rename(obj.get_name()) for obj in shop_optimized.model.tunnel], axis=1).plot(title="Gate opening")

#Compare objective functions to see improvement in result from optimizing the gate
default_objective=shop_default.model.objective["average_objective"].grand_total.get()
optimized_objective=shop_optimized.model.objective["average_objective"].grand_total.get()
print(f"Optimization of the gate in Tunnel2 improved the objective with {-(optimized_objective-default_objective):.2f}€")
Optimization of the gate in Tunnel2 improved the objective with 550.35€

File contents#

tunnel_model.py #

import pandas as pd

def build_model(shop):
    starttime=pd.Timestamp("2018-02-27 00:00:00")
    endtime=pd.Timestamp("2018-02-27 06:00:00")
    shop.set_time_resolution(starttime=starttime, endtime=endtime, timeunit="hour")

    rsv1=shop.model.reservoir.add_object("Reservoir1")
    rsv1.hrl.set(100)
    rsv1.lrl.set(90)
    rsv1.max_vol.set(5)
    rsv1.vol_head.set(pd.Series([90,100,101],index=[0,5,6]))
    rsv1.flow_descr.set(pd.Series([0, 1000], index=[100, 101]))

    rsv2=shop.model.reservoir.add_object("Reservoir2")
    rsv2.hrl.set(100)
    rsv2.lrl.set(90)
    rsv2.max_vol.set(5)
    rsv2.vol_head.set(pd.Series([90,100,101],index=[0,5,6]))
    rsv2.flow_descr.set(pd.Series([0, 1000], index=[100, 101]))

    rsv3=shop.model.reservoir.add_object("Reservoir3")
    rsv3.hrl.set(100)
    rsv3.lrl.set(90)
    rsv3.max_vol.set(5)
    rsv3.vol_head.set(pd.Series([90,100,101],index=[0,5,6]))
    rsv3.flow_descr.set(pd.Series([0, 1000], index=[100, 101]))

    plant1=shop.model.plant.add_object("Plant1")
    plant1.main_loss.set([0.0002])
    plant1.penstock_loss.set([0.0001])

    p1g1=shop.model.generator.add_object("P1G1")
    p1g1.connect_to(plant1)
    p1g1.penstock.set(1)
    p1g1.p_min.set(0.1)
    p1g1.p_nom.set(100)
    p1g1.p_max.set(100)
    p1g1.gen_eff_curve.set(pd.Series([95,98], index=[0,100]))
    p1g1.turb_eff_curves.set([pd.Series([80,95,90],index=[1,90,100],name=90),pd.Series([82,98,92],index=[1,90,100],name=100)])

    tunnel1=shop.model.tunnel.add_object("Tunnel1")
    tunnel1.loss_factor.set(0.00016)
    tunnel1.start_height.set(90)
    tunnel1.end_height.set(90)
    tunnel1.diameter.set(3)
    tunnel1.length.set(2022)
    
    tunnel2=shop.model.tunnel.add_object("Tunnel2")
    tunnel2.loss_factor.set(0.00015)
    tunnel2.start_height.set(90)
    tunnel2.end_height.set(90)
    tunnel2.diameter.set(3)
    tunnel2.length.set(2022)

    tunnel3=shop.model.tunnel.add_object("Tunnel3")
    tunnel3.loss_factor.set(0.00030)
    tunnel3.start_height.set(90)
    tunnel3.end_height.set(90)
    tunnel3.diameter.set(3)
    tunnel3.length.set(2022)

    rsv1.connect_to(tunnel1)
    tunnel1.connect_to(rsv2)
    rsv2.connect_to(tunnel2)
    tunnel2.connect_to(rsv3)
    rsv3.connect_to(tunnel3)
    tunnel3.connect_to(plant1)

    rsv1.start_head.set(93)
    rsv2.start_head.set(93)
    rsv3.start_head.set(97)

    rsv3.inflow.set(200)

    rsv1.energy_value_input.set(31.7)
    rsv2.energy_value_input.set(31.7)
    rsv3.energy_value_input.set(31.7)

    da=shop.model.market.add_object('1')
    da.sale_price.set(39.99)
    da.buy_price.set(40.01)
    da.max_sale.set(9999)
    da.max_buy.set(9999)
    
def add_gate(shop):
    tunnel2=shop.model.tunnel["Tunnel2"]
    tunnel2.gate_opening_curve.set(pd.Series([0,1],index=[0,1]))
    tunnel2.continuous_gate.set(1)
    
def run_model(shop):
    shop.start_sim([],['3'])
    shop.set_code(['inc'],[])
    shop.start_sim([],['3'])

tunnel_gate.py#

import pandas as pd

def add_tunnel_gate(shop):
    tunnel2=shop.model.tunnel["Tunnel2"]
    tunnel2.gate_opening_curve.set(pd.Series([0,1],index=[0,1]))
    tunnel2.continuous_gate.set(1)

tunnel_model.yaml #

time:
  starttime: 2018-02-27 00:00:00
  endtime: 2018-02-27 06:00:00
  timeunit: hour
  timeresolution:
    2018-02-27 00:00:00: 1
model:
  reservoir:
    Reservoir1:
      start_vol: 0
      start_head: 93
      max_vol: 5
      lrl: 90
      hrl: 100
      vol_head:
        ref: 0
        x:
          - 0
          - 5
          - 6
        y:
          - 90
          - 100
          - 101
      flow_descr:
        ref: 0
        x:
          - 100
          - 101
        y:
          - 0
          - 1000
      endpoint_desc_nok_mwh:
        ref: 0
        x:
          - 0
        y:
          - 31.7
    Reservoir2:
      start_vol: 0
      start_head: 93
      max_vol: 5
      lrl: 90
      hrl: 100
      vol_head:
        ref: 0
        x:
          - 0
          - 5
          - 6
        y:
          - 90
          - 100
          - 101
      flow_descr:
        ref: 0
        x:
          - 100
          - 101
        y:
          - 0
          - 1000
      endpoint_desc_nok_mwh:
        ref: 0
        x:
          - 0
        y:
          - 31.7
    Reservoir3:
      start_vol: 0
      start_head: 97
      max_vol: 5
      lrl: 90
      hrl: 100
      vol_head:
        ref: 0
        x:
          - 0
          - 5
          - 6
        y:
          - 90
          - 100
          - 101
      flow_descr:
        ref: 0
        x:
          - 100
          - 101
        y:
          - 0
          - 1000
      endpoint_desc_nok_mwh:
        ref: 0
        x:
          - 0
        y:
          - 31.7
      inflow:
        2018-02-27 00:00:00: 200
  plant:
    Plant1:
      main_loss:
        - 0.0002
      penstock_loss:
        - 0.0001
  generator:
    P1G1:
      penstock: 1
      p_min: 0.1
      p_max: 100
      p_nom: 100
      gen_eff_curve:
        ref: 0
        x:
          - 0
          - 100
        y:
          - 95
          - 98
      turb_eff_curves:
        - ref: 90
          x:
            - 1
            - 90
            - 100
          y:
            - 80
            - 95
            - 90
        - ref: 100
          x:
            - 1
            - 90
            - 100
          y:
            - 82
            - 98
            - 92
      startcost:
        2018-02-27 00:00:00: 0
  tunnel:
    Tunnel1:
      start_height: 90
      end_height: 90
      diameter: 3
      length: 2022
      loss_factor: 0.00016
    Tunnel2:
      start_height: 90
      end_height: 90
      diameter: 3
      length: 2022
      loss_factor: 0.00015
    Tunnel3:
      start_height: 90
      end_height: 90
      diameter: 3
      length: 2022
      loss_factor: 0.0003
  market:
    1:
      market_type: ENERGY
      max_buy:
        2018-02-27 00:00:00: 9999
      max_sale:
        2018-02-27 00:00:00: 9999
      buy_price:
        2018-02-27 00:00:00: 40.01
      sale_price:
        2018-02-27 00:00:00: 39.99
connections:
  - from: Tunnel1
    to: Reservoir2
  - from: Tunnel2
    to: Reservoir3
  - from: Tunnel3
    to: Plant1
  - from: Plant1
    to: P1G1
  - from: Reservoir1
    to: Tunnel1
  - from: Reservoir2
    to: Tunnel2
  - from: Reservoir3
    to: Tunnel3

tunnel_gate.yaml #

model:
  tunnel:
    Tunnel2:
      continuous_gate: 1
      gate_opening_curve:
        ref: 0
        x:
          - 0
          - 1
        y:
          - 0
          - 1

tunnel_model.ascii #

OPTIMIZATION time 
#Start_time;   End_time;
20180227000000 20180227060000 

#;N_full_iterations;Accuracy;
OPTIMIZATION   1000       1.00 
#Time resolution in the optimization;
#Id;Number;Start_Time;Time_unit;Period;Data_type;Y_unit;Pts;
     0      0 20180227000000000 HOUR      0     -1 NO_UNIT      7 
#            Time;      f(t);
20180227000000000 1.00000000000000000 

RESERVOIR attributes Reservoir1 
#ID;Water_course;Type;Maxvol;Lrl;Hrl;
     0      0      0      5.000     90.000    100.000 

RESERVOIR vol_head Reservoir1
#Id;Number;Reference;Pts;X_unit;Y_unit
     0      0       0.00      3 MM3 METER
#  x_value;   y_value;
0.0000000000 90.0000000000 
5.0000000000 100.0000000000 
6.0000000000 101.0000000000 

RESERVOIR flow_descr Reservoir1
#Id;Number;Reference;Pts;X_unit;Y_unit
     0      0       0.00      2 METER M3/S
#  x_value;   y_value;
100.0000000000 0.0000000000 
101.0000000000 1000.0000000000 

RESERVOIR endpoint_desc Reservoir1
#Id;Number;Reference;Pts;X_unit;Y_unit
     0      0       0.00      1 MM3 NOK/MWH
#  x_value;   y_value;
0.0000000000 31.7000000000 

RESERVOIR attributes Reservoir2 
#ID;Water_course;Type;Maxvol;Lrl;Hrl;
     0      0      0      5.000     90.000    100.000 

RESERVOIR vol_head Reservoir2
#Id;Number;Reference;Pts;X_unit;Y_unit
     0      0       0.00      3 MM3 METER
#  x_value;   y_value;
0.0000000000 90.0000000000 
5.0000000000 100.0000000000 
6.0000000000 101.0000000000 

RESERVOIR flow_descr Reservoir2
#Id;Number;Reference;Pts;X_unit;Y_unit
     0      0       0.00      2 METER M3/S
#  x_value;   y_value;
100.0000000000 0.0000000000 
101.0000000000 1000.0000000000 

RESERVOIR endpoint_desc Reservoir2
#Id;Number;Reference;Pts;X_unit;Y_unit
     0      0       0.00      1 MM3 NOK/MWH
#  x_value;   y_value;
0.0000000000 31.7000000000 

RESERVOIR attributes Reservoir3 
#ID;Water_course;Type;Maxvol;Lrl;Hrl;
     0      0      0      5.000     90.000    100.000 

RESERVOIR vol_head Reservoir3
#Id;Number;Reference;Pts;X_unit;Y_unit
     0      0       0.00      3 MM3 METER
#  x_value;   y_value;
0.0000000000 90.0000000000 
5.0000000000 100.0000000000 
6.0000000000 101.0000000000 

RESERVOIR flow_descr Reservoir3
#Id;Number;Reference;Pts;X_unit;Y_unit
     0      0       0.00      2 METER M3/S
#  x_value;   y_value;
100.0000000000 0.0000000000 
101.0000000000 1000.0000000000 

RESERVOIR endpoint_desc Reservoir3
#Id;Number;Reference;Pts;X_unit;Y_unit
     0      0       0.00      1 MM3 NOK/MWH
#  x_value;   y_value;
0.0000000000 31.7000000000 

RESERVOIR inflow Reservoir3
#Id;Number;Start_Time;Time_unit;Period;Data_type;Y_unit;Pts;
     0      0 20180227000000000 HOUR      0     -1 M3/S      7 
#            Time;      f(t);
20180227000000000 200.00000000000000000 

PLANT attributes Plant1 
#Id;Water_course;Type;Future_use;Prod_area;Num_gen;Num_pump;
     0      0      6      0      0      1      0 
#Num_main_seg;Num_penstock;Time_delay;Read_prod_factor;Outlet_line;
     1      1      0   0.000000   0.000000 
0.000200 
0.000100 

GENERATOR attributes Plant1      1 
#Id Type Penstock Nom_prod Min_prod Max_prod Start_cost 
     0      7      1     100.00       0.10     100.00       0.00 

GENERATOR gen_eff_curve Plant1      1 
#Id;Number;Reference;Pts;X_unit;Y_unit
     0      0       0.00      2 MW %
#  x_value;   y_value;
0.0000000000 95.0000000000 
100.0000000000 98.0000000000 

GENERATOR turb_eff_curves Plant1      1 
#Id;Number;Reference;Pts;X_unit;Y_unit
     0      0      90.00      3 M3/S %
#  x_value;   y_value;
1.0000000000 80.0000000000 
90.0000000000 95.0000000000 
100.0000000000 90.0000000000 

GENERATOR turb_eff_curves Plant1      1 
#Id;Number;Reference;Pts;X_unit;Y_unit
     0      0     100.00      3 M3/S %
#  x_value;   y_value;
1.0000000000 82.0000000000 
90.0000000000 98.0000000000 
100.0000000000 92.0000000000 

#Initial reservoir volumes;
STARTRES      3 MM3 
#Name; Volume (Mm3);
Reservoir1       1.5000 
Reservoir2       1.5000 
Reservoir3       3.5000 

MULTI_MARKET price_buy 1 0
#Id;Number;Start_Time;Time_unit;Period;Data_type;Y_unit;Pts;
     0      0 20180227000000000 HOUR      0     -1 NOK/MWH      7 
#            Time;      f(t);
20180227000000000 40.01

MULTI_MARKET price_sale 1 0
#Id;Number;Start_Time;Time_unit;Period;Data_type;Y_unit;Pts;
     0      0 20180227000000000 HOUR      0     -1 NOK/MWH      7 
#            Time;      f(t);
20180227000000000 39.99

MULTI_MARKET max_buy 1 0
#Id;Number;Start_Time;Time_unit;Period;Data_type;Y_unit;Pts;
     0      0 20180227000000000 HOUR      0     -1 MW      7 
#            Time;      f(t);
20180227000000000 9999

MULTI_MARKET max_sale 1 0
#Id;Number;Start_Time;Time_unit;Period;Data_type;Y_unit;Pts;
     0      0 20180227000000000 HOUR      0     -1 MW      7 
#            Time;      f(t);
20180227000000000 9999

TUNNEL attributes Tunnel1
# loss_factor start_height end_height diameter length
0.00016     90          90        3        2022
  
TUNNEL attributes Tunnel2
# loss_factor start_height end_height diameter length
0.00015     90          90        3        2022
  
TUNNEL attributes Tunnel3
# loss_factor start_height end_height diameter length
0.00030     90          90        3        2022

#Write connection data for the hydro power system
#;    From_type/To_type; From_name; To_name;
CONNECT RESERVOIR/TUNNEL Reservoir1 Tunnel1
CONNECT TUNNEL/RESERVOIR Tunnel1 Reservoir2
CONNECT RESERVOIR/TUNNEL Reservoir2 Tunnel2 
CONNECT TUNNEL/RESERVOIR Tunnel2 Reservoir3
CONNECT RESERVOIR/TUNNEL Reservoir3 Tunnel3 
CONNECT TUNNEL/PLANT Tunnel3 Plant1

tunnel_gate.ascii #

TUNNEL gate_positions Tunnel2
#Id;Number;Reference;Pts;X_unit;Y_unit
10600      0      0.000      4 NO_UNIT PERCENT
#  x_value;   y_value;
0 0.0
1 1.0

TUNNEL continuous_gate Tunnel2
1