Merge branch 'Dev'

This commit is contained in:
Brantegger Georg
2022-08-08 14:51:49 +02:00
10 changed files with 491 additions and 332 deletions

View File

@@ -14,7 +14,7 @@ def FODE_function(x_out,h,A,A_a,p,rho,g):
# based on the outflux formula by Andreas Malcherek # based on the outflux formula by Andreas Malcherek
# https://www.youtube.com/watch?v=8HO2LwqOhqQ # https://www.youtube.com/watch?v=8HO2LwqOhqQ
# adapted for a pressurized pipeline into which the reservoir effuses # adapted for a pressurized pipeline into which the reservoir effuses
# and flow direction # and flow direction
# x_out ... effusion velocity # x_out ... effusion velocity
# h ... level in the reservoir # h ... level in the reservoir
# A_a ... Area_outflux # A_a ... Area_outflux
@@ -27,14 +27,14 @@ def FODE_function(x_out,h,A,A_a,p,rho,g):
class Ausgleichsbecken_class: class Ausgleichsbecken_class:
# units # units
# make sure that units and print units are the same # make sure that units and display units are the same
# units are used to label graphs and disp units are used to have a bearable format when using pythons print() # units are used to label graphs and disp units are used to have a bearable format when using pythons print()
area_unit = r'$\mathrm{m}^2$' area_unit = r'$\mathrm{m}^2$'
area_outflux_unit = r'$\mathrm{m}^2$' area_outflux_unit = r'$\mathrm{m}^2$'
density_unit = r'$\mathrm{kg}/\mathrm{m}^3$' density_unit = r'$\mathrm{kg}/\mathrm{m}^3$'
flux_unit = r'$\mathrm{m}^3/\mathrm{s}$' flux_unit = r'$\mathrm{m}^3/\mathrm{s}$'
level_unit = 'm' level_unit = 'm'
pressure_unit = 'Pa' pressure_unit = 'Pa' # DONT CHANGE needed for pressure conversion
time_unit = 's' time_unit = 's'
velocity_unit = r'$\mathrm{m}/\mathrm{s}$' velocity_unit = r'$\mathrm{m}/\mathrm{s}$'
volume_unit = r'$\mathrm{m}^3$' volume_unit = r'$\mathrm{m}^3$'
@@ -44,6 +44,7 @@ class Ausgleichsbecken_class:
density_unit_disp = 'kg/m³' density_unit_disp = 'kg/m³'
flux_unit_disp = 'm³/s' flux_unit_disp = 'm³/s'
level_unit_disp = 'm' level_unit_disp = 'm'
# pressure_unit_disp will be set within the __init__() method
time_unit_disp = 's' time_unit_disp = 's'
velocity_unit_disp = 'm/s' velocity_unit_disp = 'm/s'
volume_unit_disp = '' volume_unit_disp = ''
@@ -53,26 +54,37 @@ class Ausgleichsbecken_class:
# init # init
def __init__(self,area,area_outflux,timestep,pressure_unit_disp,level_min=0,level_max=np.inf,rho = 1000.): def __init__(self,area,area_outflux,timestep,pressure_unit_disp,level_min=0,level_max=np.inf,rho = 1000.):
"""
Creates a reservoir with given attributes in this order: \n
Base Area [m²] \n
Outflux Area [m²] \n
Simulation timestep [s] \n
Pressure unit for displaying [string] \n
Minimal level [m] \n
Maximal level [m] \n
Density of the liquid [kg/m³] \n
"""
#set initial attributes
self.area = area # base area of the cuboid reservoir self.area = area # base area of the cuboid reservoir
self.area_out = area_outflux # area of the outlet towards the pipeline self.area_out = area_outflux # area of the outlet towards the pipeline
self.density = rho # density of the liquid in the system self.density = rho # density of the liquid in the system
self.level_min = level_min # lowest allowed water level self.level_min = level_min # lowest allowed water level - warning yet to be implemented
self.level_max = level_max # highest allowed water level self.level_max = level_max # highest allowed water level - warning yet to be implemented
self.pressure_unit_disp = pressure_unit_disp # pressure unit for displaying self.pressure_unit_disp = pressure_unit_disp # pressure unit for displaying
self.timestep = timestep # timestep in the time evolution method self.timestep = timestep # timestep in the time evolution method
# initialize for get_info # initialize for get_info() (if get_info() gets called before set_steady_state() is executed)
self.influx = "--" self.influx = -np.inf
self.outflux = "--" self.outflux = -np.inf
self.level = "--" self.level = -np.inf
self.pressure = "--" self.pressure = -np.inf
self.volume = "--" self.volume = -np.inf
# setter # setter - set attributes
def set_initial_level(self,initial_level): def set_initial_level(self,initial_level):
# sets the initial level in the reservoir and should only be called during initialization # sets the initial level in the reservoir and should only be called during initialization
if self.level == '--': if self.level == -np.inf:
self.level = initial_level self.level = initial_level
self.update_volume(set_flag=True) self.update_volume(set_flag=True)
else: else:
@@ -80,7 +92,7 @@ class Ausgleichsbecken_class:
def set_initial_pressure(self,initial_pressure): def set_initial_pressure(self,initial_pressure):
# sets the initial static pressure present at the outlet of the reservoir and should only be called during initialization # sets the initial static pressure present at the outlet of the reservoir and should only be called during initialization
if self.pressure == '--': if self.pressure == -np.inf:
self.pressure = initial_pressure self.pressure = initial_pressure
else: else:
raise Exception('Initial pressure was already set once. Use the .update_pressure(self) method to update pressure based current level.') raise Exception('Initial pressure was already set once. Use the .update_pressure(self) method to update pressure based current level.')
@@ -96,7 +108,7 @@ class Ausgleichsbecken_class:
if display_warning == True: if display_warning == True:
print('You are setting the outflux from the reservoir manually. \n \ print('You are setting the outflux from the reservoir manually. \n \
This is not an intended use of this method. \n \ This is not an intended use of this method. \n \
Refer to the timestep_reservoir_evolution() method instead.') Refer to the timestep_reservoir_evolution() or set_steady_state() method instead.')
self.outflux = outflux self.outflux = outflux
def set_level(self,level,display_warning=True): def set_level(self,level,display_warning=True):
@@ -104,7 +116,7 @@ class Ausgleichsbecken_class:
if display_warning == True: if display_warning == True:
print('You are setting the level of the reservoir manually. \n \ print('You are setting the level of the reservoir manually. \n \
This is not an intended use of this method. \n \ This is not an intended use of this method. \n \
Refer to the update_level() method instead.') Refer to the update_level() or set_steady_state() method instead.')
self.level = level self.level = level
def set_pressure(self,pressure,display_warning=True): def set_pressure(self,pressure,display_warning=True):
@@ -112,48 +124,53 @@ class Ausgleichsbecken_class:
if display_warning == True: if display_warning == True:
print('You are setting the pressure below the reservoir manually. \n \ print('You are setting the pressure below the reservoir manually. \n \
This is not an intended use of this method. \n \ This is not an intended use of this method. \n \
Refer to the update_pressure() method instead.') Refer to the update_pressure() or set_steady_state() method instead.')
self.pressure = pressure self.pressure = pressure
def set_volume(self,volume,display_warning=True): def set_volume(self,volume,display_warning=True):
# sets volume in reservoir
if display_warning == True: if display_warning == True:
print('You are setting the volume in the reservoir manually. \n \ print('You are setting the volume in the reservoir manually. \n \
This is not an intended use of this method. \n \ This is not an intended use of this method. \n \
Refer to the .update_volume() or set_initial_level() method instead.') Refer to the .update_volume() or set_initial_level() or set_steady_state() method instead.')
self.volume = volume self.volume = volume
def set_steady_state(self,ss_influx,ss_level): def set_steady_state(self,ss_influx,ss_level):
# set the steady state (ss) condition in which the net flux is zero # set the reservoir to steady state (ss) condition in which the net flux is zero
# set pressure acting on the outflux area so that the level stays constant # set pressure acting on the outflux area so that the level stays constant
ss_outflux = ss_influx ss_outflux = ss_influx
ss_influx_vel = abs(ss_influx/self.area) ss_influx_vel = abs(ss_influx/self.area)
ss_outflux_vel = abs(ss_outflux/self.area_out) ss_outflux_vel = abs(ss_outflux/self.area_out)
# see confluence doc for explaination on how to arrive at the ss pressure formula
ss_pressure = self.density*self.g*ss_level+self.density*ss_outflux_vel*(ss_influx_vel-ss_outflux_vel) ss_pressure = self.density*self.g*ss_level+self.density*ss_outflux_vel*(ss_influx_vel-ss_outflux_vel)
# use setter methods to set the attributes to their steady state values
self.set_influx(ss_influx) self.set_influx(ss_influx)
self.set_initial_level(ss_level) self.set_initial_level(ss_level)
self.set_initial_pressure(ss_pressure) self.set_initial_pressure(ss_pressure)
self.set_outflux(ss_outflux,display_warning=False) self.set_outflux(ss_outflux,display_warning=False)
# getter # getter - return attributes
def get_info(self, full = False): def get_info(self, full = False):
# prints out the info on the current state of the reservoir
new_line = '\n' new_line = '\n'
p = pressure_conversion(self.pressure,self.pressure_unit,self.pressure_unit_disp) if self.pressure != np.inf:
outflux_vel = self.outflux/self.area_out p = pressure_conversion(self.pressure,self.pressure_unit,self.pressure_unit_disp)
if self.outflux != np.inf:
outflux_vel = self.outflux/self.area_out
if full == True: if full == True:
# :<10 pads the self.value to be 10 characters wide # :<10 pads the self.value to be 10 characters wide
print_str = (f"The cuboid reservoir has the following attributes: {new_line}" print_str = (f"The cuboid reservoir has the following attributes: {new_line}"
f"----------------------------- {new_line}" f"----------------------------- {new_line}"
f"Base area = {self.area:<10} {self.area_unit_disp} {new_line}" f"Base area = {self.area:<10} {self.area_unit_disp} {new_line}"
f"Outflux area = {round(self.area_out,3):<10} {self.area_out_unit_disp} {new_line}" f"Outflux area = {round(self.area_out,3):<10} {self.area_outflux_unit_disp} {new_line}"
f"Current level = {self.level:<10} {self.level_unit_disp}{new_line}" f"Current level = {self.level:<10} {self.level_unit_disp}{new_line}"
f"Critical level low = {self.level_min:<10} {self.level_unit_disp} {new_line}" f"Critical level low = {self.level_min:<10} {self.level_unit_disp} {new_line}"
f"Critical level high = {self.level_max:<10} {self.level_unit_disp} {new_line}" f"Critical level high = {self.level_max:<10} {self.level_unit_disp} {new_line}"
f"Volume in reservoir = {self.volume:<10} {self.volume_unit_disp} {new_line}" f"Volume in reservoir = {self.volume:<10} {self.volume_unit_disp} {new_line}"
f"Current influx = {self.influx:<10} {self.flux_unit_disp} {new_line}" f"Current influx = {round(self.influx,3):<10} {self.flux_unit_disp} {new_line}"
f"Current outflux = {self.outflux:<10} {self.flux_unit_disp} {new_line}" f"Current outflux = {round(self.outflux,3):<10} {self.flux_unit_disp} {new_line}"
f"Current outflux vel = {round(outflux_vel,3):<10} {self.velocity_unit_disp} {new_line}" f"Current outflux vel = {round(outflux_vel,3):<10} {self.velocity_unit_disp} {new_line}"
f"Current pipe pressure = {round(p,3):<10} {self.pressure_unit_disp} {new_line}" f"Current pipe pressure = {round(p,3):<10} {self.pressure_unit_disp} {new_line}"
f"Simulation timestep = {self.timestep:<10} {self.time_unit_disp} {new_line}" f"Simulation timestep = {self.timestep:<10} {self.time_unit_disp} {new_line}"
@@ -165,8 +182,8 @@ class Ausgleichsbecken_class:
f"----------------------------- {new_line}" f"----------------------------- {new_line}"
f"Current level = {self.level:<10} {self.level_unit_disp}{new_line}" f"Current level = {self.level:<10} {self.level_unit_disp}{new_line}"
f"Current volume = {self.volume:<10} {self.volume_unit_disp} {new_line}" f"Current volume = {self.volume:<10} {self.volume_unit_disp} {new_line}"
f"Current influx = {self.influx:<10} {self.flux_unit_disp} {new_line}" f"Current influx = {round(self.influx,3):<10} {self.flux_unit_disp} {new_line}"
f"Current outflux = {self.outflux:<10} {self.flux_unit_disp} {new_line}" f"Current outflux = {round(self.outflux,3):<10} {self.flux_unit_disp} {new_line}"
f"Current outflux vel = {round(outflux_vel,3):<10} {self.velocity_unit_disp} {new_line}" f"Current outflux vel = {round(outflux_vel,3):<10} {self.velocity_unit_disp} {new_line}"
f"Current pipe pressure = {round(p,3):<10} {self.pressure_unit_disp} {new_line}" f"Current pipe pressure = {round(p,3):<10} {self.pressure_unit_disp} {new_line}"
f"----------------------------- {new_line}") f"----------------------------- {new_line}")
@@ -188,22 +205,26 @@ class Ausgleichsbecken_class:
def get_current_volume(self): def get_current_volume(self):
return self.volume return self.volume
# update methods # update methods - update attributes based on some parameter
def update_level(self,timestep,set_flag=False): def update_level(self,timestep,set_flag=False):
# update level based on net flux and timestep by calculating the volume change in # update level based on net flux and timestep by calculating the volume change in
# the timestep and the converting the new volume to a level by assuming a cuboid reservoir # the timestep and the converting the new volume to a level by assuming a cuboid reservoir
net_flux = self.influx-self.outflux net_flux = self.influx-self.outflux
delta_level = net_flux*timestep/self.area delta_level = net_flux*timestep/self.area
level_new = (self.level+delta_level) level_new = (self.level+delta_level)
# set flag is necessary because update_level() is used to get a halfstep value in the time evoultion
if set_flag == True: if set_flag == True:
self.set_level(level_new,display_warning=False) self.set_level(level_new,display_warning=False)
elif set_flag == False: elif set_flag == False:
return level_new return level_new
def update_pressure(self,set_flag=False): def update_pressure(self,set_flag=False):
influx_vel = abs(self.influx/self.area) # update pressure based on level and flux velocities
# see confluence doc for explaination
influx_vel = abs(self.influx/self.area)
outflux_vel = abs(self.outflux/self.area_out) outflux_vel = abs(self.outflux/self.area_out)
p_new = self.density*self.g*self.level+self.density*outflux_vel*(influx_vel-outflux_vel) p_new = self.density*self.g*self.level+self.density*outflux_vel*(influx_vel-outflux_vel)
# set flag for consistency with update_level()
if set_flag ==True: if set_flag ==True:
self.set_pressure(p_new,display_warning=False) self.set_pressure(p_new,display_warning=False)
elif set_flag == False: elif set_flag == False:
@@ -211,6 +232,7 @@ class Ausgleichsbecken_class:
def update_volume(self,set_flag=False): def update_volume(self,set_flag=False):
volume_new = self.level*self.area volume_new = self.level*self.area
# set flag for consistency with update_level()
if set_flag == True: if set_flag == True:
self.set_volume(volume_new,display_warning=False) self.set_volume(volume_new,display_warning=False)
elif set_flag == False: elif set_flag == False:
@@ -218,7 +240,9 @@ class Ausgleichsbecken_class:
#methods #methods
def timestep_reservoir_evolution(self): def timestep_reservoir_evolution(self):
# update outflux and outflux velocity based on current pipeline pressure and waterlevel in reservoir # update outflux, level, pressure and volume based on current pipeline pressure and waterlevel in reservoir
# get some variables
dt = self.timestep dt = self.timestep
rho = self.density rho = self.density
g = self.g g = self.g
@@ -229,7 +253,8 @@ class Ausgleichsbecken_class:
h_hs = self.update_level(dt/2) h_hs = self.update_level(dt/2)
p = self.pressure p = self.pressure
p_hs = self.pressure + rho*g*(h_hs-h) p_hs = self.pressure + rho*g*(h_hs-h)
# explicit 4 step Runge Kutta
# perform explicit 4 step Runge Kutta
Y1 = yn Y1 = yn
Y2 = yn + dt/2*FODE_function(Y1,h,A,A_a,p,rho,g) Y2 = yn + dt/2*FODE_function(Y1,h,A,A_a,p,rho,g)
Y3 = yn + dt/2*FODE_function(Y2,h_hs,A,A_a,p_hs,rho,g) Y3 = yn + dt/2*FODE_function(Y2,h_hs,A,A_a,p_hs,rho,g)
@@ -237,6 +262,7 @@ class Ausgleichsbecken_class:
ynp1 = yn + dt/6*(FODE_function(Y1,h,A,A_a,p,rho,g)+2*FODE_function(Y2,h_hs,A,A_a,p_hs,rho,g)+ \ ynp1 = yn + dt/6*(FODE_function(Y1,h,A,A_a,p,rho,g)+2*FODE_function(Y2,h_hs,A,A_a,p_hs,rho,g)+ \
2*FODE_function(Y3,h_hs,A,A_a,p_hs,rho,g)+ FODE_function(Y4,h,A,A_a,p,rho,g)) 2*FODE_function(Y3,h_hs,A,A_a,p_hs,rho,g)+ FODE_function(Y4,h,A,A_a,p,rho,g))
# set/update the attributes to their new values
self.set_outflux(ynp1*A_a,display_warning=False) self.set_outflux(ynp1*A_a,display_warning=False)
self.update_level(dt,set_flag=True) self.update_level(dt,set_flag=True)
self.update_volume(set_flag=True) self.update_volume(set_flag=True)

