tanks.py 2.97 KB
Newer Older
christian.foerster's avatar
christian.foerster committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
#################################################################################################################
## Package

class Tank:
    def __init__(self, level, rate, upstream_tanks=None):
        self.level = level
        self.rate = rate
        self.upstream_tanks = upstream_tanks
        return
    
    @property # allows the method use without brackets 
    def Q(self):
        tank_area = 1
        return self.level * self.rate / tank_area
        

class LevelGauge:
    def __init__(self,tank):
        self.tank = tank
        self.data = []
        return
    
    def take_measurement(self):
        self.data.append(self.tank.level)
        return


class Network:
    def __span_network(self, i):
        self.tanks[i] = Tank(levels[i], rates[i], upstream_tanks=network_structure.get(i))
        if network_structure.get(i) is not None:
            for tank in network_structure.get(i):
                #recursion
                self.__span_network(tank) 
    
    def generate(self, network_structure, levels, rates):
        
        self.final_tank = max(network_structure.keys())
        
        if network_structure[self.final_tank] == []:
            print("The last tank has no upstream tanks. The network structure you provided is invalid.")
        else:
            self.tanks = {}
            self.__span_network(self.final_tank)
        
        return "network generated"
    
    def __fill_tank(self,tank_id):
        tank = self.tanks[tank_id]
        if tank.upstream_tanks is not None:
            for uptank_id in tank.upstream_tanks:
                # recursion
                self.__fill_tank(uptank_id)          
                
                uptank = self.tanks[uptank_id]
                uptank.level -= uptank.Q
                tank.level += uptank.Q
    
    def install_level_gauges(self):
        self.gauges = {}
        for tank_id in self.tanks:
            self.gauges[tank_id] = LevelGauge(self.tanks[tank_id])
    
    def simulate(self,iterations):
        
        for gauge in self.gauges.values():
            gauge.take_measurement()
        
        for dt in range(iterations):
            self.__fill_tank(self.final_tank)
            
            for gauge in self.gauges.values():
                gauge.take_measurement()
            
        return

#################################################################################################################
## Top level Api use

network_structure = {6:[4,5],4:[1,2],5:[3]}
levels = {1:10,2:20,3:15,4:8,5:44,6:2.5}
rates = {1:0.0,2:0.06,3:0.9,4:0.3,5:0.5,6:0}

network = Network()
network.generate(network_structure,levels,rates)
network.install_level_gauges()
network.simulate(110)


#################################################################################################################
## plotting results

import matplotlib.pyplot as plt
fig, axis = plt.subplots(6,1,figsize = (12,8))
for idx, ax in enumerate(axis):
    ax.plot(network.gauges[idx+1].data)
    ax.set_title(idx+1)
plt.tight_layout()