Analysis of the Hydronic Network#


Let’s Import What We’ll Need#

from hvac import Quantity
from hvac.fluid_flow import load_network, PseudoConduit, SystemCurve
from hvac.fluid_flow.utils import plot_curves
Q_ = Quantity

Load the Hydronic Network back into this Notebook#

In the previous notebook “Design of a Hydronic Network” we saved our PipeNetwork object on the file path:

file_path = "./networks/hydronic_network.pickle"

Now, let’s load this object again from disk:

pipe_network = load_network(file_path)

Add a Pseudo Conduit with a Fixed Pressure Difference#

A pseudo conduit, as the name suggests, is not a real conduit in a pipe network. We use it to represent a fixed pressure difference between two nodes.

scheme

From the design of the hydronic pipe network it follows that we need a pressure difference of about 39 kPa between end node R0 and start node S0 to establish the design volume flow rates in the pipe network. In order to analyze the pipe network, we add a PseudoConduit object between the end node and the start node of the network with a fixed pressure difference of 39 kPa.

pseudo_conduit = PseudoConduit.create(fixed_pressure_drop=Q_(-39, 'kPa'))

The start node of the network is at a pressure that is 39 kPa higher than the pressure at the end node of the network. If there would be a real conduit between the start and the end node of the network, there would be a flow of water from the start node towards the end node. The direction of this flow would be opposite to the positive, clock-wise sense of the loop L1, as can be deduced from the scheme above. This is the reason why we need to add a minus sign. It can also be noticed that the fixed pressure difference between the end node and the start node is a pressure rise rather than a pressure drop.

After the pseudo conduit has been created, we still need to add it to the network:

pipe_network.add_conduit(
    conduit=pseudo_conduit,
    conduit_ID='P13',
    start_node_ID='R0',
    end_node_ID='S0',
    loop_ID='L1'
)

More about Loops#

Before we move on, let’s first go back to the pipe configuration csv-file that we discussed in the notebook “Design of a Hydronic Network” and which is displayed again below:

import pandas as pd

table = pd.read_csv('./networks/ex1_closed-pipe-network-design.csv')
ja.display_table(table)
conduit_ID start_node_ID end_node_ID loop_ID schedule length volume_flow_rate specific_pressure_drop
0 P1 S0 S1 L1 copper 4 0.458 0.2
1 P2 S1 S2 L2 copper 2 0.397 0.2
2 P3 S2 S3 L3 copper 2 0.214 0.2
3 P4 S3 S4 L4 copper 2 0.122 0.2
4 P5 S1 R1 (L1, L2) pex 25 0.061 0.2
5 P6 S2 R2 (L2, L3) pex 25 0.183 0.2
6 P7 S3 R3 (L3, L4) pex 15 0.092 0.2
7 P8 S4 R4 L4 pex 20 0.122 0.2
8 P9 R4 R3 L4 copper 2 0.122 0.2
9 P10 R3 R2 L3 copper 2 0.214 0.2
10 P11 R2 R1 L2 copper 2 0.397 0.2
11 P12 R1 R0 L1 copper 4 0.458 0.2

Inside the column loop_ID, we have specified the loop or loops to which each pipe in the network belongs. For example, pipe 5 belongs to two loops L1 and L2. You must enter the two loop IDs between brackets and separate them with a comma, otherwise the program will give an error.

The sense of the volume flow rates in the pipes is referenced to the sense of the loop the pipes belong to. By convention a clock-wise loop sense is considered to be positive. If the arrow of the volume flow rate in a pipe points in the same direction as the loop arrow, the volume flow rate has a positive sign, otherwise you must give it a negative sign. In the case a pipe belongs to two loops, the sign of the volume flow rate is referenced to the positive sense of the first mentioned loop. In the second loop the flow sign will always be opposite to the flow sign in the first loop, as can be observed in the network scheme above.

Analyze the Pipe Network#

Once alle pipes have been assigned a loop ID, or two loop IDs, and the pseudo conduit has been added between the end node and the start node of the network, we can let the program analyze the network with the Hardy Cross method:

i = pipe_network.analyze(tolerance=Q_(1, 'Pa'), i_max=500)
i
12

