Functionality coding

Now that the instances for all the components are available, we can write the code to actually allow the scenario to work. In this section, the functionality.py file is split into separate parts, with a discussion of each code listing following its respective part.

We've seen the code in part 1 many times before, so there is no need to discuss it in detail:

functionality.py (part 1)

1  #!/usr/bin/env python3
2 """
3 FuelFarm_functionality.py

4​
5 Purpose: Ensure valve/pump changes are passed to the rest of the system.
6​
7 Author: Cody Jackson
8​
9 Date: 6/18/18
10 #################################
11 Version 0.2
12 Added path extension for utility formulas
13 Version 0.1
14 Initial build
15 """

In part 2, we import the necessary modules and update the system path. We also list all the parameters and components that are affected by opening or closing valve 1 in lines 7-12. In line 8, we call the ffc.gate1.open() method, which is actually from components.py.

Next, we check to see which storage tank has a higher level (line 9), and therefore a higher pressure at the outlet of the tank. This is a factor if both tanks are aligned to provide fuel at the same time an unlikely condition, but possible; otherwise, only the tank that is on service will be providing the fuel, so only its gravity flow rate and pressure will be important.

If tank 2 has a higher level/pressure than tank 1, there will be no flow coming from tank 1, and the pressure through valve 3 will be the same as through valve 4. (Hydraulic pressure is the same everywhere within a fluid, which is one reason why liquids are not compressible.) However, a check valve that isn't represented on the schematic is assumed to be in the system after valve 1, so even if tank 1 is empty, there will be no flow through valve 3 (unless valve 5 is open):

functionality.py (part 2)

1  import sys
2 sys.path.extend(["/home/cody/PycharmProjects/VirtualPLC"])
3 from Utilities import utility_formulas
4 import Models.FuelFarm.components as ffc
5​
6 # Gate valve 1
7 def gate1_open():
8 ffc.gate1.open()
9 if ffc.tank2.static_tank_press > ffc.tank1.static_tank_press:
10 ffc.gate1.flow_in = ffc.gate1.flow_out = 0.0
11 ffc.gate3.press_in = ffc.gate4.press_out
12 ffc.gate3.flow_in = 0.0 # No flow because of check valves after valves 1 and 2

In part 3, if the level in tank 1 isn't lower than tank 2, then the flow and pressure through valves 3 and 5 will be the same as that which out of valve 1, which is really just the gravitational flow/pressure on the fluid in the tank.

This leads to the TODO entry in line 13. Right now, the system checks the tank level, and that determines which tank actually supplies the system. But there is nothing to ensure that the flow comes from a tank with a lower level if that one is deemed "on service," and therefore providing fuel to the rest of the system.

Note that, while pressure is a given, because of hydraulic principles, the flow rate is not guaranteed. Obviously, a valve has to be open for flow to occur, but there will be a certain pressure/flow rate seen at the inlet to each valve. We have to know what these values are to later calculate outlet values, regardless of whether the valve is open or not.

We finish part 3 by writing the method to close gate 1. This not only closes the valve, but also affects the downstream valve pressures and flow, which is described in lines 9-12:

functionality.py (part 3)

1  else:
2 ffc.gate3.press_in = ffc.gate1.press_out
3 ffc.gate3.flow_in = ffc.gate1.flow_out
4 ffc.gate5.press_in = ffc.gate1.press_out
5 ffc.gate5.flow_in = ffc.gate1.flow_out
6​
7 def gate1_close():
8 ffc.gate1.close()
9 ffc.gate3.press_in = ffc.gate4.press_out
10 ffc.gate3.flow_in = ffc.gate4.flow_out
11 ffc.gate5.press_in = ffc.gate3.press_out
12 ffc.gate5.flow_in = ffc.gate3.flow_out
13 # TODO: ensure that one tank on service allows flow

Valve 2 is the same as valve 1, so the code in part 4 shows the same functionality, except the valve numbers have changed:

functionality.py (part 4)