File diff suppressed because one or more lines are too long

View File

@@ -10,13 +10,15 @@ from functions.pressure_conversion import pressure_conversion
class Druckrohrleitung_class: class Druckrohrleitung_class:
# units # units
# make sure that units and display units are the same
# units are used to label graphs and disp units are used to have a bearable format when using pythons print()
acceleration_unit = r'$\mathrm{m}/\mathrm{s}^2$' acceleration_unit = r'$\mathrm{m}/\mathrm{s}^2$'
angle_unit = 'rad' angle_unit = 'rad'
area_unit = r'$\mathrm{m}^2$' area_unit = r'$\mathrm{m}^2$'
density_unit = r'$\mathrm{kg}/\mathrm{m}^3$' density_unit = r'$\mathrm{kg}/\mathrm{m}^3$'
flux_unit = r'$\mathrm{m}^3/\mathrm{s}$' flux_unit = r'$\mathrm{m}^3/\mathrm{s}$'
length_unit = 'm' length_unit = 'm'
pressure_unit = 'Pa' pressure_unit = 'Pa' # DONT CHANGE needed for pressure conversion
time_unit = 's' time_unit = 's'
velocity_unit = r'$\mathrm{m}/\mathrm{s}$' # for flux and pressure propagation velocity_unit = r'$\mathrm{m}/\mathrm{s}$' # for flux and pressure propagation
volume_unit = r'$\mathrm{m}^3$' volume_unit = r'$\mathrm{m}^3$'
@@ -27,33 +29,54 @@ class Druckrohrleitung_class:
density_unit_disp = 'kg/m³' density_unit_disp = 'kg/m³'
flux_unit_disp = 'm³/s' flux_unit_disp = 'm³/s'
length_unit_disp = 'm' length_unit_disp = 'm'
# pressure_unit_disp will be set within the __init__() method
time_unit_disp = 's' time_unit_disp = 's'
velocity_unit_disp = 'm/s' # for flux and pressure propagation velocity_unit_disp = 'm/s' # for flux and pressure propagation
volume_unit_disp = '' volume_unit_disp = ''
g = 9.81 g = 9.81 # m/s² gravitational acceleration
# init # init
def __init__(self,total_length,diameter,number_segments,pipeline_angle,Darcy_friction_factor,pw_vel,timestep,pressure_unit_disp,rho=1000): def __init__(self,total_length,diameter,pipeline_head,number_segments,Darcy_friction_factor,pw_vel,timestep,pressure_unit_disp,rho=1000):
self.length = total_length # total length of the pipeline """
self.dia = diameter # diameter of the pipeline Creates a reservoir with given attributes in this order: \n
self.n_seg = number_segments # number of segments for the method of characteristics Pipeline length [m] \n
self.angle = pipeline_angle # angle of the pipeline Pipeline diameter [m] \n
self.f_D = Darcy_friction_factor # = Rohrreibungszahl oder flow coefficient Pipeline head [m] \n
self.c = pw_vel Number of pipeline segments [1] \n
Darcy friction factor [1] \n
Pressure wave velocity [m/s] \n
Simulation timestep [s] \n
Pressure unit for displaying [string] \n
Density of the liquid [kg/m³] \n
"""
self.length = total_length # total length of the pipeline
self.dia = diameter # diameter of the pipeline
self.head = pipeline_head # hydraulic head of the pipeline
self.n_seg = number_segments # number of segments for the method of characteristics
self.f_D = Darcy_friction_factor # = Rohrreibungszahl oder flow coefficient
self.c = pw_vel # propagation velocity of pressure wave
self.dt = timestep self.dt = timestep
self.density = rho # density of the liquid in the pipeline self.density = rho # density of the liquid in the pipeline
self.A = (diameter/2)**2*np.pi # derivatives of input attributes
self.angle = np.arcsin(self.head/self.length) # angle of the pipeline
self.A = (diameter/2)**2*np.pi # crossectional area of the pipeline
self.dx = total_length/number_segments # length of each segment
self.x_vec = np.arange(0,(number_segments+1),1)*self.dx # vector giving the distance from each node to the start of the pipeline
self.h_vec = np.arange(0,(number_segments+1),1)*self.head/self.n_seg # vector giving the height difference from each node to the start of the pipeline
self.dx = total_length/number_segments # length of each segment self.pressure_unit_disp = pressure_unit_disp # pressure unit for displaying
self.x_vec = np.arange(0,(number_segments+1),1)*self.dx # vector giving the distance from each node to the start of the pipeline
self.pressure_unit_disp = pressure_unit_disp # setter - set attributes
def set_initial_pressure(self,pressure,display_warning=True):
# setter
def set_initial_pressure(self,pressure):
# initialize the pressure distribution in the pipeline # initialize the pressure distribution in the pipeline
if display_warning == True:
print('You are setting the pressure distribution in the pipeline manually. \n \
This is not an intended use of this method. \n \
Refer to the set_steady_state() method instead.')
# make sure the vector has the right size
if np.size(pressure) == 1: if np.size(pressure) == 1:
p0 = np.full_like(self.x_vec,pressure) p0 = np.full_like(self.x_vec,pressure)
elif np.size(pressure) == np.size(self.x_vec): elif np.size(pressure) == np.size(self.x_vec):
@@ -64,11 +87,18 @@ class Druckrohrleitung_class:
#initialize the vectors in which the old and new pressures are stored for the method of characteristics #initialize the vectors in which the old and new pressures are stored for the method of characteristics
self.p_old = p0.copy() self.p_old = p0.copy()
self.p = p0.copy() self.p = p0.copy()
# initialize the vectors in which the minimal and maximal value of the pressure at each node are stores
self.p_min = p0.copy() self.p_min = p0.copy()
self.p_max = p0.copy() self.p_max = p0.copy()
def set_initial_flow_velocity(self,velocity): def set_initial_flow_velocity(self,velocity, display_warning=True):
# initialize the velocity distribution in the pipeline # initialize the velocity distribution in the pipeline
if display_warning == True:
print('You are setting the velocity distribution in the pipeline manually. \n \
This is not an intended use of this method. \n \
Refer to the set_steady_state() method instead.')
# make sure the vector has the right size
if np.size(velocity) == 1: if np.size(velocity) == 1:
v0 = np.full_like(self.x_vec,velocity) v0 = np.full_like(self.x_vec,velocity)
elif np.size(velocity) == np.size(self.x_vec): elif np.size(velocity) == np.size(self.x_vec):
@@ -79,6 +109,7 @@ class Druckrohrleitung_class:
#initialize the vectors in which the old and new velocities are stored for the method of characteristics #initialize the vectors in which the old and new velocities are stored for the method of characteristics
self.v_old = v0.copy() self.v_old = v0.copy()
self.v = v0.copy() self.v = v0.copy()
# initialize the vectors in which the minimal and maximal value of the velocity at each node are stores
self.v_min = v0.copy() self.v_min = v0.copy()
self.v_max = v0.copy() self.v_max = v0.copy()
@@ -114,21 +145,19 @@ class Druckrohrleitung_class:
self.p[0] = p_boundary_res self.p[0] = p_boundary_res
self.p[-1] = p_boundary_tur self.p[-1] = p_boundary_tur
def set_steady_state(self,ss_flux,ss_level_reservoir,area_reservoir,x_vec,h_vec): def set_steady_state(self,ss_flux,ss_pressure_res):
# set the pressure and velocity distributions, that allow a constant flow of water from the (steady-state) reservoir to the (steady-state) turbine # set the pressure and velocity distributions, that allow a constant flow of water from the (steady-state) reservoir to the (steady-state) turbine
# the flow velocity is given by the constant flow through the pipe # the flow velocity is given by the constant flow through the pipe
ss_v0 = np.full_like(self.x_vec,ss_flux/self.A) ss_v0 = np.full_like(self.x_vec,ss_flux/self.A)
# the static pressure is given by static state pressure of the reservoir, corrected for the hydraulic head of the pipe and friction losses # the static pressure is given by static state pressure of the reservoir, corrected for the hydraulic head of the pipe and friction losses
ss_v_in_res = abs(ss_flux/area_reservoir) ss_pressure = ss_pressure_res+(self.density*self.g*self.h_vec)-(self.f_D*self.x_vec/self.dia*self.density/2*ss_v0**2)
ss_v_out_res = abs(ss_flux/self.A)
ss_pressure_res = self.density*self.g*(ss_level_reservoir)+self.density*ss_v_out_res*(ss_v_in_res-ss_v_out_res)
ss_pressure = ss_pressure_res+(self.density*self.g*h_vec)-(self.f_D*x_vec/self.dia*self.density/2*ss_v0**2)
self.set_initial_flow_velocity(ss_v0) # set the initial conditions
self.set_initial_pressure(ss_pressure) self.set_initial_flow_velocity(ss_v0,display_warning=False)
self.set_initial_pressure(ss_pressure,display_warning=False)
# getter # getter - return attributes
def get_info(self): def get_info(self):
new_line = '\n' new_line = '\n'
angle_deg = round(self.angle/np.pi*180,3) angle_deg = round(self.angle/np.pi*180,3)
@@ -139,6 +168,7 @@ class Druckrohrleitung_class:
f"----------------------------- {new_line}" f"----------------------------- {new_line}"
f"Length = {self.length:<10} {self.length_unit_disp} {new_line}" f"Length = {self.length:<10} {self.length_unit_disp} {new_line}"
f"Diameter = {self.dia:<10} {self.length_unit_disp} {new_line}" f"Diameter = {self.dia:<10} {self.length_unit_disp} {new_line}"
f"Hydraulic head = {self.head:<10} {self.length_unit_disp} {new_line}"
f"Number of segments = {self.n_seg:<10} {new_line}" f"Number of segments = {self.n_seg:<10} {new_line}"
f"Number of nodes = {self.n_seg+1:<10} {new_line}" f"Number of nodes = {self.n_seg+1:<10} {new_line}"
f"Length per segments = {self.dx:<10} {self.length_unit_disp} {new_line}" f"Length per segments = {self.dx:<10} {self.length_unit_disp} {new_line}"
@@ -148,17 +178,16 @@ class Druckrohrleitung_class:
f"Density of liquid = {self.density:<10} {self.density_unit_disp} {new_line}" f"Density of liquid = {self.density:<10} {self.density_unit_disp} {new_line}"
f"Pressure wave vel. = {self.c:<10} {self.velocity_unit_disp} {new_line}" f"Pressure wave vel. = {self.c:<10} {self.velocity_unit_disp} {new_line}"
f"Simulation timestep = {self.dt:<10} {self.time_unit_disp} {new_line}" f"Simulation timestep = {self.dt:<10} {self.time_unit_disp} {new_line}"
f"Number of timesteps = {self.nt:<10} {new_line}"
f"Total simulation time = {self.nt*self.dt:<10} {self.time_unit_disp} {new_line}"
f"----------------------------- {new_line}" f"----------------------------- {new_line}"
f"Velocity and pressure distribution are vectors and are accessible by the .v and .p attribute of the pipeline object") f"Velocity and pressure distribution are vectors and are accessible by the .v and .p attribute of the pipeline object")
print(print_str) print(print_str)
def get_current_pressure_distribution(self,disp=False): def get_current_pressure_distribution(self,disp_flag=False):
if disp == True: # disp_flag if one wants to directly plot the return of this method
if disp_flag == True: # convert to pressure unit disp
return pressure_conversion(self.p,self.pressure_unit,self.pressure_unit_disp) return pressure_conversion(self.p,self.pressure_unit,self.pressure_unit_disp)
elif disp == False: elif disp_flag == False: # stay in Pa
return self.p return self.p
def get_current_velocity_distribution(self): def get_current_velocity_distribution(self):
@@ -167,16 +196,16 @@ class Druckrohrleitung_class:
def get_current_flux_distribution(self): def get_current_flux_distribution(self):
return self.v*self.A return self.v*self.A
def get_lowest_pressure_per_node(self,disp=False): def get_lowest_pressure_per_node(self,disp_flag=False):
if disp == True: if disp_flag == True: # convert to pressure unit disp
return pressure_conversion(self.p_min,self.pressure_unit,self.pressure_unit_disp) return pressure_conversion(self.p_min,self.pressure_unit,self.pressure_unit_disp)
elif disp == False: elif disp_flag == False: # stay in Pa
return self.p_min return self.p_min
def get_highest_pressure_per_node(self,disp=False): def get_highest_pressure_per_node(self,disp_flag=False):
if disp == True: if disp_flag == True: # convert to pressure unit disp
return pressure_conversion(self.p_max,self.pressure_unit,self.pressure_unit_disp) return pressure_conversion(self.p_max,self.pressure_unit,self.pressure_unit_disp)
elif disp == False: elif disp_flag == False: # stay in Pa
return self.p_max return self.p_max
def get_lowest_velocity_per_node(self): def get_lowest_velocity_per_node(self):
@@ -194,14 +223,15 @@ class Druckrohrleitung_class:
def timestep_characteristic_method(self): def timestep_characteristic_method(self):
# use the method of characteristics to calculate the pressure and velocities at all nodes except the boundary ones # use the method of characteristics to calculate the pressure and velocities at all nodes except the boundary ones
# they are set with the .set_boundary_conditions_next_timestep() method beforehand # they are set with the .set_boundary_conditions_next_timestep() method beforehand
# constants for cleaner formula
nn = self.n_seg+1 # number of nodes nn = self.n_seg+1 # number of nodes
rho = self.density # density of liquid rho = self.density # density of liquid
c = self.c # pressure propagation velocity c = self.c # pressure propagation velocity
f_D = self.f_D # Darcy friction coefficient f_D = self.f_D # Darcy friction coefficient
dt = self.dt # timestep dt = self.dt # timestep
D = self.dia # pipeline diametet D = self.dia # pipeline diameter
g = self.g # graviational acceleration g = self.g # graviational acceleration
alpha = self.angle # pipeline angle alpha = self.angle # pipeline angle

View File

@@ -34,36 +34,32 @@
"pUnit_calc = 'Pa' # [text] DO NOT CHANGE! for pressure conversion in print statements and plot labels \n", "pUnit_calc = 'Pa' # [text] DO NOT CHANGE! for pressure conversion in print statements and plot labels \n",
"pUnit_conv = 'mWS' # [text] for pressure conversion in print statements and plot labels\n", "pUnit_conv = 'mWS' # [text] for pressure conversion in print statements and plot labels\n",
"\n", "\n",
"\n",
" # for Turbine\n", " # for Turbine\n",
"Tur_Q_nenn = 0.85 # [m³/s] nominal flux of turbine \n", "Tur_Q_nenn = 0.85 # [m³/s] nominal flux of turbine \n",
"Tur_p_nenn = pressure_conversion(10.6,'bar',pUnit_calc) # [Pa] nominal pressure of turbine \n", "Tur_p_nenn = pressure_conversion(10.6,'bar',pUnit_calc) # [Pa] nominal pressure of turbine \n",
"Tur_closingTime = 90. # [s] closing time of turbine\n", "Tur_closingTime = 90. # [s] closing time of turbine\n",
"\n",
"\n", "\n",
" # for PI controller\n", " # for PI controller\n",
"Con_targetLevel = 8. # [m]\n", "Con_targetLevel = 8. # [m]\n",
"Con_K_p = 0.1 # [-] proportional constant of PI controller\n", "Con_K_p = 0.1 # [-] proportional constant of PI controller\n",
"Con_T_i = 10. # [s] timespan in which a steady state error is corrected by the intergal term\n", "Con_T_i = 10. # [s] timespan in which a steady state error is corrected by the intergal term\n",
"Con_deadbandRange = 0.05 # [m] Deadband range around targetLevel for which the controller does NOT intervene\n", "Con_deadbandRange = 0.05 # [m] Deadband range around targetLevel for which the controller does NOT intervene\n",
"\n", "\n",
"\n",
" # for pipeline\n", " # for pipeline\n",
"Pip_length = (535.+478.) # [m] length of pipeline\n", "Pip_length = (535.+478.) # [m] length of pipeline\n",
"Pip_dia = 0.9 # [m] diameter of pipeline\n", "Pip_dia = 0.9 # [m] diameter of pipeline\n",
"Pip_area = Pip_dia**2/4*np.pi # [m²] crossectional area of pipeline\n", "Pip_area = Pip_dia**2/4*np.pi # [m²] crossectional area of pipeline\n",
"Pip_head = 105. # [m] hydraulic head of pipeline without reservoir\n", "Pip_head = 105. # [m] hydraulic head of pipeline without reservoir\n",
"Pip_angle = np.arcsin(Pip_head/Pip_length) # [rad] elevation angle of pipeline \n", "Pip_angle = np.arcsin(Pip_head/Pip_length) # [rad] elevation angle of pipeline \n",
"Pip_n_seg = 50 # [-] number of pipe segments in discretization\n", "Pip_n_seg = 50 # [-] number of pipe segments in discretization\n",
"Pip_f_D = 0.014 # [-] Darcy friction factor\n", "Pip_f_D = 0.014 # [-] Darcy friction factor\n",
"Pip_pw_vel = 500. # [m/s] propagation velocity of the pressure wave (pw) in the given pipeline\n", "Pip_pw_vel = 500. # [m/s] propagation velocity of the pressure wave (pw) in the given pipeline\n",
" # derivatives of the pipeline constants\n", " # derivatives of the pipeline constants\n",
"Pip_dx = Pip_length/Pip_n_seg # [m] length of each pipe segment\n", "Pip_dx = Pip_length/Pip_n_seg # [m] length of each pipe segment\n",
"Pip_dt = Pip_dx/Pip_pw_vel # [s] timestep according to method of characteristics\n", "Pip_dt = Pip_dx/Pip_pw_vel # [s] timestep according to method of characteristics\n",
"Pip_nn = Pip_n_seg+1 # [1] number of nodes\n", "Pip_nn = Pip_n_seg+1 # [1] number of nodes\n",
"Pip_x_vec = np.arange(0,Pip_nn,1)*Pip_dx # [m] vector holding the distance of each node from the upstream reservoir along the pipeline\n", "Pip_x_vec = np.arange(0,Pip_nn,1)*Pip_dx # [m] vector holding the distance of each node from the upstream reservoir along the pipeline\n",
"Pip_h_vec = np.arange(0,Pip_nn,1)*Pip_head/Pip_n_seg # [m] vector holding the vertival distance of each node from the upstream reservoir\n", "Pip_h_vec = np.arange(0,Pip_nn,1)*Pip_head/Pip_n_seg # [m] vector holding the vertival distance of each node from the upstream reservoir\n",
"\n",
"\n", "\n",
" # for reservoir\n", " # for reservoir\n",
"Res_area_base = 74. # [m²] total base are of the cuboid reservoir \n", "Res_area_base = 74. # [m²] total base are of the cuboid reservoir \n",
@@ -75,38 +71,68 @@
"Res_dt = Pip_dt/Res_nt # [s] harmonised timestep of reservoir time evolution\n", "Res_dt = Pip_dt/Res_nt # [s] harmonised timestep of reservoir time evolution\n",
"\n", "\n",
" # for general simulation\n", " # for general simulation\n",
"flux_init = Tur_Q_nenn/1.1 # [m³/s] initial flux through whole system for steady state initialization \n", "flux_init = Tur_Q_nenn/1.1 # [m³/s] initial flux through whole system for steady state initialization \n",
"level_init = Con_targetLevel # [m] initial water level in upstream reservoir for steady state initialization\n", "level_init = Con_targetLevel # [m] initial water level in upstream reservoir for steady state initialization\n",
"simTime_target = 600. # [s] target for total simulation time (will vary slightly to fit with Pip_dt)\n", "simTime_target = 600. # [s] target for total simulation time (will vary slightly to fit with Pip_dt)\n",
"nt = int(simTime_target//Pip_dt) # [1] Number of timesteps of the whole system\n", "nt = int(simTime_target//Pip_dt) # [1] Number of timesteps of the whole system\n",
"t_vec = np.arange(0,nt+1,1)*Pip_dt # [s] time vector. At each step of t_vec the system parameters are stored\n" "t_vec = np.arange(0,nt+1,1)*Pip_dt # [s] time vector. At each step of t_vec the system parameters are stored\n"
] ]
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 3, "execution_count": 3,
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"The pipeline has the following attributes: \n",
"----------------------------- \n",
"Length = 1013.0 m \n",
"Diameter = 0.9 m \n",
"Hydraulic head = 105.0 m \n",
"Number of segments = 50 \n",
"Number of nodes = 51 \n",
"Length per segments = 20.26 m \n",
"Pipeline angle = 0.104 rad \n",
"Pipeline angle = 5.95° \n",
"Darcy friction factor = 0.014 \n",
"Density of liquid = 1000.0 kg/m³ \n",
"Pressure wave vel. = 500.0 m/s \n",
"Simulation timestep = 0.04052 s \n",
"----------------------------- \n",
"Velocity and pressure distribution are vectors and are accessible by the .v and .p attribute of the pipeline object\n",
"The pipeline has the following attributes: \n",
"----------------------------- \n",
"Length = 1013.0 m \n",
"Diameter = 0.9 m \n",
"Hydraulic head = 105.0 m \n",
"Number of segments = 50 \n",
"Number of nodes = 51 \n",
"Length per segments = 20.26 m \n",
"Pipeline angle = 0.104 rad \n",
"Pipeline angle = 5.95° \n",
"Darcy friction factor = 0.014 \n",
"Density of liquid = 1000.0 kg/m³ \n",
"Pressure wave vel. = 500.0 m/s \n",
"Simulation timestep = 0.04052 s \n",
"----------------------------- \n",
"Velocity and pressure distribution are vectors and are accessible by the .v and .p attribute of the pipeline object\n"
]
}
],
"source": [ "source": [
"# create objects\n", "# create objects\n",
"\n", "\n",
"# Upstream reservoir\n", "# Upstream reservoir\n",
"reservoir = Ausgleichsbecken_class(Res_area_base,Res_area_out,Res_dt,Res_level_crit_lo,Res_level_crit_hi,rho)\n", "reservoir = Ausgleichsbecken_class(Res_area_base,Res_area_out,Res_dt,pUnit_conv,Res_level_crit_lo,Res_level_crit_hi,rho)\n",
"reservoir.set_steady_state(flux_init,level_init)\n", "reservoir.set_steady_state(flux_init,level_init)\n",
"\n", "\n",
"# pipeline\n", "# pipeline\n",
"pipe = Druckrohrleitung_class(Pip_length,Pip_dia,Pip_n_seg,Pip_angle,Pip_f_D,Pip_pw_vel,Pip_dt,pUnit_conv,rho)\n", "pipe = Druckrohrleitung_class(Pip_length,Pip_dia,Pip_head,Pip_n_seg,Pip_f_D,Pip_pw_vel,Pip_dt,pUnit_conv,rho)\n",
"pipe.set_steady_state(flux_init,level_init,Res_area_base,Pip_x_vec,Pip_h_vec)\n" "pipe.set_steady_state(flux_init,reservoir.get_current_pressure())\n",
] "pipe.get_info()\n"
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"reservoir.get_info(full=True)\n",
"pipe.get_info(full=True)"
] ]
}, },
{ {
@@ -171,35 +197,41 @@
"metadata": {}, "metadata": {},
"outputs": [ "outputs": [
{ {
"ename": "KeyboardInterrupt", "name": "stdout",
"evalue": "", "output_type": "stream",
"output_type": "error", "text": [
"traceback": [ "The cuboid reservoir has the following attributes: \n",
"\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", "----------------------------- \n",
"\u001b[1;31mKeyboardInterrupt\u001b[0m Traceback (most recent call last)", "Base area = 74.0 m² \n",
"\u001b[1;32my:\\KELAG\\KS\\KS-PW\\04 Digitalisierung\\KSPWDEV Server\\Digital Trainee Projekt\\DT_Slot_3_Project_Repo\\Druckrohrleitung\\Druckrohrleitung_test_steady_state.ipynb Cell 7\u001b[0m in \u001b[0;36m<cell line: 1>\u001b[1;34m()\u001b[0m\n\u001b[0;32m <a href='vscode-notebook-cell:/y%3A/KELAG/KS/KS-PW/04%20Digitalisierung/KSPWDEV%20Server/Digital%20Trainee%20Projekt/DT_Slot_3_Project_Repo/Druckrohrleitung/Druckrohrleitung_test_steady_state.ipynb#ch0000006?line=34'>35</a>\u001b[0m lo_01, \u001b[39m=\u001b[39m axs1[\u001b[39m1\u001b[39m]\u001b[39m.\u001b[39mplot(pl_vec,v_old,marker\u001b[39m=\u001b[39m\u001b[39m'\u001b[39m\u001b[39m.\u001b[39m\u001b[39m'\u001b[39m,c\u001b[39m=\u001b[39m\u001b[39m'\u001b[39m\u001b[39mblue\u001b[39m\u001b[39m'\u001b[39m)\n\u001b[0;32m <a href='vscode-notebook-cell:/y%3A/KELAG/KS/KS-PW/04%20Digitalisierung/KSPWDEV%20Server/Digital%20Trainee%20Projekt/DT_Slot_3_Project_Repo/Druckrohrleitung/Druckrohrleitung_test_steady_state.ipynb#ch0000006?line=36'>37</a>\u001b[0m fig1\u001b[39m.\u001b[39msuptitle(\u001b[39mstr\u001b[39m(\u001b[39mround\u001b[39m(t_vec[it_pipe],\u001b[39m2\u001b[39m)) \u001b[39m+\u001b[39m \u001b[39m'\u001b[39m\u001b[39m/\u001b[39m\u001b[39m'\u001b[39m \u001b[39m+\u001b[39m \u001b[39mstr\u001b[39m(\u001b[39mround\u001b[39m(t_vec[\u001b[39m-\u001b[39m\u001b[39m1\u001b[39m],\u001b[39m2\u001b[39m)))\n\u001b[1;32m---> <a href='vscode-notebook-cell:/y%3A/KELAG/KS/KS-PW/04%20Digitalisierung/KSPWDEV%20Server/Digital%20Trainee%20Projekt/DT_Slot_3_Project_Repo/Druckrohrleitung/Druckrohrleitung_test_steady_state.ipynb#ch0000006?line=37'>38</a>\u001b[0m fig1\u001b[39m.\u001b[39;49mcanvas\u001b[39m.\u001b[39;49mdraw()\n\u001b[0;32m <a href='vscode-notebook-cell:/y%3A/KELAG/KS/KS-PW/04%20Digitalisierung/KSPWDEV%20Server/Digital%20Trainee%20Projekt/DT_Slot_3_Project_Repo/Druckrohrleitung/Druckrohrleitung_test_steady_state.ipynb#ch0000006?line=38'>39</a>\u001b[0m fig1\u001b[39m.\u001b[39mtight_layout()\n\u001b[0;32m <a href='vscode-notebook-cell:/y%3A/KELAG/KS/KS-PW/04%20Digitalisierung/KSPWDEV%20Server/Digital%20Trainee%20Projekt/DT_Slot_3_Project_Repo/Druckrohrleitung/Druckrohrleitung_test_steady_state.ipynb#ch0000006?line=39'>40</a>\u001b[0m plt\u001b[39m.\u001b[39mpause(\u001b[39m0.000001\u001b[39m)\n", "Outflux area = 0.636 m² \n",
"File \u001b[1;32mc:\\ProgramData\\Anaconda3\\envs\\Georg_DT_Slot3\\lib\\site-packages\\matplotlib\\backends\\backend_agg.py:436\u001b[0m, in \u001b[0;36mFigureCanvasAgg.draw\u001b[1;34m(self)\u001b[0m\n\u001b[0;32m 432\u001b[0m \u001b[39m# Acquire a lock on the shared font cache.\u001b[39;00m\n\u001b[0;32m 433\u001b[0m \u001b[39mwith\u001b[39;00m RendererAgg\u001b[39m.\u001b[39mlock, \\\n\u001b[0;32m 434\u001b[0m (\u001b[39mself\u001b[39m\u001b[39m.\u001b[39mtoolbar\u001b[39m.\u001b[39m_wait_cursor_for_draw_cm() \u001b[39mif\u001b[39;00m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39mtoolbar\n\u001b[0;32m 435\u001b[0m \u001b[39melse\u001b[39;00m nullcontext()):\n\u001b[1;32m--> 436\u001b[0m \u001b[39mself\u001b[39;49m\u001b[39m.\u001b[39;49mfigure\u001b[39m.\u001b[39;49mdraw(\u001b[39mself\u001b[39;49m\u001b[39m.\u001b[39;49mrenderer)\n\u001b[0;32m 437\u001b[0m \u001b[39m# A GUI class may be need to update a window using this draw, so\u001b[39;00m\n\u001b[0;32m 438\u001b[0m \u001b[39m# don't forget to call the superclass.\u001b[39;00m\n\u001b[0;32m 439\u001b[0m \u001b[39msuper\u001b[39m()\u001b[39m.\u001b[39mdraw()\n", "Current level = 8.0 m\n",
"File \u001b[1;32mc:\\ProgramData\\Anaconda3\\envs\\Georg_DT_Slot3\\lib\\site-packages\\matplotlib\\artist.py:73\u001b[0m, in \u001b[0;36m_finalize_rasterization.<locals>.draw_wrapper\u001b[1;34m(artist, renderer, *args, **kwargs)\u001b[0m\n\u001b[0;32m 71\u001b[0m \u001b[39m@wraps\u001b[39m(draw)\n\u001b[0;32m 72\u001b[0m \u001b[39mdef\u001b[39;00m \u001b[39mdraw_wrapper\u001b[39m(artist, renderer, \u001b[39m*\u001b[39margs, \u001b[39m*\u001b[39m\u001b[39m*\u001b[39mkwargs):\n\u001b[1;32m---> 73\u001b[0m result \u001b[39m=\u001b[39m draw(artist, renderer, \u001b[39m*\u001b[39;49margs, \u001b[39m*\u001b[39;49m\u001b[39m*\u001b[39;49mkwargs)\n\u001b[0;32m 74\u001b[0m \u001b[39mif\u001b[39;00m renderer\u001b[39m.\u001b[39m_rasterizing:\n\u001b[0;32m 75\u001b[0m renderer\u001b[39m.\u001b[39mstop_rasterizing()\n", "Critical level low = 0.0 m \n",
"File \u001b[1;32mc:\\ProgramData\\Anaconda3\\envs\\Georg_DT_Slot3\\lib\\site-packages\\matplotlib\\artist.py:50\u001b[0m, in \u001b[0;36mallow_rasterization.<locals>.draw_wrapper\u001b[1;34m(artist, renderer)\u001b[0m\n\u001b[0;32m 47\u001b[0m \u001b[39mif\u001b[39;00m artist\u001b[39m.\u001b[39mget_agg_filter() \u001b[39mis\u001b[39;00m \u001b[39mnot\u001b[39;00m \u001b[39mNone\u001b[39;00m:\n\u001b[0;32m 48\u001b[0m renderer\u001b[39m.\u001b[39mstart_filter()\n\u001b[1;32m---> 50\u001b[0m \u001b[39mreturn\u001b[39;00m draw(artist, renderer)\n\u001b[0;32m 51\u001b[0m \u001b[39mfinally\u001b[39;00m:\n\u001b[0;32m 52\u001b[0m \u001b[39mif\u001b[39;00m artist\u001b[39m.\u001b[39mget_agg_filter() \u001b[39mis\u001b[39;00m \u001b[39mnot\u001b[39;00m \u001b[39mNone\u001b[39;00m:\n", "Critical level high = inf m \n",
"File \u001b[1;32mc:\\ProgramData\\Anaconda3\\envs\\Georg_DT_Slot3\\lib\\site-packages\\matplotlib\\figure.py:2810\u001b[0m, in \u001b[0;36mFigure.draw\u001b[1;34m(self, renderer)\u001b[0m\n\u001b[0;32m 2807\u001b[0m \u001b[39m# ValueError can occur when resizing a window.\u001b[39;00m\n\u001b[0;32m 2809\u001b[0m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39mpatch\u001b[39m.\u001b[39mdraw(renderer)\n\u001b[1;32m-> 2810\u001b[0m mimage\u001b[39m.\u001b[39;49m_draw_list_compositing_images(\n\u001b[0;32m 2811\u001b[0m renderer, \u001b[39mself\u001b[39;49m, artists, \u001b[39mself\u001b[39;49m\u001b[39m.\u001b[39;49msuppressComposite)\n\u001b[0;32m 2813\u001b[0m \u001b[39mfor\u001b[39;00m sfig \u001b[39min\u001b[39;00m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39msubfigs:\n\u001b[0;32m 2814\u001b[0m sfig\u001b[39m.\u001b[39mdraw(renderer)\n", "Volume in reservoir = 592.0 m³ \n",
"File \u001b[1;32mc:\\ProgramData\\Anaconda3\\envs\\Georg_DT_Slot3\\lib\\site-packages\\matplotlib\\image.py:132\u001b[0m, in \u001b[0;36m_draw_list_compositing_images\u001b[1;34m(renderer, parent, artists, suppress_composite)\u001b[0m\n\u001b[0;32m 130\u001b[0m \u001b[39mif\u001b[39;00m not_composite \u001b[39mor\u001b[39;00m \u001b[39mnot\u001b[39;00m has_images:\n\u001b[0;32m 131\u001b[0m \u001b[39mfor\u001b[39;00m a \u001b[39min\u001b[39;00m artists:\n\u001b[1;32m--> 132\u001b[0m a\u001b[39m.\u001b[39;49mdraw(renderer)\n\u001b[0;32m 133\u001b[0m \u001b[39melse\u001b[39;00m:\n\u001b[0;32m 134\u001b[0m \u001b[39m# Composite any adjacent images together\u001b[39;00m\n\u001b[0;32m 135\u001b[0m image_group \u001b[39m=\u001b[39m []\n", "Current influx = 0.773 m³/s \n",
"File \u001b[1;32mc:\\ProgramData\\Anaconda3\\envs\\Georg_DT_Slot3\\lib\\site-packages\\matplotlib\\artist.py:50\u001b[0m, in \u001b[0;36mallow_rasterization.<locals>.draw_wrapper\u001b[1;34m(artist, renderer)\u001b[0m\n\u001b[0;32m 47\u001b[0m \u001b[39mif\u001b[39;00m artist\u001b[39m.\u001b[39mget_agg_filter() \u001b[39mis\u001b[39;00m \u001b[39mnot\u001b[39;00m \u001b[39mNone\u001b[39;00m:\n\u001b[0;32m 48\u001b[0m renderer\u001b[39m.\u001b[39mstart_filter()\n\u001b[1;32m---> 50\u001b[0m \u001b[39mreturn\u001b[39;00m draw(artist, renderer)\n\u001b[0;32m 51\u001b[0m \u001b[39mfinally\u001b[39;00m:\n\u001b[0;32m 52\u001b[0m \u001b[39mif\u001b[39;00m artist\u001b[39m.\u001b[39mget_agg_filter() \u001b[39mis\u001b[39;00m \u001b[39mnot\u001b[39;00m \u001b[39mNone\u001b[39;00m:\n", "Current outflux = 0.773 m³/s \n",
"File \u001b[1;32mc:\\ProgramData\\Anaconda3\\envs\\Georg_DT_Slot3\\lib\\site-packages\\matplotlib\\axes\\_base.py:3082\u001b[0m, in \u001b[0;36m_AxesBase.draw\u001b[1;34m(self, renderer)\u001b[0m\n\u001b[0;32m 3079\u001b[0m a\u001b[39m.\u001b[39mdraw(renderer)\n\u001b[0;32m 3080\u001b[0m renderer\u001b[39m.\u001b[39mstop_rasterizing()\n\u001b[1;32m-> 3082\u001b[0m mimage\u001b[39m.\u001b[39;49m_draw_list_compositing_images(\n\u001b[0;32m 3083\u001b[0m renderer, \u001b[39mself\u001b[39;49m, artists, \u001b[39mself\u001b[39;49m\u001b[39m.\u001b[39;49mfigure\u001b[39m.\u001b[39;49msuppressComposite)\n\u001b[0;32m 3085\u001b[0m renderer\u001b[39m.\u001b[39mclose_group(\u001b[39m'\u001b[39m\u001b[39maxes\u001b[39m\u001b[39m'\u001b[39m)\n\u001b[0;32m 3086\u001b[0m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39mstale \u001b[39m=\u001b[39m \u001b[39mFalse\u001b[39;00m\n", "Current outflux vel = 1.215 m/s \n",
"File \u001b[1;32mc:\\ProgramData\\Anaconda3\\envs\\Georg_DT_Slot3\\lib\\site-packages\\matplotlib\\image.py:132\u001b[0m, in \u001b[0;36m_draw_list_compositing_images\u001b[1;34m(renderer, parent, artists, suppress_composite)\u001b[0m\n\u001b[0;32m 130\u001b[0m \u001b[39mif\u001b[39;00m not_composite \u001b[39mor\u001b[39;00m \u001b[39mnot\u001b[39;00m has_images:\n\u001b[0;32m 131\u001b[0m \u001b[39mfor\u001b[39;00m a \u001b[39min\u001b[39;00m artists:\n\u001b[1;32m--> 132\u001b[0m a\u001b[39m.\u001b[39;49mdraw(renderer)\n\u001b[0;32m 133\u001b[0m \u001b[39melse\u001b[39;00m:\n\u001b[0;32m 134\u001b[0m \u001b[39m# Composite any adjacent images together\u001b[39;00m\n\u001b[0;32m 135\u001b[0m image_group \u001b[39m=\u001b[39m []\n", "Current pipe pressure = 7.854 mWS \n",
"File \u001b[1;32mc:\\ProgramData\\Anaconda3\\envs\\Georg_DT_Slot3\\lib\\site-packages\\matplotlib\\artist.py:50\u001b[0m, in \u001b[0;36mallow_rasterization.<locals>.draw_wrapper\u001b[1;34m(artist, renderer)\u001b[0m\n\u001b[0;32m 47\u001b[0m \u001b[39mif\u001b[39;00m artist\u001b[39m.\u001b[39mget_agg_filter() \u001b[39mis\u001b[39;00m \u001b[39mnot\u001b[39;00m \u001b[39mNone\u001b[39;00m:\n\u001b[0;32m 48\u001b[0m renderer\u001b[39m.\u001b[39mstart_filter()\n\u001b[1;32m---> 50\u001b[0m \u001b[39mreturn\u001b[39;00m draw(artist, renderer)\n\u001b[0;32m 51\u001b[0m \u001b[39mfinally\u001b[39;00m:\n\u001b[0;32m 52\u001b[0m \u001b[39mif\u001b[39;00m artist\u001b[39m.\u001b[39mget_agg_filter() \u001b[39mis\u001b[39;00m \u001b[39mnot\u001b[39;00m \u001b[39mNone\u001b[39;00m:\n", "Simulation timestep = 0.001013 s \n",
"File \u001b[1;32mc:\\ProgramData\\Anaconda3\\envs\\Georg_DT_Slot3\\lib\\site-packages\\matplotlib\\axis.py:1170\u001b[0m, in \u001b[0;36mAxis.draw\u001b[1;34m(self, renderer, *args, **kwargs)\u001b[0m\n\u001b[0;32m 1163\u001b[0m tick\u001b[39m.\u001b[39mdraw(renderer)\n\u001b[0;32m 1165\u001b[0m \u001b[39m# scale up the axis label box to also find the neighbors, not\u001b[39;00m\n\u001b[0;32m 1166\u001b[0m \u001b[39m# just the tick labels that actually overlap note we need a\u001b[39;00m\n\u001b[0;32m 1167\u001b[0m \u001b[39m# *copy* of the axis label box because we don't want to scale\u001b[39;00m\n\u001b[0;32m 1168\u001b[0m \u001b[39m# the actual bbox\u001b[39;00m\n\u001b[1;32m-> 1170\u001b[0m \u001b[39mself\u001b[39;49m\u001b[39m.\u001b[39;49m_update_label_position(renderer)\n\u001b[0;32m 1172\u001b[0m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39mlabel\u001b[39m.\u001b[39mdraw(renderer)\n\u001b[0;32m 1174\u001b[0m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39m_update_offset_text_position(ticklabelBoxes, ticklabelBoxes2)\n", "Density of liquid = 1000.0 kg/m³ \n",
"File \u001b[1;32mc:\\ProgramData\\Anaconda3\\envs\\Georg_DT_Slot3\\lib\\site-packages\\matplotlib\\axis.py:2352\u001b[0m, in \u001b[0;36mYAxis._update_label_position\u001b[1;34m(self, renderer)\u001b[0m\n\u001b[0;32m 2348\u001b[0m \u001b[39mreturn\u001b[39;00m\n\u001b[0;32m 2350\u001b[0m \u001b[39m# get bounding boxes for this axis and any siblings\u001b[39;00m\n\u001b[0;32m 2351\u001b[0m \u001b[39m# that have been set by `fig.align_ylabels()`\u001b[39;00m\n\u001b[1;32m-> 2352\u001b[0m bboxes, bboxes2 \u001b[39m=\u001b[39m \u001b[39mself\u001b[39;49m\u001b[39m.\u001b[39;49m_get_tick_boxes_siblings(renderer\u001b[39m=\u001b[39;49mrenderer)\n\u001b[0;32m 2354\u001b[0m x, y \u001b[39m=\u001b[39m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39mlabel\u001b[39m.\u001b[39mget_position()\n\u001b[0;32m 2355\u001b[0m \u001b[39mif\u001b[39;00m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39mlabel_position \u001b[39m==\u001b[39m \u001b[39m'\u001b[39m\u001b[39mleft\u001b[39m\u001b[39m'\u001b[39m:\n", "----------------------------- \n",
"File \u001b[1;32mc:\\ProgramData\\Anaconda3\\envs\\Georg_DT_Slot3\\lib\\site-packages\\matplotlib\\axis.py:1880\u001b[0m, in \u001b[0;36mAxis._get_tick_boxes_siblings\u001b[1;34m(self, renderer)\u001b[0m\n\u001b[0;32m 1878\u001b[0m \u001b[39mfor\u001b[39;00m ax \u001b[39min\u001b[39;00m grouper\u001b[39m.\u001b[39mget_siblings(\u001b[39mself\u001b[39m\u001b[39m.\u001b[39maxes):\n\u001b[0;32m 1879\u001b[0m axis \u001b[39m=\u001b[39m \u001b[39mgetattr\u001b[39m(ax, \u001b[39mf\u001b[39m\u001b[39m\"\u001b[39m\u001b[39m{\u001b[39;00maxis_name\u001b[39m}\u001b[39;00m\u001b[39maxis\u001b[39m\u001b[39m\"\u001b[39m)\n\u001b[1;32m-> 1880\u001b[0m ticks_to_draw \u001b[39m=\u001b[39m axis\u001b[39m.\u001b[39;49m_update_ticks()\n\u001b[0;32m 1881\u001b[0m tlb, tlb2 \u001b[39m=\u001b[39m axis\u001b[39m.\u001b[39m_get_tick_bboxes(ticks_to_draw, renderer)\n\u001b[0;32m 1882\u001b[0m bboxes\u001b[39m.\u001b[39mextend(tlb)\n", "\n",
"File \u001b[1;32mc:\\ProgramData\\Anaconda3\\envs\\Georg_DT_Slot3\\lib\\site-packages\\matplotlib\\axis.py:1053\u001b[0m, in \u001b[0;36mAxis._update_ticks\u001b[1;34m(self)\u001b[0m\n\u001b[0;32m 1051\u001b[0m tick\u001b[39m.\u001b[39mset_label1(label)\n\u001b[0;32m 1052\u001b[0m tick\u001b[39m.\u001b[39mset_label2(label)\n\u001b[1;32m-> 1053\u001b[0m minor_locs \u001b[39m=\u001b[39m \u001b[39mself\u001b[39;49m\u001b[39m.\u001b[39;49mget_minorticklocs()\n\u001b[0;32m 1054\u001b[0m minor_labels \u001b[39m=\u001b[39m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39mminor\u001b[39m.\u001b[39mformatter\u001b[39m.\u001b[39mformat_ticks(minor_locs)\n\u001b[0;32m 1055\u001b[0m minor_ticks \u001b[39m=\u001b[39m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39mget_minor_ticks(\u001b[39mlen\u001b[39m(minor_locs))\n", "The pipeline has the following attributes: \n",
"File \u001b[1;32mc:\\ProgramData\\Anaconda3\\envs\\Georg_DT_Slot3\\lib\\site-packages\\matplotlib\\axis.py:1282\u001b[0m, in \u001b[0;36mAxis.get_minorticklocs\u001b[1;34m(self)\u001b[0m\n\u001b[0;32m 1280\u001b[0m \u001b[39m\"\"\"Return this Axis' minor tick locations in data coordinates.\"\"\"\u001b[39;00m\n\u001b[0;32m 1281\u001b[0m \u001b[39m# Remove minor ticks duplicating major ticks.\u001b[39;00m\n\u001b[1;32m-> 1282\u001b[0m major_locs \u001b[39m=\u001b[39m \u001b[39mself\u001b[39;49m\u001b[39m.\u001b[39;49mmajor\u001b[39m.\u001b[39;49mlocator()\n\u001b[0;32m 1283\u001b[0m minor_locs \u001b[39m=\u001b[39m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39mminor\u001b[39m.\u001b[39mlocator()\n\u001b[0;32m 1284\u001b[0m transform \u001b[39m=\u001b[39m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39m_scale\u001b[39m.\u001b[39mget_transform()\n", "----------------------------- \n",
"File \u001b[1;32mc:\\ProgramData\\Anaconda3\\envs\\Georg_DT_Slot3\\lib\\site-packages\\matplotlib\\ticker.py:2114\u001b[0m, in \u001b[0;36mMaxNLocator.__call__\u001b[1;34m(self)\u001b[0m\n\u001b[0;32m 2112\u001b[0m \u001b[39mdef\u001b[39;00m \u001b[39m__call__\u001b[39m(\u001b[39mself\u001b[39m):\n\u001b[0;32m 2113\u001b[0m vmin, vmax \u001b[39m=\u001b[39m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39maxis\u001b[39m.\u001b[39mget_view_interval()\n\u001b[1;32m-> 2114\u001b[0m \u001b[39mreturn\u001b[39;00m \u001b[39mself\u001b[39;49m\u001b[39m.\u001b[39;49mtick_values(vmin, vmax)\n", "Length = 1013.0 m \n",
"File \u001b[1;32mc:\\ProgramData\\Anaconda3\\envs\\Georg_DT_Slot3\\lib\\site-packages\\matplotlib\\ticker.py:2122\u001b[0m, in \u001b[0;36mMaxNLocator.tick_values\u001b[1;34m(self, vmin, vmax)\u001b[0m\n\u001b[0;32m 2119\u001b[0m vmin \u001b[39m=\u001b[39m \u001b[39m-\u001b[39mvmax\n\u001b[0;32m 2120\u001b[0m vmin, vmax \u001b[39m=\u001b[39m mtransforms\u001b[39m.\u001b[39mnonsingular(\n\u001b[0;32m 2121\u001b[0m vmin, vmax, expander\u001b[39m=\u001b[39m\u001b[39m1e-13\u001b[39m, tiny\u001b[39m=\u001b[39m\u001b[39m1e-14\u001b[39m)\n\u001b[1;32m-> 2122\u001b[0m locs \u001b[39m=\u001b[39m \u001b[39mself\u001b[39;49m\u001b[39m.\u001b[39;49m_raw_ticks(vmin, vmax)\n\u001b[0;32m 2124\u001b[0m prune \u001b[39m=\u001b[39m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39m_prune\n\u001b[0;32m 2125\u001b[0m \u001b[39mif\u001b[39;00m prune \u001b[39m==\u001b[39m \u001b[39m'\u001b[39m\u001b[39mlower\u001b[39m\u001b[39m'\u001b[39m:\n", "Diameter = 0.9 m \n",
"File \u001b[1;32mc:\\ProgramData\\Anaconda3\\envs\\Georg_DT_Slot3\\lib\\site-packages\\matplotlib\\ticker.py:2061\u001b[0m, in \u001b[0;36mMaxNLocator._raw_ticks\u001b[1;34m(self, vmin, vmax)\u001b[0m\n\u001b[0;32m 2059\u001b[0m \u001b[39mif\u001b[39;00m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39m_nbins \u001b[39m==\u001b[39m \u001b[39m'\u001b[39m\u001b[39mauto\u001b[39m\u001b[39m'\u001b[39m:\n\u001b[0;32m 2060\u001b[0m \u001b[39mif\u001b[39;00m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39maxis \u001b[39mis\u001b[39;00m \u001b[39mnot\u001b[39;00m \u001b[39mNone\u001b[39;00m:\n\u001b[1;32m-> 2061\u001b[0m nbins \u001b[39m=\u001b[39m np\u001b[39m.\u001b[39mclip(\u001b[39mself\u001b[39;49m\u001b[39m.\u001b[39;49maxis\u001b[39m.\u001b[39;49mget_tick_space(),\n\u001b[0;32m 2062\u001b[0m \u001b[39mmax\u001b[39m(\u001b[39m1\u001b[39m, \u001b[39mself\u001b[39m\u001b[39m.\u001b[39m_min_n_ticks \u001b[39m-\u001b[39m \u001b[39m1\u001b[39m), \u001b[39m9\u001b[39m)\n\u001b[0;32m 2063\u001b[0m \u001b[39melse\u001b[39;00m:\n\u001b[0;32m 2064\u001b[0m nbins \u001b[39m=\u001b[39m \u001b[39m9\u001b[39m\n", "Hydraulic head = 105.0 m \n",
"File \u001b[1;32mc:\\ProgramData\\Anaconda3\\envs\\Georg_DT_Slot3\\lib\\site-packages\\matplotlib\\axis.py:2524\u001b[0m, in \u001b[0;36mYAxis.get_tick_space\u001b[1;34m(self)\u001b[0m\n\u001b[0;32m 2523\u001b[0m \u001b[39mdef\u001b[39;00m \u001b[39mget_tick_space\u001b[39m(\u001b[39mself\u001b[39m):\n\u001b[1;32m-> 2524\u001b[0m ends \u001b[39m=\u001b[39m mtransforms\u001b[39m.\u001b[39;49mBbox\u001b[39m.\u001b[39;49mfrom_bounds(\u001b[39m0\u001b[39;49m, \u001b[39m0\u001b[39;49m, \u001b[39m1\u001b[39;49m, \u001b[39m1\u001b[39;49m)\n\u001b[0;32m 2525\u001b[0m ends \u001b[39m=\u001b[39m ends\u001b[39m.\u001b[39mtransformed(\u001b[39mself\u001b[39m\u001b[39m.\u001b[39maxes\u001b[39m.\u001b[39mtransAxes \u001b[39m-\u001b[39m\n\u001b[0;32m 2526\u001b[0m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39mfigure\u001b[39m.\u001b[39mdpi_scale_trans)\n\u001b[0;32m 2527\u001b[0m length \u001b[39m=\u001b[39m ends\u001b[39m.\u001b[39mheight \u001b[39m*\u001b[39m \u001b[39m72\u001b[39m\n", "Number of segments = 50 \n",
"File \u001b[1;32mc:\\ProgramData\\Anaconda3\\envs\\Georg_DT_Slot3\\lib\\site-packages\\matplotlib\\transforms.py:820\u001b[0m, in \u001b[0;36mBbox.from_bounds\u001b[1;34m(x0, y0, width, height)\u001b[0m\n\u001b[0;32m 813\u001b[0m \u001b[39m@staticmethod\u001b[39m\n\u001b[0;32m 814\u001b[0m \u001b[39mdef\u001b[39;00m \u001b[39mfrom_bounds\u001b[39m(x0, y0, width, height):\n\u001b[0;32m 815\u001b[0m \u001b[39m\"\"\"\u001b[39;00m\n\u001b[0;32m 816\u001b[0m \u001b[39m Create a new `Bbox` from *x0*, *y0*, *width* and *height*.\u001b[39;00m\n\u001b[0;32m 817\u001b[0m \n\u001b[0;32m 818\u001b[0m \u001b[39m *width* and *height* may be negative.\u001b[39;00m\n\u001b[0;32m 819\u001b[0m \u001b[39m \"\"\"\u001b[39;00m\n\u001b[1;32m--> 820\u001b[0m \u001b[39mreturn\u001b[39;00m Bbox\u001b[39m.\u001b[39;49mfrom_extents(x0, y0, x0 \u001b[39m+\u001b[39;49m width, y0 \u001b[39m+\u001b[39;49m height)\n", "Number of nodes = 51 \n",
"File \u001b[1;32mc:\\ProgramData\\Anaconda3\\envs\\Georg_DT_Slot3\\lib\\site-packages\\matplotlib\\transforms.py:839\u001b[0m, in \u001b[0;36mBbox.from_extents\u001b[1;34m(minpos, *args)\u001b[0m\n\u001b[0;32m 822\u001b[0m \u001b[39m@staticmethod\u001b[39m\n\u001b[0;32m 823\u001b[0m \u001b[39mdef\u001b[39;00m \u001b[39mfrom_extents\u001b[39m(\u001b[39m*\u001b[39margs, minpos\u001b[39m=\u001b[39m\u001b[39mNone\u001b[39;00m):\n\u001b[0;32m 824\u001b[0m \u001b[39m\"\"\"\u001b[39;00m\n\u001b[0;32m 825\u001b[0m \u001b[39m Create a new Bbox from *left*, *bottom*, *right* and *top*.\u001b[39;00m\n\u001b[0;32m 826\u001b[0m \n\u001b[1;32m (...)\u001b[0m\n\u001b[0;32m 837\u001b[0m \u001b[39m scales where negative bounds result in floating point errors.\u001b[39;00m\n\u001b[0;32m 838\u001b[0m \u001b[39m \"\"\"\u001b[39;00m\n\u001b[1;32m--> 839\u001b[0m bbox \u001b[39m=\u001b[39m Bbox(np\u001b[39m.\u001b[39;49mreshape(args, (\u001b[39m2\u001b[39;49m, \u001b[39m2\u001b[39;49m)))\n\u001b[0;32m 840\u001b[0m \u001b[39mif\u001b[39;00m minpos \u001b[39mis\u001b[39;00m \u001b[39mnot\u001b[39;00m \u001b[39mNone\u001b[39;00m:\n\u001b[0;32m 841\u001b[0m bbox\u001b[39m.\u001b[39m_minpos[:] \u001b[39m=\u001b[39m minpos\n", "Length per segments = 20.26 m \n",
"File \u001b[1;32mc:\\ProgramData\\Anaconda3\\envs\\Georg_DT_Slot3\\lib\\site-packages\\matplotlib\\transforms.py:775\u001b[0m, in \u001b[0;36mBbox.__init__\u001b[1;34m(self, points, **kwargs)\u001b[0m\n\u001b[0;32m 768\u001b[0m \u001b[39m\"\"\"\u001b[39;00m\n\u001b[0;32m 769\u001b[0m \u001b[39mParameters\u001b[39;00m\n\u001b[0;32m 770\u001b[0m \u001b[39m----------\u001b[39;00m\n\u001b[0;32m 771\u001b[0m \u001b[39mpoints : ndarray\u001b[39;00m\n\u001b[0;32m 772\u001b[0m \u001b[39m A 2x2 numpy array of the form ``[[x0, y0], [x1, y1]]``.\u001b[39;00m\n\u001b[0;32m 773\u001b[0m \u001b[39m\"\"\"\u001b[39;00m\n\u001b[0;32m 774\u001b[0m \u001b[39msuper\u001b[39m()\u001b[39m.\u001b[39m\u001b[39m__init__\u001b[39m(\u001b[39m*\u001b[39m\u001b[39m*\u001b[39mkwargs)\n\u001b[1;32m--> 775\u001b[0m points \u001b[39m=\u001b[39m np\u001b[39m.\u001b[39;49masarray(points, \u001b[39mfloat\u001b[39;49m)\n\u001b[0;32m 776\u001b[0m \u001b[39mif\u001b[39;00m points\u001b[39m.\u001b[39mshape \u001b[39m!=\u001b[39m (\u001b[39m2\u001b[39m, \u001b[39m2\u001b[39m):\n\u001b[0;32m 777\u001b[0m \u001b[39mraise\u001b[39;00m \u001b[39mValueError\u001b[39;00m(\u001b[39m'\u001b[39m\u001b[39mBbox points must be of the form \u001b[39m\u001b[39m'\u001b[39m\n\u001b[0;32m 778\u001b[0m \u001b[39m'\u001b[39m\u001b[39m\"\u001b[39m\u001b[39m[[x0, y0], [x1, y1]]\u001b[39m\u001b[39m\"\u001b[39m\u001b[39m.\u001b[39m\u001b[39m'\u001b[39m)\n", "Pipeline angle = 0.104 rad \n",
"\u001b[1;31mKeyboardInterrupt\u001b[0m: " "Pipeline angle = 5.95° \n",
"Darcy friction factor = 0.014 \n",
"Density of liquid = 1000.0 kg/m³ \n",
"Pressure wave vel. = 500.0 m/s \n",
"Simulation timestep = 0.04052 s \n",
"----------------------------- \n",
"Velocity and pressure distribution are vectors and are accessible by the .v and .p attribute of the pipeline object\n"
] ]
} }
], ],
@@ -246,12 +278,12 @@
" plt.pause(0.000001)\n", " plt.pause(0.000001)\n",
"\n", "\n",
"reservoir.get_info(full=True)\n", "reservoir.get_info(full=True)\n",
"pipe.get_info(full=True)" "pipe.get_info()"
] ]
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 12, "execution_count": 7,
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [

View File

@@ -2,7 +2,7 @@
"cells": [ "cells": [
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 27, "execution_count": 1,
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
@@ -23,7 +23,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 28, "execution_count": 2,
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
@@ -85,7 +85,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 29, "execution_count": 3,
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
@@ -93,7 +93,7 @@
"offset_pressure = pressure_conversion(Pip_head,'mws',pUnit_calc)\n", "offset_pressure = pressure_conversion(Pip_head,'mws',pUnit_calc)\n",
"\n", "\n",
"# Upstream reservoir\n", "# Upstream reservoir\n",
"reservoir = Ausgleichsbecken_class(Res_area_base,Res_area_out,Res_dt,Res_level_crit_lo,Res_level_crit_hi,rho)\n", "reservoir = Ausgleichsbecken_class(Res_area_base,Res_area_out,Res_dt,pUnit_conv,Res_level_crit_lo,Res_level_crit_hi,rho)\n",
"reservoir.set_steady_state(flux_init,level_init)\n", "reservoir.set_steady_state(flux_init,level_init)\n",
"\n", "\n",
"# downstream turbine\n", "# downstream turbine\n",
@@ -108,7 +108,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 30, "execution_count": 4,
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
@@ -124,7 +124,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 31, "execution_count": 5,
"metadata": {}, "metadata": {},
"outputs": [ "outputs": [
{ {
@@ -181,7 +181,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 32, "execution_count": 6,
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [

View File

@@ -1,6 +1,7 @@
import numpy as np import numpy as np
#based on https://en.wikipedia.org/wiki/PID_controller#Discrete_implementation #based on https://en.wikipedia.org/wiki/PID_controller#Discrete_implementation
# performance parameters for controllers
def trap_int(vec,timestep): def trap_int(vec,timestep):
# numerical integration via the trapeziod rule to calculate the performance parameters # numerical integration via the trapeziod rule to calculate the performance parameters
l = np.size(vec) l = np.size(vec)
@@ -41,6 +42,7 @@ def ITAE_fun(error_history,timestep):
itae = trap_int(np.abs(e),dt) itae = trap_int(np.abs(e),dt)
return itae return itae
# P controller
class P_controller_class: class P_controller_class:
# def __init__(self,setpoint,proportionality_constant): # def __init__(self,setpoint,proportionality_constant):
# self.SP = setpoint # self.SP = setpoint
@@ -72,14 +74,14 @@ class P_controller_class:
def __init__(self): def __init__(self):
pass pass
# PI controller
class PI_controller_class: class PI_controller_class:
# init # init
def __init__(self,setpoint,deadband,proportionality_constant,Ti,timestep,lower_limit=0.,upper_limit=1.): def __init__(self,setpoint,deadband,proportionality_constant,Ti,timestep,lower_limit=0.,upper_limit=1.):
self.SP = setpoint self.SP = setpoint
self.db = deadband self.db = deadband
self.Kp = proportionality_constant self.Kp = proportionality_constant
self.Ti = Ti # integration time self.Ti = Ti # ~integration time
self.dt = timestep self.dt = timestep
# use a list to be able to append more easily - will get converted to np.array when needed # use a list to be able to append more easily - will get converted to np.array when needed
self.error_history = [0] self.error_history = [0]
@@ -88,15 +90,13 @@ class PI_controller_class:
self.cv_upper_limit = upper_limit # limits for the controll variable self.cv_upper_limit = upper_limit # limits for the controll variable
# setter # setter
def set_setpoint(self,setpoint): def set_setpoint(self,setpoint):
self.SP = setpoint self.SP = setpoint
def set_control_variable(self,control_variable, display_warning=True): def set_control_variable(self,control_variable, display_warning=True):
if display_warning == True: if display_warning == True:
print('WARNING! You are setting the control variable of the PI controller manually \ print('WARNING! You are setting the control variable of the PI controller manually! \
and are not using the .update_controll_variable() method') Consider using the .update_controll_variable() method instead.')
self.control_variable = control_variable self.control_variable = control_variable
# getter # getter
@@ -167,15 +167,14 @@ class PI_controller_class:
# only if that is the case, change control variable # only if that is the case, change control variable
if abs(self.error) > self.db: if abs(self.error) > self.db:
new_control = cv+Kp*(e0-e1)+dt/Ti*e0 new_control = cv+Kp*(e0-e1)+dt/Ti*e0
# ensure that the controll variable stays within the predefined limits
if new_control < self.cv_lower_limit:
new_control = self.cv_lower_limit
if new_control > self.cv_upper_limit:
new_control = self.cv_upper_limit
else: else:
new_control = cv new_control = cv
# ensure that the controll variable stays within the predefined limits
if new_control < self.cv_lower_limit:
new_control = self.cv_lower_limit
if new_control > self.cv_upper_limit:
new_control = self.cv_upper_limit
# set the control variable attribute # set the control variable attribute
self.set_control_variable(new_control,display_warning=False) self.set_control_variable(new_control,display_warning=False)

View File

@@ -11,8 +11,8 @@ sys.path.append(parent)
from functions.pressure_conversion import pressure_conversion from functions.pressure_conversion import pressure_conversion
class Francis_Turbine: class Francis_Turbine:
# units # units
# make sure that units and print units are the same # make sure that units and display units are the same
# units are used to label graphs and disp units are used to have a bearable format when using pythons print() # units are used to label graphs and disp units are used to have a bearable format when using pythons print()
density_unit = r'$\mathrm{kg}/\mathrm{m}^3$' density_unit = r'$\mathrm{kg}/\mathrm{m}^3$'
flux_unit = r'$\mathrm{m}^3/\mathrm{s}$' flux_unit = r'$\mathrm{m}^3/\mathrm{s}$'
@@ -32,7 +32,16 @@ class Francis_Turbine:
g = 9.81 # m/s² gravitational acceleration g = 9.81 # m/s² gravitational acceleration
# init # init
def __init__(self, Q_nenn,p_nenn,t_closing,timestep,pressure_unit_disp): def __init__(self,Q_nenn,p_nenn,t_closing,timestep,pressure_unit_disp):
"""
Creates a turbine with given attributes in this order: \n
Nominal flux [m³/s] \n
Nominal pressure [Pa] \n
Closing time [s] \n
Simulation timestep [s] \n
Pressure unit for displaying [string] \n
"""
self.Q_n = Q_nenn # nominal flux self.Q_n = Q_nenn # nominal flux
self.p_n = p_nenn # nominal pressure self.p_n = p_nenn # nominal pressure
self.LA_n = 1. # 100% # nominal Leitapparatöffnung self.LA_n = 1. # 100% # nominal Leitapparatöffnung
@@ -42,21 +51,21 @@ class Francis_Turbine:
self.pressure_unit_disp = pressure_unit_disp self.pressure_unit_disp = pressure_unit_disp
# initialize for get_info() - parameters will be converted to display -1 if not overwritten # initialize for get_info()
self.p = pressure_conversion(-1,self.pressure_unit_disp,self.pressure_unit) self.p = -np.inf
self.Q = -1. self.Q = -np.inf
self.LA = -0.01 self.LA = -np.inf
# setter # setter - set attributes
def set_LA(self,LA,display_warning=True): def set_LA(self,LA,display_warning=True):
# set Leitapparatöffnung
self.LA = LA
# warn user, that the .set_LA() method should not be used ot set LA manually # warn user, that the .set_LA() method should not be used ot set LA manually
if display_warning == True: if display_warning == True:
print('You are setting the guide vane opening of the turbine manually. \n \ print('You are setting the guide vane opening of the turbine manually. \n \
This is not an intended use of this method. \n \ This is not an intended use of this method. \n \
Refer to the .update_LA() method instead.') Refer to the .update_LA() method instead.')
# set Leitapparatöffnung
self.LA = LA
def set_pressure(self,pressure): def set_pressure(self,pressure):
# set pressure in front of the turbine # set pressure in front of the turbine
@@ -70,7 +79,7 @@ class Francis_Turbine:
raise Exception('LA out of range [0;1]') raise Exception('LA out of range [0;1]')
self.set_LA(ss_LA,display_warning=False) self.set_LA(ss_LA,display_warning=False)
#getter #getter - get attributes
def get_current_Q(self): def get_current_Q(self):
# return the flux through the turbine, based on the current pressure in front # return the flux through the turbine, based on the current pressure in front
# of the turbine and the Leitapparatöffnung # of the turbine and the Leitapparatöffnung
@@ -83,8 +92,11 @@ class Francis_Turbine:
def get_current_LA(self): def get_current_LA(self):
return self.LA return self.LA
def get_current_pressure(self): def get_current_pressure(self,disp_flag=True):
return pressure_conversion(self.p,self.pressure_unit,self.pressure_unit_disp) if disp_flag == True:
return pressure_conversion(self.p,self.pressure_unit,self.pressure_unit_disp)
else:
return self.p
def get_info(self, full = False): def get_info(self, full = False):
new_line = '\n' new_line = '\n'
@@ -135,7 +147,9 @@ class Francis_Turbine:
# methods # methods
def converge(self,convergence_parameters): def converge(self,convergence_parameters):
# small numerical disturbances (~1e-12 m/s) in the velocity can get amplified at the turbine node, because the new velocity of the turbine and the # small numerical disturbances (~1e-12 m/s) in the velocity can get amplified at the turbine node, because the new velocity of the turbine and the
# new pressure from the forward characteristic are not compatible. # new pressure from the forward characteristic are not perfectly compatible.
# Therefore, iterate the flux and the pressure so long, until they converge
eps = 1e-12 # convergence criterion: iteration change < eps eps = 1e-12 # convergence criterion: iteration change < eps
iteration_change = 1. # change in Q from one iteration to the next iteration_change = 1. # change in Q from one iteration to the next
i = 0 # safety variable. break loop if it exceeds 1e6 iterations i = 0 # safety variable. break loop if it exceeds 1e6 iterations
@@ -150,20 +164,18 @@ class Francis_Turbine:
rho = convergence_parameters[7] # density of the liquid rho = convergence_parameters[7] # density of the liquid
dt = convergence_parameters[8] # timestep of the characteristic method dt = convergence_parameters[8] # timestep of the characteristic method
p_old = self.get_current_pressure()
Q_old = self.get_current_Q() Q_old = self.get_current_Q()
v_old = Q_old/area_pipe v_old = Q_old/area_pipe
while iteration_change > eps: while iteration_change > eps:
self.set_pressure(p_old) p_new = p-rho*c*(v_old-v)+rho*c*dt*g*np.sin(alpha)-f_D*rho*c*dt/(2*D)*abs(v)*v
self.set_pressure(p_new)
Q_new = self.get_current_Q() Q_new = self.get_current_Q()
v_new = Q_new/area_pipe v_new = Q_new/area_pipe
p_new = p-rho*c*(v_old-v)+rho*c*dt*g*np.sin(alpha)-f_D*rho*c*dt/(2*D)*abs(v)*v
iteration_change = abs(Q_old-Q_new) iteration_change = abs(Q_old-Q_new)
Q_old = Q_new.copy() Q_old = Q_new.copy()
p_old = p_new.copy()
v_old = v_new.copy() v_old = v_new.copy()
i = i+1 i = i+1
if i == 1e6: if i == 1e6:

View File

@@ -2,7 +2,7 @@
"cells": [ "cells": [
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 8, "execution_count": null,
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
@@ -23,7 +23,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 9, "execution_count": null,
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
@@ -35,12 +35,10 @@
"pUnit_calc = 'Pa' # [text] DO NOT CHANGE! for pressure conversion in print statements and plot labels \n", "pUnit_calc = 'Pa' # [text] DO NOT CHANGE! for pressure conversion in print statements and plot labels \n",
"pUnit_conv = 'mWS' # [text] for pressure conversion in print statements and plot labels\n", "pUnit_conv = 'mWS' # [text] for pressure conversion in print statements and plot labels\n",
"\n", "\n",
"\n",
" # for Turbine\n", " # for Turbine\n",
"Tur_Q_nenn = 0.85 # [m³/s] nominal flux of turbine \n", "Tur_Q_nenn = 0.85 # [m³/s] nominal flux of turbine \n",
"Tur_p_nenn = pressure_conversion(10.6,'bar',pUnit_calc) # [Pa] nominal pressure of turbine \n", "Tur_p_nenn = pressure_conversion(10.6,'bar',pUnit_calc) # [Pa] nominal pressure of turbine \n",
"Tur_closingTime = 90. # [s] closing time of turbine\n", "Tur_closingTime = 90. # [s] closing time of turbine\n",
"\n",
"\n", "\n",
" # for PI controller\n", " # for PI controller\n",
"Con_targetLevel = 8. # [m]\n", "Con_targetLevel = 8. # [m]\n",
@@ -48,23 +46,21 @@
"Con_T_i = 10. # [s] timespan in which a steady state error is corrected by the intergal term\n", "Con_T_i = 10. # [s] timespan in which a steady state error is corrected by the intergal term\n",
"Con_deadbandRange = 0.05 # [m] Deadband range around targetLevel for which the controller does NOT intervene\n", "Con_deadbandRange = 0.05 # [m] Deadband range around targetLevel for which the controller does NOT intervene\n",
"\n", "\n",
"\n",
" # for pipeline\n", " # for pipeline\n",
"Pip_length = (535.+478.) # [m] length of pipeline\n", "Pip_length = (535.+478.) # [m] length of pipeline\n",
"Pip_dia = 0.9 # [m] diameter of pipeline\n", "Pip_dia = 0.9 # [m] diameter of pipeline\n",
"Pip_area = Pip_dia**2/4*np.pi # [m²] crossectional area of pipeline\n", "Pip_area = Pip_dia**2/4*np.pi # [m²] crossectional area of pipeline\n",
"Pip_head = 105. # [m] hydraulic head of pipeline without reservoir\n", "Pip_head = 105. # [m] hydraulic head of pipeline without reservoir\n",
"Pip_angle = np.arcsin(Pip_head/Pip_length) # [rad] elevation angle of pipeline \n", "Pip_angle = np.arcsin(Pip_head/Pip_length) # [rad] elevation angle of pipeline \n",
"Pip_n_seg = 50 # [-] number of pipe segments in discretization\n", "Pip_n_seg = 50 # [-] number of pipe segments in discretization\n",
"Pip_f_D = 0.014 # [-] Darcy friction factor\n", "Pip_f_D = 0.014 # [-] Darcy friction factor\n",
"Pip_pw_vel = 500. # [m/s] propagation velocity of the pressure wave (pw) in the given pipeline\n", "Pip_pw_vel = 500. # [m/s] propagation velocity of the pressure wave (pw) in the given pipeline\n",
" # derivatives of the pipeline constants\n", " # derivatives of the pipeline constants\n",
"Pip_dx = Pip_length/Pip_n_seg # [m] length of each pipe segment\n", "Pip_dx = Pip_length/Pip_n_seg # [m] length of each pipe segment\n",
"Pip_dt = Pip_dx/Pip_pw_vel # [s] timestep according to method of characteristics\n", "Pip_dt = Pip_dx/Pip_pw_vel # [s] timestep according to method of characteristics\n",
"Pip_nn = Pip_n_seg+1 # [1] number of nodes\n", "Pip_nn = Pip_n_seg+1 # [1] number of nodes\n",
"Pip_x_vec = np.arange(0,Pip_nn,1)*Pip_dx # [m] vector holding the distance of each node from the upstream reservoir along the pipeline\n", "Pip_x_vec = np.arange(0,Pip_nn,1)*Pip_dx # [m] vector holding the distance of each node from the upstream reservoir along the pipeline\n",
"Pip_h_vec = np.arange(0,Pip_nn,1)*Pip_head/Pip_n_seg # [m] vector holding the vertival distance of each node from the upstream reservoir\n", "Pip_h_vec = np.arange(0,Pip_nn,1)*Pip_head/Pip_n_seg # [m] vector holding the vertival distance of each node from the upstream reservoir\n",
"\n",
"\n", "\n",
" # for reservoir\n", " # for reservoir\n",
"Res_area_base = 74. # [m²] total base are of the cuboid reservoir \n", "Res_area_base = 74. # [m²] total base are of the cuboid reservoir \n",
@@ -76,16 +72,16 @@
"Res_dt = Pip_dt/Res_nt # [s] harmonised timestep of reservoir time evolution\n", "Res_dt = Pip_dt/Res_nt # [s] harmonised timestep of reservoir time evolution\n",
"\n", "\n",
" # for general simulation\n", " # for general simulation\n",
"flux_init = Tur_Q_nenn/1.1 # [m³/s] initial flux through whole system for steady state initialization \n", "flux_init = Tur_Q_nenn/1.1 # [m³/s] initial flux through whole system for steady state initialization \n",
"level_init = Con_targetLevel # [m] initial water level in upstream reservoir for steady state initialization\n", "level_init = Con_targetLevel # [m] initial water level in upstream reservoir for steady state initialization\n",
"simTime_target = 600. # [s] target for total simulation time (will vary slightly to fit with Pip_dt)\n", "simTime_target = 100. # [s] target for total simulation time (will vary slightly to fit with Pip_dt)\n",
"nt = int(simTime_target//Pip_dt) # [1] Number of timesteps of the whole system\n", "nt = int(simTime_target//Pip_dt) # [1] Number of timesteps of the whole system\n",
"t_vec = np.arange(0,nt+1,1)*Pip_dt # [s] time vector. At each step of t_vec the system parameters are stored\n" "t_vec = np.arange(0,nt+1,1)*Pip_dt # [s] time vector. At each step of t_vec the system parameters are stored\n"
] ]
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 10, "execution_count": null,
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
@@ -96,8 +92,8 @@
"reservoir.set_steady_state(flux_init,level_init)\n", "reservoir.set_steady_state(flux_init,level_init)\n",
"\n", "\n",
"# pipeline\n", "# pipeline\n",
"pipe = Druckrohrleitung_class(Pip_length,Pip_dia,Pip_n_seg,Pip_angle,Pip_f_D,Pip_pw_vel,Pip_dt,pUnit_conv,rho)\n", "pipe = Druckrohrleitung_class(Pip_length,Pip_dia,Pip_head,Pip_n_seg,Pip_f_D,Pip_pw_vel,Pip_dt,pUnit_conv,rho)\n",
"pipe.set_steady_state(flux_init,level_init,Res_area_base,Pip_x_vec,Pip_h_vec)\n", "pipe.set_steady_state(flux_init,reservoir.get_current_pressure())\n",
"\n", "\n",
"# downstream turbine\n", "# downstream turbine\n",
"turbine = Francis_Turbine(Tur_Q_nenn,Tur_p_nenn,Tur_closingTime,Pip_dt,pUnit_conv)\n", "turbine = Francis_Turbine(Tur_Q_nenn,Tur_p_nenn,Tur_closingTime,Pip_dt,pUnit_conv)\n",
@@ -114,7 +110,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 11, "execution_count": null,
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
@@ -133,22 +129,22 @@
"Q_in_vec = np.zeros_like(t_vec)\n", "Q_in_vec = np.zeros_like(t_vec)\n",
"Q_in_vec[0] = flux_init\n", "Q_in_vec[0] = flux_init\n",
"\n", "\n",
"v_boundary_res = np.zeros_like(t_vec)\n", "v_boundary_res = np.zeros_like(t_vec)\n",
"v_boundary_tur = np.zeros_like(t_vec)\n", "v_boundary_tur = np.zeros_like(t_vec)\n",
"Q_boundary_res = np.zeros_like(t_vec)\n", "Q_boundary_res = np.zeros_like(t_vec)\n",
"Q_boundary_tur = np.zeros_like(t_vec)\n", "Q_boundary_tur = np.zeros_like(t_vec)\n",
"p_boundary_res = np.zeros_like(t_vec)\n", "p_boundary_res = np.zeros_like(t_vec)\n",
"p_boundary_tur = np.zeros_like(t_vec)\n", "p_boundary_tur = np.zeros_like(t_vec)\n",
"\n", "\n",
"level_vec = np.full_like(t_vec,level_init) # level at the end of each pipeline timestep\n", "level_vec = np.full_like(t_vec,level_init) # level at the end of each pipeline timestep\n",
"volume_vec = np.full_like(t_vec,reservoir.get_current_volume()) # volume at the end of each pipeline timestep\n", "volume_vec = np.full_like(t_vec,reservoir.get_current_volume()) # volume at the end of each pipeline timestep\n",
"\n", "\n",
"v_boundary_res[0] = v_old[0]\n", "v_boundary_res[0] = v_old[0]\n",
"v_boundary_tur[0] = v_old[-1] \n", "v_boundary_tur[0] = v_old[-1] \n",
"Q_boundary_res[0] = Q_old[0]\n", "Q_boundary_res[0] = Q_old[0]\n",
"Q_boundary_tur[0] = Q_old[-1]\n", "Q_boundary_tur[0] = Q_old[-1]\n",
"p_boundary_res[0] = p_old[0]\n", "p_boundary_res[0] = p_old[0]\n",
"p_boundary_tur[0] = p_old[-1]\n", "p_boundary_tur[0] = p_old[-1]\n",
"\n", "\n",
"LA_soll_vec = np.full_like(t_vec,turbine.get_current_LA())\n", "LA_soll_vec = np.full_like(t_vec,turbine.get_current_LA())\n",
"LA_ist_vec = np.full_like(t_vec,turbine.get_current_LA())\n", "LA_ist_vec = np.full_like(t_vec,turbine.get_current_LA())\n",
@@ -158,7 +154,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 12, "execution_count": null,
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
@@ -176,8 +172,8 @@
"axs1[1].set_ylabel(r'$Q$ [$\\mathrm{m}^3 / \\mathrm{s}$]')\n", "axs1[1].set_ylabel(r'$Q$ [$\\mathrm{m}^3 / \\mathrm{s}$]')\n",
"lo_p, = axs1[0].plot(Pip_x_vec,pressure_conversion(p_old,pUnit_calc, pUnit_conv),marker='.')\n", "lo_p, = axs1[0].plot(Pip_x_vec,pressure_conversion(p_old,pUnit_calc, pUnit_conv),marker='.')\n",
"lo_q, = axs1[1].plot(Pip_x_vec,Q_old,marker='.')\n", "lo_q, = axs1[1].plot(Pip_x_vec,Q_old,marker='.')\n",
"lo_pmin, = axs1[0].plot(Pip_x_vec,pipe.get_lowest_pressure_per_node(disp=True),c='red')\n", "lo_pmin, = axs1[0].plot(Pip_x_vec,pipe.get_lowest_pressure_per_node(disp_flag=True),c='red')\n",
"lo_pmax, = axs1[0].plot(Pip_x_vec,pipe.get_highest_pressure_per_node(disp=True),c='red')\n", "lo_pmax, = axs1[0].plot(Pip_x_vec,pipe.get_highest_pressure_per_node(disp_flag=True),c='red')\n",
"lo_qmin, = axs1[1].plot(Pip_x_vec,pipe.get_lowest_flux_per_node(),c='red')\n", "lo_qmin, = axs1[1].plot(Pip_x_vec,pipe.get_lowest_flux_per_node(),c='red')\n",
"lo_qmax, = axs1[1].plot(Pip_x_vec,pipe.get_highest_flux_per_node(),c='red')\n", "lo_qmax, = axs1[1].plot(Pip_x_vec,pipe.get_highest_flux_per_node(),c='red')\n",
"\n", "\n",
@@ -191,7 +187,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 13, "execution_count": null,
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
@@ -212,7 +208,7 @@
" # calculate the Con_T_ime evolution of the reservoir level within each pipeline timestep to avoid runaway numerical error\n", " # calculate the Con_T_ime evolution of the reservoir level within each pipeline timestep to avoid runaway numerical error\n",
" for it_res in range(Res_nt):\n", " for it_res in range(Res_nt):\n",
" reservoir.timestep_reservoir_evolution() \n", " reservoir.timestep_reservoir_evolution() \n",
" level_vec[it_pipe] = reservoir.get_current_level() \n", " level_vec[it_pipe] = reservoir.get_current_level() \n",
" volume_vec[it_pipe] = reservoir.get_current_volume() \n", " volume_vec[it_pipe] = reservoir.get_current_volume() \n",
"\n", "\n",
" # get the control variable\n", " # get the control variable\n",
@@ -247,7 +243,6 @@
" v_old = pipe.get_current_velocity_distribution()\n", " v_old = pipe.get_current_velocity_distribution()\n",
" Q_old = pipe.get_current_flux_distribution()\n", " Q_old = pipe.get_current_flux_distribution()\n",
"\n", "\n",
"\n",
" # plot some stuff\n", " # plot some stuff\n",
" # remove line-objects to autoscale axes (there is definetly a better way, but this works ¯\\_(ツ)_/¯ )\n", " # remove line-objects to autoscale axes (there is definetly a better way, but this works ¯\\_(ツ)_/¯ )\n",
" lo_p.remove()\n", " lo_p.remove()\n",
@@ -257,9 +252,9 @@
" lo_qmin.remove()\n", " lo_qmin.remove()\n",
" lo_qmax.remove()\n", " lo_qmax.remove()\n",
" # plot new pressure and velocity distribution in the pipeline\n", " # plot new pressure and velocity distribution in the pipeline\n",
" lo_p, = axs1[0].plot(Pip_x_vec,pipe.get_current_pressure_distribution(disp=True),marker='.',c='blue')\n", " lo_p, = axs1[0].plot(Pip_x_vec,pipe.get_current_pressure_distribution(disp_flag=True),marker='.',c='blue')\n",
" lo_pmin, = axs1[0].plot(Pip_x_vec,pipe.get_lowest_pressure_per_node(disp=True),c='red')\n", " lo_pmin, = axs1[0].plot(Pip_x_vec,pipe.get_lowest_pressure_per_node(disp_flag=True),c='red')\n",
" lo_pmax, = axs1[0].plot(Pip_x_vec,pipe.get_highest_pressure_per_node(disp=True),c='red')\n", " lo_pmax, = axs1[0].plot(Pip_x_vec,pipe.get_highest_pressure_per_node(disp_flag=True),c='red')\n",
" lo_q, = axs1[1].plot(Pip_x_vec,pipe.get_current_flux_distribution(),marker='.',c='blue')\n", " lo_q, = axs1[1].plot(Pip_x_vec,pipe.get_current_flux_distribution(),marker='.',c='blue')\n",
" lo_qmin, = axs1[1].plot(Pip_x_vec,pipe.get_lowest_flux_per_node(),c='red')\n", " lo_qmin, = axs1[1].plot(Pip_x_vec,pipe.get_lowest_flux_per_node(),c='red')\n",
" lo_qmax, = axs1[1].plot(Pip_x_vec,pipe.get_highest_flux_per_node(),c='red')\n", " lo_qmax, = axs1[1].plot(Pip_x_vec,pipe.get_highest_flux_per_node(),c='red')\n",
@@ -272,7 +267,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 14, "execution_count": null,
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
@@ -316,8 +311,8 @@
"\n", "\n",
"fig2,axs2 = plt.subplots(1,1)\n", "fig2,axs2 = plt.subplots(1,1)\n",
"axs2.set_title('Min and Max Pressure')\n", "axs2.set_title('Min and Max Pressure')\n",
"axs2.plot(Pip_x_vec,pipe.get_lowest_pressure_per_node(disp=True),c='red')\n", "axs2.plot(Pip_x_vec,pipe.get_lowest_pressure_per_node(disp_flag=True),c='red')\n",
"axs2.plot(Pip_x_vec,pipe.get_highest_pressure_per_node(disp=True),c='red')\n", "axs2.plot(Pip_x_vec,pipe.get_highest_pressure_per_node(disp_flag=True),c='red')\n",
"axs2.set_xlabel(r'$x$ [$\\mathrm{m}$]')\n", "axs2.set_xlabel(r'$x$ [$\\mathrm{m}$]')\n",
"axs2.set_ylabel(r'$p$ ['+pUnit_conv+']')\n", "axs2.set_ylabel(r'$p$ ['+pUnit_conv+']')\n",
"\n", "\n",
@@ -328,13 +323,6 @@
"axs2.set_xlabel(r'$x$ [$\\mathrm{m}$]')\n", "axs2.set_xlabel(r'$x$ [$\\mathrm{m}$]')\n",
"axs2.set_ylabel(r'$Q$ [$\\mathrm{m}^3/\\mathrm{s}$]')\n", "axs2.set_ylabel(r'$Q$ [$\\mathrm{m}^3/\\mathrm{s}$]')\n",
"\n", "\n",
"# axs2[0,1].legend()\n",
"# axs2[1,0].legend()\n",
"# axs2[1,1].legend()\n",
"# # axs2[2,0].legend()\n",
"# # axs2[2,1].legend()\n",
"\n",
"\n",
"fig2.tight_layout()\n", "fig2.tight_layout()\n",
"plt.show()" "plt.show()"
] ]

View File

@@ -2,7 +2,7 @@
"cells": [ "cells": [
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 8, "execution_count": 4,
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
@@ -18,7 +18,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 9, "execution_count": 5,
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
@@ -30,12 +30,10 @@
"pUnit_calc = 'Pa' # [text] DO NOT CHANGE! for pressure conversion in print statements and plot labels \n", "pUnit_calc = 'Pa' # [text] DO NOT CHANGE! for pressure conversion in print statements and plot labels \n",
"pUnit_conv = 'mWS' # [text] for pressure conversion in print statements and plot labels\n", "pUnit_conv = 'mWS' # [text] for pressure conversion in print statements and plot labels\n",
"\n", "\n",
"\n",
" # for Turbine\n", " # for Turbine\n",
"Tur_Q_nenn = 0.85 # [m³/s] nominal flux of turbine \n", "Tur_Q_nenn = 0.85 # [m³/s] nominal flux of turbine \n",
"Tur_p_nenn = pressure_conversion(10.6,'bar',pUnit_calc) # [Pa] nominal pressure of turbine \n", "Tur_p_nenn = pressure_conversion(10.6,'bar',pUnit_calc) # [Pa] nominal pressure of turbine \n",
"Tur_closingTime = 90. # [s] closing time of turbine\n", "Tur_closingTime = 90. # [s] closing time of turbine\n",
"\n",
"\n", "\n",
" # for PI controller\n", " # for PI controller\n",
"Con_targetLevel = 8. # [m]\n", "Con_targetLevel = 8. # [m]\n",
@@ -43,23 +41,21 @@
"Con_T_i = 1000. # [s] timespan in which a steady state error is corrected by the intergal term\n", "Con_T_i = 1000. # [s] timespan in which a steady state error is corrected by the intergal term\n",
"Con_deadbandRange = 0.05 # [m] Deadband range around targetLevel for which the controller does NOT intervene\n", "Con_deadbandRange = 0.05 # [m] Deadband range around targetLevel for which the controller does NOT intervene\n",
"\n", "\n",
"\n",
" # for pipeline\n", " # for pipeline\n",
"Pip_length = (535.+478.) # [m] length of pipeline\n", "Pip_length = (535.+478.) # [m] length of pipeline\n",
"Pip_dia = 0.9 # [m] diameter of pipeline\n", "Pip_dia = 0.9 # [m] diameter of pipeline\n",
"Pip_area = Pip_dia**2/4*np.pi # [m²] crossectional area of pipeline\n", "Pip_area = Pip_dia**2/4*np.pi # [m²] crossectional area of pipeline\n",
"Pip_head = 105. # [m] hydraulic head of pipeline without reservoir\n", "Pip_head = 105. # [m] hydraulic head of pipeline without reservoir\n",
"Pip_angle = np.arcsin(Pip_head/Pip_length) # [rad] elevation angle of pipeline \n", "Pip_angle = np.arcsin(Pip_head/Pip_length) # [rad] elevation angle of pipeline \n",
"Pip_n_seg = 50 # [-] number of pipe segments in discretization\n", "Pip_n_seg = 50 # [-] number of pipe segments in discretization\n",
"Pip_f_D = 0.014 # [-] Darcy friction factor\n", "Pip_f_D = 0.014 # [-] Darcy friction factor\n",
"Pip_pw_vel = 500. # [m/s] propagation velocity of the pressure wave (pw) in the given pipeline\n", "Pip_pw_vel = 500. # [m/s] propagation velocity of the pressure wave (pw) in the given pipeline\n",
" # derivatives of the pipeline constants\n", " # derivatives of the pipeline constants\n",
"Pip_dx = Pip_length/Pip_n_seg # [m] length of each pipe segment\n", "Pip_dx = Pip_length/Pip_n_seg # [m] length of each pipe segment\n",
"Pip_dt = Pip_dx/Pip_pw_vel # [s] timestep according to method of characteristics\n", "Pip_dt = Pip_dx/Pip_pw_vel # [s] timestep according to method of characteristics\n",
"Pip_nn = Pip_n_seg+1 # [1] number of nodes\n", "Pip_nn = Pip_n_seg+1 # [1] number of nodes\n",
"Pip_x_vec = np.arange(0,Pip_nn,1)*Pip_dx # [m] vector holding the distance of each node from the upstream reservoir along the pipeline\n", "Pip_x_vec = np.arange(0,Pip_nn,1)*Pip_dx # [m] vector holding the distance of each node from the upstream reservoir along the pipeline\n",
"Pip_h_vec = np.arange(0,Pip_nn,1)*Pip_head/Pip_n_seg # [m] vector holding the vertival distance of each node from the upstream reservoir\n", "Pip_h_vec = np.arange(0,Pip_nn,1)*Pip_head/Pip_n_seg # [m] vector holding the vertival distance of each node from the upstream reservoir\n",
"\n",
"\n", "\n",
" # for reservoir\n", " # for reservoir\n",
"Res_area_base = 74. # [m²] total base are of the cuboid reservoir \n", "Res_area_base = 74. # [m²] total base are of the cuboid reservoir \n",
@@ -71,28 +67,28 @@
"Res_dt = Pip_dt/Res_nt # [s] harmonised timestep of reservoir time evolution\n", "Res_dt = Pip_dt/Res_nt # [s] harmonised timestep of reservoir time evolution\n",
"\n", "\n",
" # for general simulation\n", " # for general simulation\n",
"flux_init = Tur_Q_nenn/1.1 # [m³/s] initial flux through whole system for steady state initialization \n", "flux_init = Tur_Q_nenn/1.1 # [m³/s] initial flux through whole system for steady state initialization \n",
"level_init = Con_targetLevel # [m] initial water level in upstream reservoir for steady state initialization\n", "level_init = Con_targetLevel # [m] initial water level in upstream reservoir for steady state initialization\n",
"simTime_target = 600. # [s] target for total simulation time (will vary slightly to fit with Pip_dt)\n", "simTime_target = 100. # [s] target for total simulation time (will vary slightly to fit with Pip_dt)\n",
"nt = int(simTime_target//Pip_dt) # [1] Number of timesteps of the whole system\n", "nt = int(simTime_target//Pip_dt) # [1] Number of timesteps of the whole system\n",
"t_vec = np.arange(0,nt+1,1)*Pip_dt # [s] time vector. At each step of t_vec the system parameters are stored\n" "t_vec = np.arange(0,nt+1,1)*Pip_dt # [s] time vector. At each step of t_vec the system parameters are stored\n"
] ]
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 10, "execution_count": 6,
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
"# create objects\n", "# create objects\n",
"\n", "\n",
"# Upstream reservoir\n", "# Upstream reservoir\n",
"reservoir = Ausgleichsbecken_class(Res_area_base,Res_area_out,Res_dt,Res_level_crit_lo,Res_level_crit_hi,rho)\n", "reservoir = Ausgleichsbecken_class(Res_area_base,Res_area_out,Res_dt,pUnit_conv,Res_level_crit_lo,Res_level_crit_hi,rho)\n",
"reservoir.set_steady_state(flux_init,level_init)\n", "reservoir.set_steady_state(flux_init,level_init)\n",
"\n", "\n",
"# pipeline\n", "# pipeline\n",
"pipe = Druckrohrleitung_class(Pip_length,Pip_dia,Pip_n_seg,Pip_angle,Pip_f_D,Pip_pw_vel,Pip_dt,pUnit_conv,rho)\n", "pipe = Druckrohrleitung_class(Pip_length,Pip_dia,Pip_head,Pip_n_seg,Pip_f_D,Pip_pw_vel,Pip_dt,pUnit_conv,rho)\n",
"pipe.set_steady_state(flux_init,level_init,Res_area_base,Pip_x_vec,Pip_h_vec)\n", "pipe.set_steady_state(flux_init,reservoir.get_current_pressure())\n",
"\n", "\n",
"# downstream turbine\n", "# downstream turbine\n",
"turbine = Francis_Turbine(Tur_Q_nenn,Tur_p_nenn,Tur_closingTime,Pip_dt,pUnit_conv)\n", "turbine = Francis_Turbine(Tur_Q_nenn,Tur_p_nenn,Tur_closingTime,Pip_dt,pUnit_conv)\n",
@@ -109,7 +105,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 5, "execution_count": 7,
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
@@ -158,7 +154,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 6, "execution_count": 9,
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
@@ -176,8 +172,8 @@
"axs1[1].set_ylabel(r'$Q$ [$\\mathrm{m}^3 / \\mathrm{s}$]')\n", "axs1[1].set_ylabel(r'$Q$ [$\\mathrm{m}^3 / \\mathrm{s}$]')\n",
"lo_p, = axs1[0].plot(Pip_x_vec,pressure_conversion(p_old,pUnit_calc, pUnit_conv),marker='.')\n", "lo_p, = axs1[0].plot(Pip_x_vec,pressure_conversion(p_old,pUnit_calc, pUnit_conv),marker='.')\n",
"lo_q, = axs1[1].plot(Pip_x_vec,Q_old,marker='.')\n", "lo_q, = axs1[1].plot(Pip_x_vec,Q_old,marker='.')\n",
"lo_pmin, = axs1[0].plot(Pip_x_vec,pipe.get_lowest_pressure_per_node(disp=True),c='red')\n", "lo_pmin, = axs1[0].plot(Pip_x_vec,pipe.get_lowest_pressure_per_node(disp_flag=True),c='red')\n",
"lo_pmax, = axs1[0].plot(Pip_x_vec,pipe.get_highest_pressure_per_node(disp=True),c='red')\n", "lo_pmax, = axs1[0].plot(Pip_x_vec,pipe.get_highest_pressure_per_node(disp_flag=True),c='red')\n",
"lo_qmin, = axs1[1].plot(Pip_x_vec,pipe.get_lowest_flux_per_node(),c='red')\n", "lo_qmin, = axs1[1].plot(Pip_x_vec,pipe.get_lowest_flux_per_node(),c='red')\n",
"lo_qmax, = axs1[1].plot(Pip_x_vec,pipe.get_highest_flux_per_node(),c='red')\n", "lo_qmax, = axs1[1].plot(Pip_x_vec,pipe.get_highest_flux_per_node(),c='red')\n",
"\n", "\n",
@@ -191,7 +187,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 7, "execution_count": 10,
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
@@ -247,7 +243,6 @@
" v_old = pipe.get_current_velocity_distribution()\n", " v_old = pipe.get_current_velocity_distribution()\n",
" Q_old = pipe.get_current_flux_distribution()\n", " Q_old = pipe.get_current_flux_distribution()\n",
"\n", "\n",
"\n",
" # plot some stuff\n", " # plot some stuff\n",
" # remove line-objects to autoscale axes (there is definetly a better way, but this works ¯\\_(ツ)_/¯ )\n", " # remove line-objects to autoscale axes (there is definetly a better way, but this works ¯\\_(ツ)_/¯ )\n",
" lo_p.remove()\n", " lo_p.remove()\n",
@@ -257,9 +252,9 @@
" lo_qmin.remove()\n", " lo_qmin.remove()\n",
" lo_qmax.remove()\n", " lo_qmax.remove()\n",
" # plot new pressure and velocity distribution in the pipeline\n", " # plot new pressure and velocity distribution in the pipeline\n",
" lo_p, = axs1[0].plot(Pip_x_vec,pipe.get_current_pressure_distribution(disp=True),marker='.',c='blue')\n", " lo_p, = axs1[0].plot(Pip_x_vec,pipe.get_current_pressure_distribution(disp_flag=True),marker='.',c='blue')\n",
" lo_pmin, = axs1[0].plot(Pip_x_vec,pipe.get_lowest_pressure_per_node(disp=True),c='red')\n", " lo_pmin, = axs1[0].plot(Pip_x_vec,pipe.get_lowest_pressure_per_node(disp_flag=True),c='red')\n",
" lo_pmax, = axs1[0].plot(Pip_x_vec,pipe.get_highest_pressure_per_node(disp=True),c='red')\n", " lo_pmax, = axs1[0].plot(Pip_x_vec,pipe.get_highest_pressure_per_node(disp_flag=True),c='red')\n",
" lo_q, = axs1[1].plot(Pip_x_vec,pipe.get_current_flux_distribution(),marker='.',c='blue')\n", " lo_q, = axs1[1].plot(Pip_x_vec,pipe.get_current_flux_distribution(),marker='.',c='blue')\n",
" lo_qmin, = axs1[1].plot(Pip_x_vec,pipe.get_lowest_flux_per_node(),c='red')\n", " lo_qmin, = axs1[1].plot(Pip_x_vec,pipe.get_lowest_flux_per_node(),c='red')\n",
" lo_qmax, = axs1[1].plot(Pip_x_vec,pipe.get_highest_flux_per_node(),c='red')\n", " lo_qmax, = axs1[1].plot(Pip_x_vec,pipe.get_highest_flux_per_node(),c='red')\n",
@@ -272,7 +267,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 13, "execution_count": 11,
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
@@ -316,8 +311,8 @@
"\n", "\n",
"fig2,axs2 = plt.subplots(1,1)\n", "fig2,axs2 = plt.subplots(1,1)\n",
"axs2.set_title('Min and Max Pressure')\n", "axs2.set_title('Min and Max Pressure')\n",
"axs2.plot(Pip_x_vec,pipe.get_lowest_pressure_per_node(disp=True),c='red')\n", "axs2.plot(Pip_x_vec,pipe.get_lowest_pressure_per_node(disp_flag=True),c='red')\n",
"axs2.plot(Pip_x_vec,pipe.get_highest_pressure_per_node(disp=True),c='red')\n", "axs2.plot(Pip_x_vec,pipe.get_highest_pressure_per_node(disp_flag=True),c='red')\n",
"axs2.set_xlabel(r'$x$ [$\\mathrm{m}$]')\n", "axs2.set_xlabel(r'$x$ [$\\mathrm{m}$]')\n",
"axs2.set_ylabel(r'$p$ ['+pUnit_conv+']')\n", "axs2.set_ylabel(r'$p$ ['+pUnit_conv+']')\n",
"\n", "\n",

View File

@@ -27,8 +27,6 @@ def pa_to_torr(p):
def pa_to_atm(p): def pa_to_atm(p):
return p*1/(101.325*1e3) return p*1/(101.325*1e3)
# converstion function
def pa_to_psi(p): def pa_to_psi(p):
return p/6894.8 return p/6894.8