The Hardy Cross method is an iterative solution method to find the volume flow rates in a fluid network. The aim is to find the volume flow rate in each pipe of the network, such that the sum of the pressure differences across the pipes in each loop of the network becomes zero (as demanded by the physical law about conservation of energy). As it may take numerous iterations to find a solution for which the loop pressure differences all become exactly zero, a stop criterium is provided by the tolerance parameter. A tolerance of 1 Pa means that the analyze method will stop when all loop pressure differences are between 0 and 1 Pa. The parameter i_max sets the maximum number of iterations. If no solution has been found within i_max iterations, an OverflowError excepction will be raised to alert that no solution could be found that fulfills the stop criterium. The volume flow rates we entered in the pipe configuration file are used by the program as an initial guess to start the iterative solving process. Here we use the design flow rates for which we have sized the network.

IMPORTANT!
When we choose a volume flow rate for each pipe in the network, we must make sure that the sum of the flow rates that arrive at a node is equal to the sum of the flow rates that leave at this same node (as demanded by the physical law about conservation of mass).

When the analyze method is finished and has returned, we can take a look at the results:

pipe_table1 = pipe_network.get_pipe_table()
ja.display_table(pipe_table1)
pipe ID L [m] DN [mm] Di [mm] V [l/s] v [m/s] zeta Re Δp-dyn. [kPa]
0 P1 4.0 35.0 32.0 0.458009 0.569488 0.007096 32946.314459 0.463940
1 P2 2.0 28.0 25.0 0.397008 0.808777 -0.011416 36554.548492 0.580817
2 P3 2.0 22.0 20.0 0.214003 0.681194 -0.037437 24630.493799 0.559739
3 P4 2.0 18.0 16.0 0.122002 0.606788 0.096462 17552.098316 0.629693
4 P5 25.0 18.0 14.0 0.061002 0.396274 434.804239 10029.882171 38.030197
5 P6 25.0 26.0 20.0 0.183004 0.582521 186.696474 21062.691816 36.689134
6 P7 15.0 20.0 16.0 0.092001 0.457578 315.569585 13236.018933 35.442611
7 P8 20.0 20.0 16.0 0.122002 0.606788 154.280699 17552.098316 34.185247
8 P9 2.0 18.0 16.0 0.122002 0.606788 0.084951 17552.098316 0.627599
9 P10 2.0 22.0 20.0 0.214003 0.681194 0.514415 24630.493799 0.686250
10 P11 2.0 28.0 25.0 0.397008 0.808777 0.543084 36554.548492 0.760010
11 P12 4.0 35.0 32.0 0.458009 0.569488 0.262696 32946.314459 0.504894

As could have been expected, the volume flow rates are still close to the initial design values, as we have added a fixed pressure rise of 39 kPa that is close to what was calculated when designing the network and because we haven’t changed anything yet to the configuration of the pipe network.

ja.display_table(pipe_network.get_flow_path_table())
path Δp-elev. [kPa] Δp-dyn. [kPa] Δp-tot. [kPa] Δp-deficit [kPa]
0 P1|P2|P3|P4|P8|P9|P10|P11|P12 0.0 38.998188 38.998188 0.000843
1 P1|P5|P12 0.0 38.999031 38.999031 0.000000
2 P1|P2|P6|P11|P12 0.0 38.998794 38.998794 0.000237
3 P1|P2|P3|P7|P10|P11|P12 0.0 38.998260 38.998260 0.000771

Hydraulic Resistance of Pipe Network#

ja.display_list([
    f"network hydraulic resistance: <b>{pipe_network.hydraulic_resistance.to('Pa / (m ** 3 / s) ** 2'):~P.5g}</b>"
])
  • network hydraulic resistance: 1.8591×10¹¹ Pa·s²/m⁶
system_curve1 = SystemCurve.create(pipe_network.hydraulic_resistance, name='system curve 1')

We will this SystemCurve object later on when we will display different system curves of the pipe network.

Effect of Closing Control Valves#

What will happen to the volume flow rates in the network when we close the control valves in cross-overs P6 and P7 to 50 % opening? To find this out, we first set the valve opening of these control valves to 50 % of the fully open position:

pipe_network.set_control_valve_opening('P6', percent_open=50)
pipe_network.set_control_valve_opening('P7', percent_open=50)

Then we run the analyze method again:

i = pipe_network.analyze(tolerance=Q_(1, 'Pa'), i_max=500)
i
155

Now can display the pipe table again, to check the volume flow rates in the network.

