Source code for Water.pipe

'''Pipe :

This module holds the data for a variety of fittings and pipe sizes.
It holds a class called Pipe that allows you to create Pipe objects
and calculate losses.

'''

from __future__ import print_function
import Water.tools as tools
from Water import Imperial_properties as WATER
import sqlite3
from os import path

BASE_DIR = path.dirname(path.abspath(__file__))
db_path = path.join(BASE_DIR, "water.db")

# dictionary of C-factor values used in Hazen-Williams equation
c_dict = {
    'PVC' : 150,
    'DI' : 130,
    'STEEL' : 150,
    'HDPE' : 140,
    'STAINLESS STEEL' : 140,
    'PE 4710' : 140,
    'GALVANIZED PIPE' : 120
}
# pipe dimension dictionary {sch:{nominal size:[OD, wall thickness}}  (inches)
pipe_dims = {
    40 : {
        1 : [1.315, 0.133],
        1.25 : [1.66, 0.140],
        1.5 : [1.9, 0.145],
        2 : [2.375, 0.154],
        2.5 : [2.875, 0.203],
        3 : [3.5, 0.216],
        4 : [4.5, 0.237],
        6 : [6.625, 0.28],
        8 : [8.625, 0.322],
        10 : [10.75, 0.365],
        12 : [12.75, 0.43]        
    },
    80 : {
        1 : [1.315, 0.179],
        1.25 : [1.66, 0.191],
        1.5 : [1.9, 0.2],
        2 : [2.375, 0.218],
        2.5 : [2.875, 0.276],
        3 : [3.5, 0.3],
        4 : [4.5, 0.337],
        6 : [6.625, 0.432],
        8 : [8.625, 0.5],
        10 : [10.75, 0.5],
        12 : [12.75, 0.728]
    },
    52 : {
        3 : [3.96, 0.28],
        4 : [4.8, 0.29],
        6 : [6.9, 0.31],
        8 : [9.05, 0.33],
        10 : [11.1, 0.35],
        12 : [13.2, 0.37],
        14 : [15.3, 0.39],
        16 : [17.4, 0.40],
        18 : [19.5, 0.41],
        20 : [21.6, 0.41],
        24 : [25.8, 0.44]
    },
    'C900 DR-18' : {
        4 : [4.80, 0.267],
        6 : [6.90, 0.383],
        8 : [9.05, 0.503],
        10 : [11.10, 0.617],
        12 : [13.20, 0.733]
    },
    'DR 7' : {
        0.75 : [1.05, 0.150],
        1 : [1.315, 0.188],
        1.25 : [1.66, 0.237],
        1.5 : [1.90, 0.271],
        2 : [2.375, 0.339],
        3 : [3.5, 0.50],
        4 : [4.5, 0.643],
        5 : [5.375, 0.768],
        6 : [6.625, 0.946],
        7 : [7.125, 1.018],
        8 : [8.625, 1.232],
        10 : [10.75, 1.536],
        12 : [12.75, 1.821],
        14 : [14.0, 2.0],
        16 : [16.0, 2.286]
    },
    'SIDR 7' : {
        0.5 : [0.84, 0.069 ],
        0.75 : [1.05, 0.092],
        1 : [1.315, 0.117],
        1.25 : [1.66, 0.153],
        1.5 : [1.90, 0.179],
        2 : [2.375, 0.23]
    }
}

'''fitting dictionary:

    holds fitting shape and connection type. For each connection 
    type there is a kfactor list [geometry, k1, k_infinity]
'''

