from logging import exception import numpy as np #importing pressure conversion function import sys import os current = os.path.dirname(os.path.realpath(__file__)) parent = os.path.dirname(current) sys.path.append(parent) from functions.pressure_conversion import pressure_conversion def FODE_function(x,h,A,A_a,p,rho,g): # (FODE ... first order differential equation) # based on the outflux formula by Andreas Malcherek # https://www.youtube.com/watch?v=8HO2LwqOhqQ # adapted for a pressurized pipeline into which the reservoir effuses # and flow direction # x ... effusion velocity # h ... level in the reservoir # A_a ... Outflux_Area # A ... Reservoir_Area # g ... gravitational acceleration # rho ... density of the liquid in the reservoir f = x*abs(x)/h*(A_a/A-1)+g-p/(rho*h) return f class Ausgleichsbecken_class: # units # make sure that units and print units are the same # units are used to label graphs and print units are used to have a bearable format when using pythons print() area_unit = r'$\mathrm{m}^2$' area_outflux_unit = r'$\mathrm{m}^2$' density_unit = r'$\mathrm{kg}/\mathrm{m}^3$' flux_unit = r'$\mathrm{m}^3/\mathrm{s}$' level_unit = 'm' pressure_unit = 'Pa' time_unit = 's' velocity_unit = r'$\mathrm{m}/\mathrm{s}$' volume_unit = r'$\mathrm{m}^3$' area_unit_print = 'm²' area_outflux_unit_print = 'm²' density_unit_print = 'kg/m³' flux_unit_print = 'm³/s' level_unit_print = 'm' pressure_unit_print = '--' # will be set by .set_pressure() method time_unit_print = 's' velocity_unit_print = 'm/s' volume_unit_print = 'm³' g = 9.81 # m/s² gravitational acceleration # init def __init__(self,area,outflux_area,level_min = 0,level_max = np.inf ,timestep = 1,rho = 1000): self.area = area # base area of the rectangular structure self.area_outflux = outflux_area # area of the outlet towards the pipeline self.density = rho # density of the liquid in the system self.level_min = level_min # lowest allowed water level self.level_max = level_max # highest allowed water level self.timestep = timestep # timestep of the simulation # initialize for get_info self.influx = "--" self.level = "--" self.outflux = "--" self.volume = "--" # setter def set_initial_level(self,initial_level): # sets the level in the reservoir and should only be called during initialization if self.level == '--': self.level = initial_level self.volume = self.update_volume() else: raise Exception('Initial level was already set once. Use the .update_level(self,timestep) method to update level based on net flux.') def set_influx(self,influx): # sets influx to the reservoir in m³/s # positive influx means that liquid flows into the reservoir self.influx = influx def set_outflux(self,outflux): # sets outflux to the reservoir in m³/s # positive outflux means that liquid flows out of reservoir the reservoir self.outflux = outflux def set_initial_pressure(self,pressure,display_pressure_unit): # sets the static pressure present at the outlet of the reservoir # units are used to convert and display the pressure self.pressure = pressure self.pressure_unit_print = display_pressure_unit def set_pressure(self,pressure): # sets the static pressure present at the outlet of the reservoir # units are used to convert and display the pressure self.pressure = pressure def set_steady_state(self,ss_influx,ss_level,display_pressure_unit): # set the steady state (ss) condition in which the net flux is zero # set pressure acting on the outflux area so that the level stays constant ss_outflux = ss_influx ss_pressure = self.density*self.g*ss_level-(ss_outflux/self.area_outflux)**2*self.density/2 self.set_influx(ss_influx) self.set_initial_level(ss_level) self.set_initial_pressure(ss_pressure,display_pressure_unit) self.set_outflux(ss_outflux) # getter def get_info(self, full = False): new_line = '\n' p = pressure_conversion(self.pressure,self.pressure_unit,self.pressure_unit_print) if full == True: # :<10 pads the self.value to be 10 characters wide print_str = (f"The cuboid reservoir has the following attributes: {new_line}" f"----------------------------- {new_line}" f"Base area = {self.area:<10} {self.area_unit_print} {new_line}" f"Outflux area = {round(self.area_outflux,3):<10} {self.area_outflux_unit_print} {new_line}" f"Current level = {self.level:<10} {self.level_unit_print}{new_line}" f"Critical level low = {self.level_min:<10} {self.level_unit_print} {new_line}" f"Critical level high = {self.level_max:<10} {self.level_unit_print} {new_line}" f"Volume in reservoir = {self.volume:<10} {self.volume_unit_print} {new_line}" f"Current influx = {self.influx:<10} {self.flux_unit_print} {new_line}" f"Current outflux = {self.outflux:<10} {self.flux_unit_print} {new_line}" f"Current outflux vel = {round(self.outflux_vel,3):<10} {self.velocity_unit_print} {new_line}" f"Current pipe pressure = {round(p,3):<10} {self.pressure_unit_print} {new_line}" f"Simulation timestep = {self.timestep:<10} {self.time_unit_print} {new_line}" f"Density of liquid = {self.density:<10} {self.density_unit_print} {new_line}" f"----------------------------- {new_line}") else: # :<10 pads the self.value to be 10 characters wide print_str = (f"The current attributes are: {new_line}" f"----------------------------- {new_line}" f"Current level = {self.level:<10} {self.level_unit_print}{new_line}" f"Volume in reservoir = {self.volume:<10} {self.volume_unit_print} {new_line}" f"Current influx = {self.influx:<10} {self.flux_unit_print} {new_line}" f"Current outflux = {self.outflux:<10} {self.flux_unit_print} {new_line}" f"Current outflux vel = {round(self.outflux_vel,3):<10} {self.velocity_unit_print} {new_line}" f"Current pipe pressure = {round(p,3):<10} {self.pressure_unit_print} {new_line}" f"----------------------------- {new_line}") print(print_str) def get_current_level(self): return self.level def get_current_influx(self): return self.influx def get_current_outflux(self): return self.outflux def get_current_volume(self): return self.volume def get_current_pressure(self): return self.pressure # methods def update_level(self,timestep): # 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 # cannot set new level directly in this method, because it gets called to calcuate during the Runge Kutta # to calculate a ficticious level at half the timestep net_flux = self.influx-self.outflux delta_V = net_flux*timestep new_level = (self.volume+delta_V)/self.area return new_level def update_volume(self): # sets volume in reservoir based on self.level return self.level*self.area def update_pressure(self): p_new = self.density*self.g*self.level-(self.outflux/self.area_outflux)**2*self.density/2 return p_new def timestep_reservoir_evolution(self): # update outflux and outflux velocity based on current pipeline pressure and waterlevel in reservoir dt = self.timestep rho = self.density g = self.g A = self.area A_a = self.area_outflux yn = self.outflux/A_a # outflux velocity h = self.level h_hs = self.update_level(dt/2) p = self.pressure p_hs = self.pressure + rho*g*(h_hs-h) # explicit 4 step Runge Kutta Y1 = yn 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) Y4 = yn + dt*FODE_function(Y3,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)) self.outflux = ynp1*A_a self.level = self.update_level(dt) self.volume = self.update_volume() self.pressure = self.update_pressure()