pipe_table2 = pipe_network.get_pipe_table()
ja.display_table(pipe_table2)
pipe ID L [m] DN [mm] Di [mm] V [l/s] v [m/s] zeta Re Δp-dyn. [kPa]
0 P1 4.0 35.0 32.0 0.356759 0.443593 0.007096 25662.971166 0.298219
1 P2 2.0 28.0 25.0 0.295473 0.601933 -0.011416 27205.754582 0.344295
2 P3 2.0 22.0 20.0 0.175576 0.558877 -0.037437 20207.794106 0.395152
3 P4 2.0 18.0 16.0 0.124235 0.617895 0.096462 17873.388934 0.650165
4 P5 25.0 18.0 14.0 0.061285 0.398116 434.804239 10076.515197 38.379412
5 P6 25.0 26.0 20.0 0.119897 0.381643 486.754974 13799.399123 37.592559
6 P7 15.0 20.0 16.0 0.051341 0.255351 1108.298588 7386.353698 36.717880
7 P8 20.0 20.0 16.0 0.124235 0.617895 154.280699 17873.388934 35.420302
8 P9 2.0 18.0 16.0 0.124235 0.617895 0.084951 17873.388934 0.647994
9 P10 2.0 22.0 20.0 0.175576 0.558877 0.514415 20207.794106 0.480309
10 P11 2.0 28.0 25.0 0.295473 0.601933 0.543084 27205.754582 0.443552
11 P12 4.0 35.0 32.0 0.356759 0.443593 0.262696 25662.971166 0.323067
ja.display_table(pipe_network.get_flow_path_table())
path Δp-elev. [kPa] Δp-dyn. [kPa] Δp-tot. [kPa] Δp-deficit [kPa]
0 P1|P2|P3|P4|P8|P9|P10|P11|P12 0.0 39.003055 39.003055 0.000000
1 P1|P5|P12 0.0 39.000698 39.000698 0.002357
2 P1|P2|P6|P11|P12 0.0 39.001692 39.001692 0.001363
3 P1|P2|P3|P7|P10|P11|P12 0.0 39.002474 39.002474 0.000581

Comparing volume flow rates#

data = {
    'pipe_IDs': pipe_table1.iloc[:, 0].copy(),
    'V_1': pipe_table1.iloc[:, 4].copy(),
    'V_2': pipe_table2.iloc[:, 4].copy()
}
comp_table = pd.DataFrame(data)
comp_table['abs. difference'] = comp_table['V_2'] - comp_table['V_1']
comp_table['% difference'] = comp_table['abs. difference'] / comp_table['V_1'] * 100
ja.display_table(comp_table)
pipe_IDs V_1 V_2 abs. difference % difference
0 P1 0.458009 0.356759 -0.101251 -22.106701
1 P2 0.397008 0.295473 -0.101534 -25.574913
2 P3 0.214003 0.175576 -0.038427 -17.956196
3 P4 0.122002 0.124235 0.002233 1.830497
4 P5 0.061002 0.061285 0.000284 0.464941
5 P6 0.183004 0.119897 -0.063108 -34.484162
6 P7 0.092001 0.051341 -0.040660 -44.195050
7 P8 0.122002 0.124235 0.002233 1.830497
8 P9 0.122002 0.124235 0.002233 1.830497
9 P10 0.214003 0.175576 -0.038427 -17.956196
10 P11 0.397008 0.295473 -0.101534 -25.574913
11 P12 0.458009 0.356759 -0.101251 -22.106701

For the same pressure rise of 39 kPa between the start and the end node of the network, the total flow rate into (and out of) the network has been reduced. In the cross-overs P6 and P7 the volume flow rate has been reduced approximately by 35 % and 44 % respectively. However, in the cross-overs P5 and P8 the volume flow rates have slightly increased, because there is less pressure drop in the supply and return header.

Hydraulic Resistance of Pipe Network#

ja.display_list([
    f"network hydraulic resistance: <b>{pipe_network.hydraulic_resistance.to('Pa / (m ** 3 / s) ** 2'):~P.5g}</b>"
])
  • network hydraulic resistance: 3.0644×10¹¹ Pa·s²/m⁶
system_curve2 = SystemCurve.create(pipe_network.hydraulic_resistance, name='system curve 2')

Plot the System Curves#

chart = plot_curves(
    pump_curves=[],
    system_curves=[system_curve1, system_curve2],
    working_point=None,
    fig_size=(8, 6),
    V_step=Q_(0.05, 'L / s'),
    V_max=Q_(0.55, 'L / s'),
    dP_step=Q_(5, 'kPa'),
    dP_max=Q_(50, 'kPa'),
    V_unit='L / s',
    dP_unit='kPa'
)

chart.show()
_images/closed_pipe_network_analysis_49_0.png