fitting_dict = {
    'elbow_90' : {
        'standard_threaded' : [1, 800, 0.4],
        'standard_glued' : [1, 800, 0.3],
        'standard_flanged' : [1, 800, 0.25],
        'long_radius' : [1.5, 800, 0.2],
        'mitered_1' : [1.5, 1000, 1.15,],
        'mitered_2' : [1.5, 800, 0.35],
        'mitered_3' : [1.5, 800, 0.3],
        'mitered_4' : [1.5, 800, 0.27],
        'mitered_5' : [1.5, 800, 0.25]
    },
    'elbow_45' : {
        'stardard_threaded' : [1, 500, 0.2],
        'stardard_glued' : [1, 500, 0.2],
        'standard_flanged' : [1, 500, 0.2],
        'standard' : [1, 500, 0.2],
        'long_radius' : [1.5, 500, 0.15],
        'mitered_1' : [None, 500, 0.25],
        'mitered_2' : [None, 500, 0.15]
    },
    'elbow_180' : {
        'standard_threaded' : [1, 1000, 0.60],
        'standard_glued' : [1, 800, 0.4],
        'standard_flanged' : [1, 1000, 0.35],
        'long_radius' : [1.5, 1000, 0.30]
    },
    'tee_branch' : {
        'standard_threaded' : [None, 500, 0.70],
        'standard_glued' : [1, 800, 0.75],
        'standard_flanged' : [None, 800, 0.80],
        'long_radius' : [None, 800, 0.40],
        'stub_in' : [None, 1000, 1.00]
    },
    'tee_through' : {
        'standard_threaded' : [1, 200, 0.10],
        'standard_glued' : [1, 800, 0.25],
        'standard_flanged' : [1, 150, 0.50],
        'stub_in' : [None, 100, 0]
    },
    'valve' : {
        'gate' : [1, 300, 0.10],
        'ball' : [0.9, 500, 0.15],
        'plug' : [0.9, 1000, 0.25],
        'globe' : [None, 1500, 4.0],
        'angle' : [None, 1000, 2.0],
        'diaphragm' : [None, 1000, 0.25],
        'butterfly' : [None, 800, 0.25],
        'lift_check' : [None, 2000, 10.0],
        'swing_check' : [None, 1500, 1.5],
        'tilt_disc_check' : [None, 1000, 0.50]
    }
}