1  # Gate valve 2
2 def gate2_open():
3 ffc.gate2.open()
4 if ffc.tank2.static_tank_press < ffc.tank1.static_tank_press:
5 ffc.gate2.flow_in = ffc.gate2.flow_out = 0.0
6 ffc.gate4.press_in = ffc.gate3.press_out
7 ffc.gate4.flow_in = 0.0 # No flow because of check valves after valves 1 and 2
8 else:
9 ffc.gate4.press_in = ffc.gate2.press_out
10 ffc.gate4.flow_in = ffc.gate2.flow_out
11 ffc.gate7.press_in = ffc.gate2.press_out
12 ffc.gate7.flow_in = ffc.gate2.flow_out

In part 5, we finish with the close method for valve 2. Valves 3 and 4 are opposites, so the discussion about valve 3 applies to valve 4.

With valve 3, we again have to figure out which tank's side has a higher pressure. After we open the valve in line 3, we check to see whether valves 1, 2, and 4 are open at the same time (line 11)—in other words, whether both tanks 1 and 2 are lined up to supply fuel. If so, then we have to figure out which side has the higher pressure (line 12). Based on that information, we can figure out what the inlet pressure to the various valves is:

functionality.py (part 5)

1  def gate2_close():
2 ffc.gate2.close()
3 ffc.gate4.press_in = ffc.gate4.press_out
4 ffc.gate4.flow_in = ffc.gate4.flow_out
5 ffc.gate7.press_in = ffc.gate4.press_out
6 ffc.gate7.flow_in = ffc.gate4.flow_out
7​
8 # Gate valve 3
9 def gate3_open():
10 ffc.gate3.open()
11 if ffc.gate1.position == 100 and ffc.gate2.position == 100 and ffc.gate4.position == 100: # dual input
12 if ffc.gate3.press_out > ffc.gate4.press_out: # pressure # from tank 1 > tank 2
13 ffc.gate6.press_in = ffc.gate3.press_out
14 ffc.gate4.press_in = ffc.gate3.press_out

Part 6 continues with valve 3. Line 1 checks whether tank 2 has a higher level and pressure than tank 1, while line 4 assumes that the pressure is equal from both tanks, so valves 3 and 4 will show the same pressure values. In this case, we arbitrarily determine which valve's outlet pressure provides the inlet pressure to valve 6 (line 5).

Line 7 checks that there is no flow coming from the tanks, and then ensures that all parameters for valve 3 are set to zero in line 8. This is just to explicitly ensure that no weird values are available for other calculation input, which could generate hard-to-find errors.

In line 9, if the outlet of tank 2 is closed, then valve 3 will provide input values to valve 4. In line 12, the opposite occurs:

functionality.py (part 6)

1         elif ffc.gate3.press_out < ffc.gate4.press_out: # pressure           # from tank 1 < tank 2
2 ffc.gate3.press_in = ffc.gate4.press_out
3 ffc.gate6.press_in = ffc.gate4.press_out
4 else: # Pout from valves 3 and 4 is equal
5 ffc.gate6.press_in = ffc.gate3.press_out # doesn't matter # which Pout to use
6 ffc.gate6.flow_in = ffc.gate3.flow_out + ffc.gate4.flow_out # combined flow from valves 3 and 4
7 if ffc.gate1.position == 0 and (ffc.gate2.position == 0 or ffc.gate4.position == 0): # no input flow
8 ffc.gate3.press_in = ffc.gate3.flow_in = ffc.gate3.press_out = ffc.gate3.flow_out = 0.0 # Ensure null values
9 if ffc.gate2.position == 0: # valve 3 provides flow to valve 4
10 ffc.gate4.press_in = ffc.gate3.press_out
11 ffc.gate4.flow_in = ffc.gate6.flow_in = ffc.gate3.flow_out
12 if ffc.gate1.position == 0: # valve 4 provides flow to valve 3
13 ffc.gate5.press_in = ffc.gate3.press_out
14 ffc.gate5.flow_in = ffc.gate6.flow_in = ffc.gate3.flow_out