[docs]class Pipe: '''Defines Pipe object to add pipe section and fittings for head loss calculations. See :doc:`data <data>` for available pipe properties. :param length: straight pipe length (ft) :param size: nominal pipe diamter (in) :param kind: pipe material, default 'PVC' :param sch: pipe schedule, default='C900 DR-18' :param Re: Reynolds number, default=2000 :type length: int :type size: float :type kind: string :type sch: variable depending on pipe material :type Re: int :return: Pipe object :rtype: object :Example: >>> from Water import Pipe >>> pipe = Pipe(length=10, size=4, kind='STEEL', sch=40) ''' def __init__(self,length, size, kind='PVC', sch='C900 DR-18', Re=2000): self.kind = kind self.sch = sch self.size = size self.dims = pipe_dims[sch][size] self.length = length self.fitting_list = [] self.reynolds = Re @property def outer_diameter(self): ''' outer diameter pipe property (inches) ''' return self.dims[0] @property def inner_diameter(self): '''inner diameter pipe property (inches) ''' return self.outer_diameter - 2 * self.dims[1] @property def volume(self): '''pipe volume property (cuft) ''' return tools.volume_cyl(self.inner_diameter/12, self.length) @property def area(self): '''pipe cross-sectional area property (sqft) ''' return self.volume/self.length @property def c_factor(self): ''':pipe material C-factor for Hazen-Williams eq. property''' return c_dict[self.kind]
[docs] def search_material(self, material, coefficient=None): '''Checks sqlite database for existing material record :param material: pipe material :param coefficient: Hazen Williams coefficient for major pipe losses :type material: string :type coefficient: int :return: record(s) if one exists :rtype: list ''' conn = sqlite3.connect(db_path) c = conn.cursor() existing_params = {'material' : material, 'coefficient' : coefficient} if coefficient: sqlquery='''SELECT * FROM hazen WHERE material=:material AND coefficient=:coefficient''' else: sqlquery='''SELECT * FROM hazen WHERE material=:material''' c.execute(sqlquery, existing_params) result = c.fetchall() conn.commit() conn.close() return result
[docs] def add_material(self, material, coefficient): '''Adds a record to the hazen williams coefficient table of the sqlite db :param material: pipe material :param coefficient: Hazen Williams coefficient for major pipe losses :type material: string :type coefficient: int ''' conn = sqlite3.connect(db_path) c = conn.cursor() # check if material currently exists in database exists = self.search_material(material, coefficient) if len(exists) == 0: sqlinsert = '''INSERT INTO hazen( material, coefficient) values( :material, :coefficient)''' params = {'material' : material, 'coefficient': coefficient} print('Material added to hazen table in database') c.execute(sqlinsert, params) conn.commit() conn.close() else: print('Material exists in the database, check below for specific parameters: ') for each_mat in exists: print(each_mat)
[docs] def fitting(self, fitting_type=None, con_type=None, qty=1, Kvalue=None): '''Adds fitting to Pipe object's fitting_list to add to head loss :param fitting_type: keyword from fitting dictionary, (default None) :param con_type: keyword from fitting dictionary, (default None) :param qty: number of fittings in pipe object, (default 1) :param Kvalue: custom loss coefficient, (default None) :type fitting_type: string :type con_type: string :type qty: int :type kValue: float :return: appends fitting onto Pipe object's fitting list :Example: >>> # using fitting in standard fittings dictionary >>> pipe.fitting(fitting_type='elbow_90', con_type='standard_threaded', qty=2) >>> # creating custom fitting >>> pipe.fitting('flow meter', 'flanged', qty=1, Kvalue=1.6) ''' if fitting_type in fitting_dict and con_type in fitting_dict[fitting_type]: Kfactors = fitting_dict[fitting_type][con_type] if not Kvalue: Kvalue = Kfactors[1]/self.reynolds+Kfactors[2]*(1+1/self.inner_diameter) self.fitting_list.append((fitting_type, con_type, Kvalue,qty))
[docs] def fitting_info(self): ''':return: list of fittings currently defined in pipe object :rtype: string :Example: >>> print(pipe.fitting_info()) elbow_90, standard_threaded: Kvalue = 0.899, qty = 2 flow meter, flanged: Kvalue = 1.600, qty = 1 ''' info = '' for fitting in self.fitting_list: info += '{}, {}: Kvalue = {:.3f}, qty = {} \n'.format(fitting[0], fitting[1], fitting[2], fitting[3] ) return info
[docs] def print_fitting(self): ''':return: prints out fitting dictionary for a quick reference''' for each_fitting in fitting_dict: print(each_fitting) for each_type in fitting_dict[each_fitting]: print('\t', each_type)
[docs] def major_loss(self, flow): '''Uses `Hazen-Williams equation`_ to calculate major head loss for pipe object. .. _Hazen-Williams equation: https://en.wikipedia.org/wiki/Hazen%E2%80%93Williams_equation .. math:: h_{major} = \\frac{4.52 Q^{1.852}}{C^{1.852} \\ d^{4.8704}} \\cdot L :param flow: in gallons per minute (gpm) :type flow: int :return: major head loss in ft of head :rtype: float :Example: >>> flow = 300 # gpm >>> pipe.major_loss(flow) 0.4272... ''' h = (10.45 * self.length * flow**1.852)/(self.c_factor**1.852 * self.inner_diameter**4.8704) return h
[docs] def minor_loss(self, flow): '''Uses `Minor Loss Equation`_ to calculate minor head loss through fittings in fittings_list. .. _Minor Loss Equation: https://en.wikipedia.org/wiki/Minor_losses_in_pipe_flow#Minor_Losses .. math:: h_{minor} = K_L\\cdot \\frac{v^2}{2g} :param flow: flow in gallons per minute (gpm) :type flow: int :return: minor head loss in ft of head :rtype: float :Example: >>> flow = 300 # gpm >>> pipe.minor_loss(flow) 3.0168... ''' vel = tools.velocity(flow, self.inner_diameter) minor_loss = 0 if len(self.fitting_list) > 0: for fitting in self.fitting_list: loss = fitting[2]*vel**2/(2*WATER.g) * fitting[3] minor_loss += loss return minor_loss
[docs] def get_losses(self, flow): ''' Calculate the major and minor losses through the Pipe object. :param flow: in gallons per minute (gpm) :type flow: int :return: total losses (major + minor) :rtype: float :Example: >>> flow = 300 # gpm >>> pipe.get_losses(flow) 3.4441... ''' total_loss = self.major_loss(flow) + self.minor_loss(flow) return total_loss
#### test script to test functionality if __name__=="__main__": print('test script:\n') length = 1000 # pipe length in ft size = 6 # nominal pipe diameter in inches flow = 300 # flow in gpm pipe_1 = Pipe(length, size) print('Pipe 1 info:\n', 'Pipe Length = ', pipe_1.length, 'ft \nHead Loss = ', round(pipe_1.major_loss(flow),2), 'ft \nTotal Loss = ', round(pipe_1.get_losses(flow),2), 'ft' ) pipe_2 = Pipe(length, size, kind='STEEL', sch=40) pipe_2.fitting('elbow_90', 'standard_threaded', qty=2) pipe_2.fitting('tee_branch', 'standard_threaded', qty=3) losses = pipe_2.get_losses(flow) print('\nPipe 2 info:') print(pipe_2.fitting_info()) print('total head loss: ', round(losses,2), 'ft') pipe_1.print_fitting()