In part 7, we determine the parameters if valve 3 is closed. This is comparatively easy, as we only have to account for values from tank 2, as it is the only supply to valves 4 and 6 when valve 3 is closed:

functionality.py (part 7)

1  def gate3_close():
2 ffc.gate3.close()
3 ffc.gate6.press_in = ffc.gate4.press_out
4 ffc.gate6.flow_in = ffc.gate4.flow_out
5 if ffc.gate2.position == 0:
6 ffc.gate4.press_in = 0.0
7 ffc.gate4.flow_in = 0.0

As mentioned before, valve 4 is a mirror image of valve 3, so its code is not presented here; however, the full functionality.py file is available in the book's code repository.

Valves 5-7 are pretty similar, the only difference being in the pump they supply. Therefore, only the code for valve 5 is presented in part 8, as shown in the following code:

functionality.py (part 8)

1  # Gate valve 5
2 def gate5_open():
3 ffc.gate5.open()
4 ffc.pump1.head_in = utility_formulas.press_to_head(ffc.gate5.press_out)
5​
6 def gate5_close():
7 ffc.gate5.close()
8 ffc.pump1.head_in = 0.0

Valves 8-10 are similar as well, so part 9 only shows the methods for valve 8.

Valves 8-10 don't have any components that rely on their outlet parameters, so simple calls to their open/close methods are sufficient. However, in a later revision, having some way to account for valves 8 and 9 recirculating flow back to the tanks should be added:

functionality.py (part 9)

1  # Gate valve 8
2 def gate8_open():
3 ffc.gate8.open()
4​
5 def gate8_close():
6 ffc.gate8.close()

In part 10, we create a method that will change the tank level and subsequent static pressure. This can be used to programmatically adjust levels as fuel is used, though this particular functionality isn't implemented in this version:

functionality.py (part 10)

1  # Change tank level
2 def change_tank_level(tank, level):
3 tank.level = level
4 tank.static_tank_press = tank.level
5 if tank == ffc.tank1:
6 ffc.gate1.press_in = ffc.tank1.static_tank_press
7 elif tank == ffc.tank2:
8 ffc.gate2.press_in = ffc.tank2.static_tank_press
9 else:
10 return "Invalid tank number."

The fuel pump code is presented in part 11. We already know the speed of the pump, so when the pump is started, we only need to change the speed. The adjust_speed() method handles all the parameter changes for us. The only other parameters to account for are primary valves on the outlet.

Pumps 2 and 3 can supply fuel to a number of common valves, notably valves 8 and 10. Normally, the valves wouldn't be supplied by two pumps at once, but if we want to do this, we only have to account for adding the flow rates from pumps 2 and 3. The outlet pressure of all pumps is held constant by their respective regulating valves, which adjust the throttle percentage based on system changes, so a constant pressure value is seen downstream of the pumps.

Because pumps 2 and 3 are functionally the same, only the code for pump 2 is displayed in the following code:

functionality.py (part 11)

1  # Pump 1
2 def pump1_on():
3 ffc.pump1.adjust_speed(1480)
4 ffc.pump1.outlet_pressure = ffc.gate9.press_in = 50
5 ffc.gate9.flow_in = ffc.pump1.flow
6​
7 def pump1_off():
8 ffc.pump1.adjust_speed(0)
9 ffc.pump1.outlet_pressure = 0
10​
11 # Pump 2
12 def pump2_on():
13 ffc.pump2.adjust_speed(1480)
14 ffc.pump2.outlet_pressure = ffc.gate8.press_in = ffc.gate10.press_in = 50
15 ffc.gate8.flow_in = ffc.gate10.flow_in = ffc.pump2.flow + ffc.pump3.flow
16​
17 def pump2_off():
18 ffc.pump2.adjust_speed(0)
19 ffc.pump2.outlet_pressure = 0
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset