def calculate_headloss(vol_flow, flow_area, lpipe, idiameter, eroughness, kin_visc, grav): """Calculate Darcy-Weisbach friction factor and head loss Calculates head loss (m) and Darcy-Wesibach friction factor and intermediate quantities flow velocity (m/s), and Reynolds number. These values are returned in a named tuple with key names ``head_loss``, ``friction``, ``vflow``, and ``Re`` respectively. Args: vol_flow (float): volumetric flow, in cubic meters per second flow_area (float): pipe flow area, in square meters lpipe (float): pipe length, in meters idiameter (float): pipe inner diameter, in meters eroughness (float): pipe relative roughness, dimensionless kin_visc (float): kinematic viscosity, in square meters per second grav (float): gravitational acceleration, in meters per second squared Returns: (namedtuple): Results and intermediate quantities """ HeadLoss = namedtuple('HeadLoss', ['head_loss', 'friction', 'vflow', 'Re']) flow_vel = vol_flow / flow_area Re = flow_vel * idiameter / kin_visc friction = friction_factor(Re=Re, eD=eroughness) head_loss = (friction * lpipe * flow_vel**2) / (2.0 * grav * idiameter) hldat = HeadLoss(head_loss, friction, flow_vel, Re) return hldat
def accept(self): Dp = 0 elements = list() Q = float(self.form.editFlow.text()) / 3600 if self.form.comboWhat.currentText() == '<on selection>': elements = FreeCADGui.Selection.getSelection() else: o = FreeCAD.ActiveDocument.getObjectsByLabel( self.form.comboWhat.currentText())[0] if hasattr(o, 'PType') and o.PType == 'PypeBranch': elements = o.Tubes + o.Curves for o in elements: if hasattr(o, 'PType') and o.PType in ['Pipe', 'Elbow']: ID = float(o.ID) / 1000 e = float(self.form.editRough.text()) * 1e-6 / ID if self.isLiquid: v = Q / ((ID)**2 * pi / 4) else: v = Q / ((ID)**2 * pi / 4) / self.Rho Re = Reynolds(V=v, D=ID, rho=self.Rho, mu=self.Mu) f = friction.friction_factor(Re, eD=e) # Darcy, =4xFanning if o.PType == 'Pipe': L = float(o.Height) / 1000 K = K_from_f(fd=f, L=L, D=ID) FreeCAD.Console.PrintMessage( 'ID=%.2f\nV=%.2f\ne=%f\nf=%f\nK=%f\nL=%.3f\n***\n' % (ID, v, e, f, K, L)) Dp += dP_from_K(K, rho=self.Rho, V=v) elif o.PType == 'Elbow': ang = float(o.BendAngle) R = float(o.BendRadius) / 1000 K = fittings.bend_rounded(ID, ang, f, R) FreeCAD.Console.PrintMessage( 'ID=%.2f\nV=%.2f\ne=%f\nf=%f\nK=%f\nang=%.3f\nR=%f\n***\n' % (ID, v, e, f, K, ang, R)) Dp += dP_from_K(K, rho=self.Rho, V=v) elif o.PType == 'Reduct': pass elif hasattr(o, 'Kv') and o.Kv > 0: if self.isLiquid: Dp += (Q * 3600 / o.Kv)**2 * 100000 else: pass if Dp > 200: result = ' = %.3f bar' % (Dp / 100000) else: result = ' = %.2e bar' % (Dp / 100000) self.form.labResult.setText(result)
def pipe_friction(vol_flow, idiameter, kin_visc, flow_area, eroughness): """Calculate friction factor from pipe geometry, flow conditions, and fluid properties. Input values should be supplied in a consistent unit system (all SI or all US Traditional) Args: vol_flow (float): Volumetric flow idiameter (float): Pipe inner diameter kin_visc (float): Fluid kinematic viscosity flow_area (float): Pipe flow area eroughness (float): Relative pipe roughness. Returns: (float): Darcy-Weisbach friction factor""" vflow = vol_flow / flow_area Re = vflow * idiameter / kin_visc friction = friction_factor(Re=Re, eD=eroughness) _logger.debug('{0:16s}{1:12.4E}'.format('vflow', vflow)) _logger.debug('{0:16s}{1:12.4E}'.format('Re', Re)) return friction
def set_flow_conditions(self, vol_flow, kin_visc): """Specify volumetric flow and fluid properties so flow velocity, Reynolds number, and friction factor may be calculated. Args: vol_flow (Quantity): Volumetric flow rate kin_visc (Quantity): Kinematic viscosity Raises: ValueError: An error occurred deriving friction factor, etc.""" self._vol_flow = vol_flow self._kin_visc = kin_visc self._vflow = self._vol_flow / self.flow_area self._Re = (self._vflow.to('m/s') * self._idiameter.to('m') / self._kin_visc.to('m**2/s')).magnitude self._friction = friction_factor(Re=self._Re, eD=self._eroughness) self._flow_set = True return
def calculate(doc_original): doc = deepcopy(doc_original) treeUnitConvert(doc, doc['units'], SI_UNITS) K_pipe = 0 K_fittings = 0 K_entry = 0 K_exit = 0 K_fixed = 0 K_LbyD = 0 K_total = 0 LbyD_total = 0 deltaP_fixed = 0 deltaP_total = 0 size_definition = doc['input']['pipe']['size_definition']['_val'] if (size_definition == "NPS"): nps = float(doc['input']['pipe']['NPS']['_val']) schedule = doc['input']['pipe']['schedule']['_val'] NPS, Di, Do, t = nearest_pipe(NPS=nps, schedule=schedule) else: Di = float(doc['input']['pipe']['Dia_inner']['_val']) NPS = math.nan Do = math.nan t = math.nan doc['result'].update({'Di': {'_val': str(Di), '_dim': 'length_mili'}}) doc['result'].update({'Do': {'_val': str(Do), '_dim': 'length_mili'}}) doc['result'].update({'t': {'_val': str(t), '_dim': 'length_mili'}}) area = math.pi * pow(Di / 2, 2) Q = float(doc['input']['fluidData']['Q']['_val']) V = roundit(Q / area) doc['result'].update({'V': {'_val': str(V), '_dim': 'speed'}}) mu = float(doc['input']['fluidData']['mu']['_val']) rho = float(doc['input']['fluidData']['rho']['_val']) Re = roundit(Reynolds(V=V, D=Di, rho=rho, mu=mu)) doc['result'].update({'Re': {'_val': str(Re)}}) Hdyn = roundit(rho * pow(V, 2) / 2) doc['result'].update({'Hdyn': {'_val': str(Hdyn), '_dim': 'length'}}) #K calculation for straigth pipe roughness_basis = doc['input']['pipe']['roughness_basis']['_val'] if (roughness_basis == "Material"): material = doc['input']['pipe']['material']['_val'] roughness = get_roughness(material) else: roughness = float(doc['input']['pipe']['roughness']['_val']) eD = roughness / Di doc['result'].update({'eD': {'_val': str(eD)}}) fd = roundit(friction_factor(Re=Re, eD=eD, Method="Moody")) doc['result'].update({'fd_Moody': {'_val': str(fd)}}) length = float(doc['input']['pipe']['length']['_val']) K_pipe = roundit(K_from_f(fd=fd, L=length, D=Di)) doc['result'].update({'K_pipe': {'_val': str(K_pipe)}}) deltaP_pipe = roundit(dP_from_K(K_pipe, rho, V)) doc['result'].update( {'deltaP_pipe': { '_val': str(deltaP_pipe), '_dim': 'pressure' }}) #calculating pressure drop for entrance entry_type = doc['input']['entrance']['entry_type']['_val'] print('entry type is') print(entry_type) if entry_type == 'none': K_entry = 0 elif entry_type == 'Sharp': K_entry = fluids.fittings.entrance_sharp() elif entry_type == 'Rounded': Rc = float(doc['input']['entrance']['Rc']['_val']) K_entry = fluids.fittings.entrance_rounded(Di, Rc) elif entry_type == 'Angled': angle_radians = float(doc['input']['entrance']['angle']['_val']) angle = angle_radians * 57.2958 K_entry = fluids.fittings.entrance_angled(angle) elif entry_type == 'Projecting': wall_thickness = float( doc['input']['entrance']['wall_thickness']['_val']) K_entry = fluids.fittings.entrance_distance(Di, wall_thickness) K_entry = roundit(K_entry) doc['result'].update({'K_entry': {'_val': str(K_entry)}}) deltaP_entry = roundit(dP_from_K(K_entry, rho, V)) doc['result'].update( {'deltaP_entry': { '_val': str(deltaP_entry), '_dim': 'pressure' }}) #calculating pressure drop for exit exit_type = doc['input']['exit']['exit_type']['_val'] print('exit_type') print(exit_type) if (exit_type == 'Normal'): K_exit = exit_normal() else: K_exit = 0 K_exit = roundit(K_exit) doc['result'].update({'K_exit': {'_val': str(K_exit)}}) deltaP_exit = roundit(dP_from_K(K_exit, rho, V)) doc['result'].update( {'deltaP_exit': { '_val': str(deltaP_exit), '_dim': 'pressure' }}) #calculating pressure drop for fittings fittings_list = doc['input']['fittings'] for fitting in fittings_list: name = get_hooper_list()[fitting['index']] Di_inch = Di * 39.3701 K_fitting = Hooper2K(Di_inch, Re, name=name) K_fittings += K_fitting * fitting['quantity'] K_fittings = roundit(K_fittings) doc['result'].update({'K_fittings': {'_val': str(K_fittings)}}) deltaP_fittings = dP_from_K(K_fittings, rho, V) deltaP_fittings = roundit(deltaP_fittings) doc['result'].update({ 'deltaP_fittings': { '_val': str(deltaP_fittings), '_dim': 'pressure' } }) #calculating pressure drop for sharp contractions deltaP_contractions_sharp = 0 contractions_sharp = doc['input']['contractions_sharp']['_list'] for contraction in contractions_sharp: D1 = contraction['D1'] D2 = contraction['D2'] A2 = 3.1416 * (D2**2) / 4 V2 = Q / A2 K_contraction = fluids.fittings.contraction_sharp(D1, D2) deltaP = dP_from_K(K_contraction, rho, V2) deltaP_contractions_sharp += deltaP deltaP_contractions_sharp = roundit(deltaP_contractions_sharp) doc['result'].update({ 'deltaP_contractions_sharp': { '_val': str(deltaP_contractions_sharp), '_dim': 'pressure' } }) #calculating pressure drop for rounded contractions deltaP_contractions_rounded = 0 contractions_rounded = doc['input']['contractions_rounded']['_list'] for contraction in contractions_rounded: D1 = contraction['D1'] D2 = contraction['D2'] Rc = contraction['Rc'] A2 = 3.1416 * (D2**2) / 4 V2 = Q / A2 K_contraction = fluids.fittings.contraction_round(D1, D2, Rc) deltaP = dP_from_K(K_contraction, rho, V2) deltaP_contractions_rounded += deltaP deltaP_contractions_rounded = roundit(deltaP_contractions_rounded) doc['result'].update({ 'deltaP_contractions_rounded': { '_val': str(deltaP_contractions_rounded), '_dim': 'pressure' } }) #calculating pressure drop for conical contractions deltaP_contractions_conical = 0 contractions_conical = doc['input']['contractions_conical']['_list'] for contraction in contractions_conical: D1 = contraction['D1'] D2 = contraction['D2'] L = contraction['L'] A2 = 3.1416 * (D2**2) / 4 V2 = Q / A2 K_contraction = fluids.fittings.contraction_conical(D1, D2, fd=fd, l=L) deltaP = dP_from_K(K_contraction, rho, V2) deltaP_contractions_conical += deltaP deltaP_contractions_conical = roundit(deltaP_contractions_conical) doc['result'].update({ 'deltaP_contractions_conical': { '_val': str(deltaP_contractions_conical), '_dim': 'pressure' } }) #calculating pressure drop for pipe reducers contractions deltaP_contractions_reducer = 0 contractions_reducer = doc['input']['contractions_reducer']['_list'] for contraction in contractions_reducer: reducer_size = contraction['reducer_size'] D1, D2, L = reducer_dimensions(reducer_size) A2 = 3.1416 * (D2**2) / 4 V2 = Q / A2 K_contraction = fluids.fittings.contraction_conical(D1, D2, fd=fd, l=L) deltaP = dP_from_K(K_contraction, rho, V2) deltaP_contractions_reducer += deltaP deltaP_contractions_reducer = roundit(deltaP_contractions_reducer) doc['result'].update({ 'deltaP_contractions_reducer': { '_val': str(deltaP_contractions_reducer), '_dim': 'pressure' } }) # calculating total pressure drop in all contractions deltaP_contractions = deltaP_contractions_sharp + deltaP_contractions_rounded + deltaP_contractions_conical + deltaP_contractions_reducer deltaP_contractions = roundit(deltaP_contractions) doc['result'].update({ 'deltaP_contractions': { '_val': str(deltaP_contractions), '_dim': 'pressure' } }) #calculating pressure drop for sharp expansions deltaP_expansions_sharp = 0 expansions_sharp = doc['input']['expansions_sharp']['_list'] for contraction in expansions_sharp: D1 = contraction['D1'] D2 = contraction['D2'] A1 = 3.1416 * (D1**2) / 4 V1 = Q / A1 K_contraction = fluids.fittings.diffuser_sharp(D1, D2) deltaP = dP_from_K(K_contraction, rho, V1) deltaP_expansions_sharp += deltaP deltaP_expansions_sharp = roundit(deltaP_expansions_sharp) doc['result'].update({ 'deltaP_expansions_sharp': { '_val': str(deltaP_expansions_sharp), '_dim': 'pressure' } }) #calculating pressure drop for conical expansions deltaP_expansions_conical = 0 expansions_conical = doc['input']['expansions_conical']['_list'] for contraction in expansions_conical: D1 = contraction['D1'] D2 = contraction['D2'] L = contraction['L'] A1 = 3.1416 * (D1**2) / 4 V1 = Q / A1 K_contraction = fluids.fittings.diffuser_conical(D1, D2, fd=fd, l=L) deltaP = dP_from_K(K_contraction, rho, V1) deltaP_expansions_conical += deltaP deltaP_expansions_conical = roundit(deltaP_expansions_conical) doc['result'].update({ 'deltaP_expansions_conical': { '_val': str(deltaP_expansions_conical), '_dim': 'pressure' } }) #calculating pressure drop for pipe reducer expansions deltaP_expansions_reducer = 0 expansions_reducer = doc['input']['expansions_reducer']['_list'] for contraction in expansions_reducer: reducer_size = contraction['reducer_size'] D2, D1, L = reducer_dimensions(reducer_size) A1 = 3.1416 * (D1**2) / 4 V1 = Q / A1 K_contraction = fluids.fittings.diffuser_conical(D1, D2, fd=fd, l=L) deltaP = dP_from_K(K_contraction, rho, V1) deltaP_expansions_reducer += deltaP deltaP_expansions_reducer = roundit(deltaP_expansions_reducer) doc['result'].update({ 'deltaP_expansions_reducer': { '_val': str(deltaP_expansions_reducer), '_dim': 'pressure' } }) # calculating total pressure drop in all expansions deltaP_expansions = deltaP_expansions_sharp + deltaP_expansions_conical + deltaP_expansions_reducer doc['result'].update( {'deltaP_expansions': { '_val': deltaP_expansions, '_dim': 'pressure' }}) fixed_K_loss = doc['input']['fixed_K_losses']['_list'] for loss in fixed_K_loss: K_fixed += loss['K'] * loss['quantity'] deltaP_fixed_K = dP_from_K(K_fixed, rho, V) fixed_LbyD_loss = doc['input']['fixed_LbyD_losses']['_list'] for loss in fixed_LbyD_loss: L_D = loss['LbyD'] K_LbyD += K_from_L_equiv(L_D=L_D, fd=fd) * loss['quantity'] deltaP_fixed_LbyD = dP_from_K(K_LbyD, rho, V) fixed_deltaP_loss = doc['input']['fixed_deltaP_losses']['_list'] for loss in fixed_deltaP_loss: deltaP_fixed += loss['deltaP'] * loss['quantity'] deltaP_fixed_deltaP = deltaP_fixed deltaP_fixed_all = deltaP_fixed_K + deltaP_fixed_LbyD + deltaP_fixed_deltaP deltaP_fixed_all = roundit(deltaP_fixed_all) doc['result'].update({ 'deltaP_fixed_all': { '_val': str(deltaP_fixed_all), '_dim': 'pressure' } }) deltaP_total = deltaP_pipe + deltaP_entry + deltaP_exit + deltaP_fittings + deltaP_contractions + deltaP_expansions + deltaP_fixed_all deltaP_total = roundit(deltaP_total) doc['result'].update( {'deltaP_total': { '_val': str(deltaP_total), '_dim': 'pressure' }}) # doc_original['input'].update(doc['input']) doc_original['result'].update(doc['result']) treeUnitConvert(doc, SI_UNITS, doc['units'], autoRoundOff=True) return True
def solve_network_flows(case_dom): """Find the volumetric flow and head loss for the piping network defined in the case_dom structure by using the linear method as described in Chapter 5 of Jeppson. Args: case_dom (dict): Pipe flow network data model Raises: ValueError: Network solution matrix is singular or does not converge. """ # The goal of this project is to reimplement the JEPPSON_CH5 code in Python, # not simply translate the original Fortran into Python. For this reason, pipe, # junction, and loop indices are zero-based, default NumPy matrix storage is # used. This complicates the comparison between the original Fortran and the # Python implementations, but effectively illusrates the differences in # implementing the solution in each language. # The matrix a is constructed in the same manner as in the original Fortran # application with a few modifications. While NumPy multidimensional array # storage can be set to either C-style (row-major) or Fortran-style # (column-major), the default NumPy style is used - C-style. # Matrix columns represent flow through pipes. The first (njunctions-1) rows # contain 1.0 in a column for outflow via that column's pipe or -1.0 for inflow # from that column's pipe. The corresponding term in the first (njunctions - 1) # rows of the column vector b contains the fixed flow into the junction from # outside the system, positive for inflow and negative for outflow. # The remaining nloops rows in the a matrix contain the flow resistance of each # pipe, positive if the flow convention is clockwise/forward in the loop, # negative if the flow convention is counter-clockwise/reverse in the loop. # Flow direction/convention is heuristically assigned by the modeler; the # actual flow direction will be determined from the problem solution. The # corresponding entries in the b column vector are zero since the flow around a # loop is conservative - no net increase or decrease. ugrav = Q_(sc.g, 'm/s**2') nct = 0 ssum = 100.0 flow_units = '' npipes = case_dom['params']['npipes'] njunctions = case_dom['params']['njunctions'] qpredict = np.zeros(npipes) # Set (njunctions-1) independent, conservative junction equations. # Note that the remaining junction equation can be derived from the # junction equations specified so far. _logger.debug('5a. Assemble constant portions of matrix and RHS vector') a = np.zeros((npipes, npipes)) # This portion of the A matrix is constant for pipe in case_dom['pipe']: pipe_id = pipe['id'] jfrom = pipe['from'] jto = pipe['to'] _logger.debug('Pipe {0:d} goes from {1:d} to {2:d}'.format( pipe_id, jfrom, jto)) if jfrom < njunctions - 1: a[jfrom, pipe_id] = -1.0 if jto < njunctions - 1: a[jto, pipe_id] = 1.0 # The B vector is constant b = np.zeros((npipes)) for idx, inflow in enumerate(case_dom['inflows']): # Use base units of first non-zero flow for result # conversion. if flow_units == '' and inflow.magnitude != 0.0: flow_units = inflow.to_base_units().units if idx < njunctions - 1: b[idx] = inflow.to_base_units().magnitude done = False converged = False while not done: # Step 5. Assemble matrix _logger.debug('5b. Assemble matrix rows of loop equations') for iloop, looppipe in enumerate(case_dom['loop']): row_id = njunctions - 1 + iloop _logger.debug('Row id is {0:d} = njunctions + iloop = ' '{1:d} + {2:d}'.format(row_id, njunctions, iloop)) for pipe in looppipe: pid = pipe['pipe_id'] col_id = pid resistance = (pipe['flow_dir'] * case_dom['pipe'][pid]['kp']) _logger.debug( ' Col id is {0:d}; resistance = {1:0.4E}'.format( col_id, resistance)) a[row_id, col_id] = resistance _logger.debug( 'Resultant flows are in units of {0:s}'.format(flow_units)) # print(repr(a)) # print(repr(b)) # Call matrix solver # Step 6. Solve matrix _logger.debug('6. Solve matrix') try: x = np.linalg.solve(a, b) except np.linalg.LinAlgError as err: msg = 'Cannot solve matrix: {0:s}'.format(str(err)) _logger.error(msg) print('Error: ' + msg) print('A Matrix:\n{:s}\n'.format(repr(a))) print('B Vector:\n{:s}\n'.format(repr(b))) converged = False # force-exit iteration loop break # print(repr(x)) _logger.debug('7. Adjust matrix') if nct > 0: ssum = 0.0 for ipipe, currpipe in enumerate(case_dom['pipe']): if nct > 0: qm = 0.5 * (qpredict[ipipe] + x[ipipe]) ssum += abs(qpredict[ipipe] - x[ipipe]) else: qm = x[ipipe] qpredict[ipipe] = qm dq = Q_(qm * case_dom['params']['fvol_flow'], flow_units) qmu = Q_(abs(qm), flow_units) # vflowe = qmu / currpipe['flow_area'] qq_lo = qmu - dq vflowv_lo = qq_lo / currpipe['flow_area'] qq_hi = qmu + dq vflowv_hi = qq_hi / currpipe['flow_area'] if vflowv_lo.magnitude < 0.001: vflowv_lo = Q_(0.002, vflowv_lo.units) _logger.info(' Flow velocity low endpoint for pipe {0:d} ' 'increased to {1:0.4E~}'.format(ipipe, vflowv_lo)) re_lo = (vflowv_lo * currpipe['idiameter'] / case_dom['params']['kin_visc']).to_base_units() re_hi = (vflowv_hi * currpipe['idiameter'] / case_dom['params']['kin_visc']).to_base_units() _logger.debug(' Pipe {0:d} Re varies from {1:0.4E~} to ' '{1:0.4E~}'.format(ipipe, re_lo, re_hi)) friction_lo = friction_factor(Re=re_lo, eD=currpipe['eroughness']) friction_hi = friction_factor(Re=re_hi, eD=currpipe['eroughness']) _logger.debug( ' Pipe {0:d} f varies from {1:0.4E~} to {1:0.4E~}'.format( ipipe, friction_lo, friction_hi)) # Calculate new Kp for each pipe based on flow regime and # friction factor # Note: Flow is laminar for Reynolds number less than 2050 if re_lo < 2050.0: currpipe['expp'] = 1.0 tmp_kp = (2.0 * ugrav * case_dom['params']['kin_visc'] * currpipe['arl'] / currpipe['idiameter']) currpipe['kp'] = tmp_kp.to('1/ft**3/s').magnitude _logger.debug( ' Pipe {0:d} flow in laminar region'.format(ipipe)) else: # Consider only transition regime, not transition-rough be = ((log(friction_lo) - log(friction_hi)) / (log(qq_lo.to('ft**3/s').magnitude) - log(qq_hi.to('ft**3/s').magnitude))) ae = friction_lo * qq_lo.to('ft**3/s').magnitude**be ep = 1.0 - be currpipe['expp'] = 2.0 - be currpipe['kp'] = (ae * currpipe['arl'].to('s**2/ft**5').magnitude * qmu.to('ft**3/s').magnitude**ep).magnitude _logger.debug(' arl is in units of {0:s}'.format( currpipe['arl'].units)) _logger.debug(' Pipe {0:d} flow in transition / ' 'turbulent region'.format(ipipe)) _logger.debug(' Pipe {0:d} Kp is updated to {1:0.4E}'.format( ipipe, currpipe['kp'])) _logger.debug('8. Display interim results') print('Iteration {0:d}'.format(nct)) print('Deviation {0:0.4E} (Tolerance {1:0.4E})'.format( ssum, case_dom['params']['tolerance'])) print() print('Pipe Kp expp Qcurrent ' 'Qpredict') for ipipe, currpipe in enumerate(case_dom['pipe']): print('{0:3d} {1:0.4E} {2:0.4E} {3:0.4E~} {4:0.4E~}'. format(ipipe, currpipe['kp'], currpipe['expp'], Q_(x[ipipe], 'm**3/s').to('ft**3/s'), Q_(qpredict[ipipe], 'm**3/s').to('ft**3/s'))) print() nct += 1 _logger.debug('9. Check convergence') converged = ssum <= case_dom['params']['tolerance'] done = converged or (nct >= case_dom['params']['maxiter']) # End iteration # ######################################################################## if not converged: msg = 'Case not converged: ssum = {0:0.4E} > tolerance {1:0.4E}' \ .format(ssum, case_dom['params']['tolerance']) # Advance to next case raise ValueError(msg) # Add final results to case_dom flow_disp_units = 'm**3/s' for qext in case_dom['inflows']: if qext != 0.0: flow_disp_units = qext.units break for ipipe, currpipe in enumerate(case_dom['pipe']): currpipe['vol_flow'] = Q_(x[ipipe], 'm**3/s').to(flow_disp_units) currpipe['head_loss'] = (Q_( currpipe['kp'] * currpipe['vol_flow'].to('ft**3/s').magnitude, 'ft')) return
def Stein_Schmidt(m=None, Dtank=None, Djacket=None, H=None, Dinlet=None, rho=None, Cp=None, k=None, mu=None, muw=None, rhow=None, inlettype='tangential', inletlocation='auto', roughness=0): r'''Calculates average heat transfer coefficient for a jacket around a vessel according to [1]_ as described in [2]_. .. math:: l_{ch} = \left[\left(\frac{\pi}{2}\right)^2 D_{tank}^2+H^2\right]^{0.5} d_{ch} = 2\delta Re_j = \frac{v_{ch}d_{ch}\rho}{\mu} Gr_J = \frac{g\rho(\rho-\rho_w)d_{ch}^3}{\mu^2} Re_{J,eq} = \left[Re_J^2\pm \left(\frac{|Gr_J|\frac{H}{d_{ch}}}{50} \right)\right]^{0.5} Nu_J = (Nu_A^3 + Nu_B^3 + Nu_C^3 + Nu_D^3)^{1/3}\left(\frac{\mu} {\mu_w}\right)^{0.14} Nu_J = \frac{h d_{ch}}{k} Nu_A = 3.66 Nu_B = 1.62 Pr^{1/3}Re_{J,eq}^{1/3}\left(\frac{d_{ch}}{l_{ch}} \right)^{1/3} Nu_C = 0.664Pr^{1/3}(Re_{J,eq}\frac{d_{ch}}{l_{ch}})^{0.5} \text{if } Re_{J,eq} < 2300: Nu_D = 0 Nu_D = 0.0115Pr^{1/3}Re_{J,eq}^{0.9}\left(1 - \left(\frac{2300} {Re_{J,eq}}\right)^{2.5}\right)\left(1 + \left(\frac{d_{ch}}{l_{ch}} \right)^{2/3}\right) For Radial inlets: .. math:: v_{ch} = v_{Mit}\left(\frac{\ln\frac{b_{Mit}}{b_{Ein}}}{1 - \frac{b_{Ein}}{b_{Mit}}}\right) b_{Ein} = \frac{\pi}{8}\frac{D_{inlet}^2}{\delta} b_{Mit} = \frac{\pi}{2}D_{tank}\sqrt{1 + \frac{\pi^2}{4}\frac {D_{tank}^2}{H^2}} v_{Mit} = \frac{Q}{2\delta b_{Mit}} For Tangential inlets: .. math:: v_{ch} = (v_x^2 + v_z^2)^{0.5} v_x = v_{inlet}\left(\frac{\ln[1 + \frac{f_d D_{tank}H}{D_{inlet}^2} \frac{v_x(0)}{v_{inlet}}]}{\frac{f_d D_{tank}H}{D_{inlet}^2}}\right) v_x(0) = K_3 + (K_3^2 + K_4)^{0.5} K_3 = \frac{v_{inlet}}{4} -\frac{D_{inlet}^2v_{inlet}}{4f_d D_{tank}H} K_4 = \frac{D_{inlet}^2v_{inlet}^2}{2f_d D_{tank} H} v_z = \frac{Q}{\pi D_{tank}\delta} v_{inlet} = \frac{Q}{\frac{\pi}{4}D_{inlet}^2} Parameters ---------- m : float Mass flow rate of fluid, [kg/m^3] Dtank : float Outer diameter of tank or vessel surrounded by jacket, [m] Djacket : float Inner diameter of jacket surrounding a vessel or tank, [m] H : float Height of the vessel or tank, [m] Dinlet : float Inner diameter of inlet into the jacket, [m] rho : float Density of the fluid at Tm [kg/m^3] Cp : float Heat capacity of fluid at Tm [J/kg/K] k : float Thermal conductivity of fluid at Tm [W/m/K] mu : float Viscosity of fluid at Tm [Pa*s] muw : float, optional Viscosity of fluid at Tw [Pa*s] rhow : float, optional Density of the fluid at Tw [kg/m^3] inlettype : str, optional Either 'tangential' or 'radial' inletlocation : str, optional Either 'top' or 'bottom' or 'auto' roughness : float, optional Roughness of the tank walls [m] Returns ------- h: float Average transfer coefficient inside the jacket [W/m^2/K] Notes ----- [1]_ is in German and has not been reviewed. Multiple other formulations are considered in [1]_. If the fluid is heated and enters from the bottom, natural convection assists the heat tansfer and the Grashof term is added; if it were to enter from the top, it would be substracted. The situation is reversed if entry is from the top. Examples -------- Example as in [2]_, matches in all but friction factor: >>> Stein_Schmidt(m=2.5, Dtank=0.6, Djacket=0.65, H=0.6, Dinlet=0.025, ... rho=995.7, Cp=4178.1, k=0.615, mu=798E-6, muw=355E-6, rhow=971.8) 5695.204169808863 References ---------- .. [1] Stein, Prof Dr-Ing Werner Alexander, and Dipl-Ing (FH) Wolfgang Schmidt. "Wärmeübergang auf der Wärmeträgerseite eines Rührbehälters mit einem einfachen Mantel." Forschung im Ingenieurwesen 59, no. 5 (May 1993): 73-90. doi:10.1007/BF02561203. .. [2] Gesellschaft, V. D. I., ed. VDI Heat Atlas. 2nd edition. Berlin; New York:: Springer, 2010. ''' delta = (Djacket - Dtank) / 2. Q = m / rho Pr = Cp * mu / k lch = (pi**2 / 4 * Dtank**2 + H**2)**0.5 dch = 2 * delta if inlettype == 'radial': bEin = pi / 8 * Dinlet**2 / delta bMit = pi / 2 * Dtank * (1 + pi**2 / 4 * Dtank**2 / H**2)**0.5 vMit = Q / (2 * delta * bMit) vch = vMit * log(bMit / bEin) / (1 - bEin / bMit) ReJ = vch * dch * rho / mu elif inlettype == 'tangential': f = friction_factor(1E5, roughness / dch) for run in range(5): vinlet = Q / (pi / 4 * Dinlet**2) vz = Q / (pi * Dtank * delta) K4 = Dinlet**2 * vinlet**2 / (2 * f * Dtank * H) K3 = vinlet / 4. - Dinlet**2 * vinlet / (4 * f * Dtank * H) vx0 = K3 + (K3**2 + K4)**0.5 vx = vinlet * log(1 + f * Dtank * H / Dinlet**2 * vx0 / vinlet) / ( f * Dtank * H / Dinlet**2) vch = (vx**2 + vz**2)**0.5 ReJ = vch * dch * rho / mu f = friction_factor(ReJ, roughness / dch) if inletlocation and rhow: GrJ = g * rho * (rho - rhow) * dch**3 / mu**2 if rhow < rho: # Heating jacket fluid if inletlocation == 'auto' or inletlocation == 'bottom': ReJeq = (ReJ**2 + GrJ * H / dch / 50.)**0.5 else: ReJeq = (ReJ**2 - GrJ * H / dch / 50.)**0.5 else: # Cooling jacket fluid if inletlocation == 'auto' or inletlocation == 'top': ReJeq = (ReJ**2 + GrJ * H / dch / 50.)**0.5 else: ReJeq = (ReJ**2 - GrJ * H / dch / 50.)**0.5 else: ReJeq = (ReJ**2)**0.5 NuA = 3.66 NuB = 1.62 * Pr**(1 / 3.) * ReJeq**(1 / 3.) * (dch / lch)**(1 / 3.) NuC = 0.664 * Pr**(1 / 3.) * (ReJeq * dch / lch)**0.5 if ReJeq < 2300: NuD = 0 else: NuD = 0.0115 * Pr**(1 / 3.) * ReJeq**0.9 * ( 1 - (2300. / ReJeq)**2.5) * (1 + (dch / lch)**(2 / 3.)) if muw: NuJ = (NuA**3 + NuB**3 + NuC**3 + NuD**3)**(1 / 3.) * (mu / muw)**0.14 else: NuJ = (NuA**3 + NuB**3 + NuC**3 + NuD**3)**(1 / 3.) h = NuJ * k / dch return h
def accept(self): Dp = Ltot = nc = 0 elements = list() Q = float(self.form.editFlow.text()) / 3600 if not self.isLiquid: Q = Q / self.Rho if self.form.comboWhat.currentText() == '<on selection>': elements = FreeCADGui.Selection.getSelection() else: o = FreeCAD.ActiveDocument.getObjectsByLabel( self.form.comboWhat.currentText())[0] if hasattr(o, 'PType') and o.PType == 'PypeBranch': elements = [ FreeCAD.ActiveDocument.getObject(name) for name in o.Tubes + o.Curves ] elif hasattr(o, 'PType') and o.PType == 'PypeLine': group = FreeCAD.ActiveDocument.getObjectsByLabel(o.Label + '_pieces')[0] elements = group.OutList self.form.editResults.clear() for o in elements: loss = 0 if hasattr(o, 'PType') and o.PType in ['Pipe', 'Elbow', 'Reduct']: if o.PType in ['Pipe', 'Elbow']: ID = float(o.ID) / 1000 else: ID = float(o.OD - 2 * o.thk) / 1000 e = float(self.form.editRough.text()) * 1e-6 / ID v = Q / ((ID)**2 * pi / 4) if isFluidsAvailable: Re = Reynolds(V=v, D=ID, rho=self.Rho, mu=self.Mu) f = friction.friction_factor(Re, eD=e) # Darcy, =4xFanning else: Re = v * ID * self.Rho / self.Mu if Re <= 2300: f = 64 / Re else: f = (-1.8 * log((e / 3.7)**1.11 + 6.9 / Re, 10))**-2 if o.PType == 'Pipe': L = float(o.Height) / 1000 Ltot += L if isFluidsAvailable: K = K_from_f(fd=f, L=L, D=ID) loss = dP_from_K(K, rho=self.Rho, V=v) else: loss = v**2 / 2 * self.Rho * f * L / ID self.form.editResults.append( '%s\t%.1f mm\t%.1f m/s\t%.5f bar' % (o.Label, ID * 1000, v, loss / 1e5)) elif o.PType == 'Elbow': ang = float(o.BendAngle) R = float(o.BendRadius) / 1000 nc += 1 if isFluidsAvailable: K = fittings.bend_rounded(ID, ang, f, R) loss = dP_from_K(K, rho=self.Rho, V=v) else: ang = radians(ang) K = f * ang * R / ID + (0.10 + 2.4 * f) * sin( ang / 2) + (6.6 * f * (sqrt(sin(ang / 2)) + sin(ang / 2))) / ( (R / ID)**(4 * ang / pi)) # Rennels loss = self.Rho * K * v**2 / 2 self.form.editResults.append( '%s\t%.1f mm\t%.1f m/s\t%.5f bar' % (o.Label, ID * 1000, v, loss / 1e5)) elif o.PType == 'Reduct': ID1 = float(o.OD - o.thk * 2) ID2 = float(o.OD2 - o.thk2 * 2) teta = 2 * atan((ID1 - ID2) / 2.0 / float(o.Height)) if isFluidsAvailable: K = fittings.contraction_conical(ID1, ID2, angle=degrees(teta), Re=Re) loss = dP_from_K(K, rho=self.Rho, V=v) else: beta = ID2 / ID1 if teta < pi / 4: K = 0.8 * sin(teta / 2) * (1 - beta**2) else: K = 0.5 * sqrt(sin(teta / 2)) * (1 - beta**2) loss = self.Rho * K * v**2 / 2 self.form.editResults.append( '%s\t%.1f mm\t%.1f m/s\t%.5f bar' % (o.Label, ID * 1000, v, loss / 1e5)) elif hasattr(o, 'Kv') and o.Kv > 0: if self.isLiquid: loss = (Q * 3600 / o.Kv)**2 * 100000 * self.Rho / 1000 elif self.form.comboFluid.currentText( ) == 'water' and not self.isLiquid: pass # TODO formula for steam else: pass # TODO formula for gases if hasattr(o, 'ID'): ID = float(o.ID) / 1000 v = Q / (ID**2 * pi / 4) else: v = 0 ID = 0 self.form.editResults.append( '%s\t%.1f mm\t%.1f m/s\t%.5f bar' % (o.Label, ID * 1000, v, loss / 1e5)) Dp += loss if Dp > 200: result = ' = %.3f bar' % (Dp / 100000) else: result = ' = %.2e bar' % (Dp / 100000) self.form.labResult.setText(result) self.form.labLength.setText('Total length = %.3f m' % Ltot) self.form.labCurves.setText('Nr. of curves = %i' % nc)
def Stein_Schmidt(m=None, Dtank=None, Djacket=None, H=None, Dinlet=None, rho=None, Cp=None, k=None, mu=None, muw=None, rhow=None, inlettype='tangential', inletlocation='auto', roughness=0): r'''Calculates average heat transfer coefficient for a jacket around a vessel according to [1]_ as described in [2]_. .. math:: l_{ch} = \left[\left(\frac{\pi}{2}\right)^2 D_{tank}^2+H^2\right]^{0.5} d_{ch} = 2\delta Re_j = \frac{v_{ch}d_{ch}\rho}{\mu} Gr_J = \frac{g\rho(\rho-\rho_w)d_{ch}^3}{\mu^2} Re_{J,eq} = \left[Re_J^2\pm \left(\frac{|Gr_J|\frac{H}{d_{ch}}}{50} \right)\right]^{0.5} Nu_J = (Nu_A^3 + Nu_B^3 + Nu_C^3 + Nu_D^3)^{1/3}\left(\frac{\mu} {\mu_w}\right)^{0.14} Nu_J = \frac{h d_{ch}}{k} Nu_A = 3.66 Nu_B = 1.62 Pr^{1/3}Re_{J,eq}^{1/3}\left(\frac{d_{ch}}{l_{ch}} \right)^{1/3} Nu_C = 0.664Pr^{1/3}(Re_{J,eq}\frac{d_{ch}}{l_{ch}})^{0.5} \text{if } Re_{J,eq} < 2300: Nu_D = 0 Nu_D = 0.0115Pr^{1/3}Re_{J,eq}^{0.9}\left(1 - \left(\frac{2300} {Re_{J,eq}}\right)^{2.5}\right)\left(1 + \left(\frac{d_{ch}}{l_{ch}} \right)^{2/3}\right) For Radial inlets: .. math:: v_{ch} = v_{Mit}\left(\frac{\ln\frac{b_{Mit}}{b_{Ein}}}{1 - \frac{b_{Ein}}{b_{Mit}}}\right) b_{Ein} = \frac{\pi}{8}\frac{D_{inlet}^2}{\delta} b_{Mit} = \frac{\pi}{2}D_{tank}\sqrt{1 + \frac{\pi^2}{4}\frac {D_{tank}^2}{H^2}} v_{Mit} = \frac{Q}{2\delta b_{Mit}} For Tangential inlets: .. math:: v_{ch} = (v_x^2 + v_z^2)^{0.5} v_x = v_{inlet}\left(\frac{\ln[1 + \frac{f_d D_{tank}H}{D_{inlet}^2} \frac{v_x(0)}{v_{inlet}}]}{\frac{f_d D_{tank}H}{D_{inlet}^2}}\right) v_x(0) = K_3 + (K_3^2 + K_4)^{0.5} K_3 = \frac{v_{inlet}}{4} -\frac{D_{inlet}^2v_{inlet}}{4f_d D_{tank}H} K_4 = \frac{D_{inlet}^2v_{inlet}^2}{2f_d D_{tank} H} v_z = \frac{Q}{\pi D_{tank}\delta} v_{inlet} = \frac{Q}{\frac{\pi}{4}D_{inlet}^2} Parameters ---------- m : float Mass flow rate of fluid, [kg/m^3] Dtank : float Outer diameter of tank or vessel surrounded by jacket, [m] Djacket : float Inner diameter of jacket surrounding a vessel or tank, [m] H : float Height of the vessel or tank, [m] Dinlet : float Inner diameter of inlet into the jacket, [m] rho : float Density of the fluid at Tm [kg/m^3] Cp : float Heat capacity of fluid at Tm [J/kg/K] k : float Thermal conductivity of fluid at Tm [W/m/K] mu : float Viscosity of fluid at Tm [Pa*s] muw : float, optional Viscosity of fluid at Tw [Pa*s] rhow : float, optional Density of the fluid at Tw [kg/m^3] inlettype : str, optional Either 'tangential' or 'radial' inletlocation : str, optional Either 'top' or 'bottom' or 'auto' roughness : float, optional Roughness of the tank walls [m] Returns ------- h: float Average transfer coefficient inside the jacket [W/m^2/K] Notes ----- [1]_ is in German and has not been reviewed. Multiple other formulations are considered in [1]_. If the fluid is heated and enters from the bottom, natural convection assists the heat tansfer and the Grashof term is added; if it were to enter from the top, it would be substracted. The situation is reversed if entry is from the top. Examples -------- Example as in [2]_, matches in all but friction factor: >>> Stein_Schmidt(m=2.5, Dtank=0.6, Djacket=0.65, H=0.6, Dinlet=0.025, ... rho=995.7, Cp=4178.1, k=0.615, mu=798E-6, muw=355E-6, rhow=971.8) 5695.1871940874225 References ---------- .. [1] Stein, Prof Dr-Ing Werner Alexander, and Dipl-Ing (FH) Wolfgang Schmidt. "Wärmeübergang auf der Wärmeträgerseite eines Rührbehälters mit einem einfachen Mantel." Forschung im Ingenieurwesen 59, no. 5 (May 1993): 73-90. doi:10.1007/BF02561203. .. [2] Gesellschaft, V. D. I., ed. VDI Heat Atlas. 2nd edition. Berlin; New York:: Springer, 2010. ''' delta = (Djacket-Dtank)/2. Q = m/rho Pr = Cp*mu/k lch = (pi**2/4*Dtank**2 + H**2)**0.5 dch = 2*delta if inlettype == 'radial': bEin = pi/8*Dinlet**2/delta bMit = pi/2*Dtank*(1 + pi**2/4*Dtank**2/H**2)**0.5 vMit = Q/(2*delta*bMit) vch = vMit*log(bMit/bEin)/(1 - bEin/bMit) ReJ = vch*dch*rho/mu elif inlettype == 'tangential': f = friction_factor(1E5, roughness/dch) for run in range(5): vinlet = Q/(pi/4*Dinlet**2) vz = Q/(pi*Dtank*delta) K4 = Dinlet**2*vinlet**2/(2*f*Dtank*H) K3 = vinlet/4. - Dinlet**2*vinlet/(4*f*Dtank*H) vx0 = K3 + (K3**2 + K4)**0.5 vx = vinlet*log(1 + f*Dtank*H/Dinlet**2*vx0/vinlet)/(f*Dtank*H/Dinlet**2) vch = (vx**2 + vz**2)**0.5 ReJ = vch*dch*rho/mu f = friction_factor(ReJ, roughness/dch) if inletlocation and rhow: GrJ = g*rho*(rho-rhow)*dch**3/mu**2 if rhow < rho: # Heating jacket fluid if inletlocation == 'auto' or inletlocation == 'bottom': ReJeq = (ReJ**2 + GrJ*H/dch/50.)**0.5 else: ReJeq = (ReJ**2 - GrJ*H/dch/50.)**0.5 else: # Cooling jacket fluid if inletlocation == 'auto' or inletlocation == 'top': ReJeq = (ReJ**2 + GrJ*H/dch/50.)**0.5 else: ReJeq = (ReJ**2 - GrJ*H/dch/50.)**0.5 else: ReJeq = (ReJ**2)**0.5 NuA = 3.66 NuB = 1.62*Pr**(1/3.)*ReJeq**(1/3.)*(dch/lch)**(1/3.) NuC = 0.664*Pr**(1/3.)*(ReJeq*dch/lch)**0.5 if ReJeq < 2300: NuD = 0 else: NuD = 0.0115*Pr**(1/3.)*ReJeq**0.9*(1 - (2300./ReJeq)**2.5)*(1 + (dch/lch)**(2/3.)) if muw: NuJ = (NuA**3 + NuB**3 + NuC**3 + NuD**3)**(1/3.)*(mu/muw)**0.14 else: NuJ = (NuA**3 + NuB**3 + NuC**3 + NuD**3)**(1/3.) h = NuJ*k/dch return h
def accept(self): Dp=Ltot=nc=0 elements=list() Q=float(self.form.editFlow.text())/3600 if not self.isLiquid: Q=Q/self.Rho if self.form.comboWhat.currentText()=='<on selection>': elements = FreeCADGui.Selection.getSelection() else: o=FreeCAD.ActiveDocument.getObjectsByLabel(self.form.comboWhat.currentText())[0] if hasattr(o,'PType') and o.PType=='PypeBranch': elements=[FreeCAD.ActiveDocument.getObject(name) for name in o.Tubes+o.Curves] elif hasattr(o,'PType') and o.PType=='PypeLine': group=FreeCAD.ActiveDocument.getObjectsByLabel(o.Label+'_pieces')[0] elements=group.OutList self.form.editResults.clear() for o in elements: loss=0 if hasattr(o,'PType') and o.PType in ['Pipe','Elbow']: ID=float(o.ID)/1000 e=float(self.form.editRough.text())*1e-6/ID v=Q/((ID)**2*pi/4) Re=Reynolds(V=v,D=ID,rho=self.Rho, mu=self.Mu) f=friction.friction_factor(Re, eD=e) # Darcy, =4xFanning if o.PType=='Pipe': L=float(o.Height)/1000 Ltot+=L K=K_from_f(fd=f, L=L, D=ID) loss=dP_from_K(K,rho=self.Rho,V=v) FreeCAD.Console.PrintMessage('%s: %s\nID=%.2f\nV=%.2f\ne=%f\nf=%f\nK=%f\nL=%.3f\nDp = %.5f bar\n***'%(o.PType,o.Label,ID,v,e,f,K,L,loss/1e5)) self.form.editResults.append('%s\t%.1f mm\t%.1f m/s\t%.5f bar'%(o.Label,ID*1000,v,loss/1e5)) elif o.PType=='Elbow': ang=float(o.BendAngle) R=float(o.BendRadius)/1000 nc+=1 K=fittings.bend_rounded(ID,ang,f,R) loss=dP_from_K(K,rho=self.Rho,V=v) FreeCAD.Console.PrintMessage('%s: %s\nID=%.2f\nV=%.2f\ne=%f\nf=%f\nK=%f\nang=%.3f\nR=%f\nDp = %.5f bar\n***'%(o.PType,o.Label,ID,v,e,f,K,ang,R,loss/1e5)) self.form.editResults.append('%s\t%.1f mm\t%.1f m/s\t%.5f bar'%(o.Label,ID*1000,v,loss/1e5)) elif o.PType=='Reduct': pass elif hasattr(o,'Kv') and o.Kv>0: if self.isLiquid: loss=(Q*3600/o.Kv)**2*100000 elif self.form.comboFluid.currentText()=='water' and not self.isLiquid: pass # TODO formula for steam else: pass # TODO formula for gases if hasattr(o,'ID'): ID = float(o.ID)/1000 v=Q/(ID**2*pi/4) else: v = 0 ID = 0 self.form.editResults.append('%s\t%.1f mm\t%.1f m/s\t%.5f bar'%(o.Label,ID*1000,v,loss/1e5)) Dp+=loss if Dp>200: result=' = %.3f bar'%(Dp/100000) else: result=' = %.2e bar'%(Dp/100000) self.form.labResult.setText(result) self.form.labLength.setText('Total length = %.3f m' %Ltot) self.form.labCurves.setText('Nr. of curves = %i' %nc)
roughness = .05E-3 plt.plot(Crane_fts_Ds, Crane_fts, 'o', label='Crane data') spline_obj = UnivariateSpline(Crane_fts_Ds, Crane_fts, k=3, s=5e-6) Ds_interp = np.linspace(Crane_fts_Ds[0], Crane_fts_Ds[-1], 500) plt.plot(Ds_interp, spline_obj(Ds_interp), label='Cubic spline') ft_crane_correlation = [.25/log10(roughness/(Di)/3.7)**2 for Di in Crane_fts_Ds] plt.plot(Crane_fts_Ds, ft_crane_correlation, label='Crane formula') plt.plot(Crane_fts_Ds, [round(i, 3) for i in ft_crane_correlation], '.', label='Crane formula (rounded)') eDs_Farshad = [roughness_Farshad(ID='Carbon steel, bare', D=D)/D for D in Crane_fts_Ds] fts_good = [friction_factor(Re=7.5E6*Di, eD=ed) for ed, Di in zip(eDs_Farshad, Crane_fts_Ds)] plt.plot(Crane_fts_Ds, fts_good, label='Colebrook') plt.plot(Crane_fts_Ds, [round(i, 3) for i in fts_good], 'x', label='Colebrook (rounded)') plt.legend() plt.title("Comparison of implementation options") plt.xlabel('Pipe actual diameter, [m]') plt.ylabel('Darcy friction factor, [-]') #plt.show()
def solve_network_flows(case_dom): """Find the volumetric flow and head loss for the piping network defined in the case_dom structure by using the first method described in Chapter 6 of Jeppson. Args: case_dom (dict): Pipe flow network object model Raises: ValueError: Network solution matrix is singular or does not converge. """ # The goal of this project is to reimplement the JEPPSON_CH7 code in Python, # not simply translate the original Fortran into Python. For this reason, pipe, # junction, and loop indices are zero-based, default NumPy matrix storage is # used. This complicates the comparison between the original Fortran and the # Python implementations, but effectively illusrates the differences in # implementing the solution in each language. # The matrix a is constructed in the same manner as in the original Fortran # application with a few modifications. While NumPy multidimensional array # storage can be set to either C-style (row-major) or Fortran-style # (column-major), the default NumPy style is used - C-style. # Matrix columns represent flow through pipes. The first (njunctions-1) rows # contain 1.0 in a column for outflow via that column's pipe or -1.0 for inflow # from that column's pipe. The corresponding term in the first (njunctions - 1) # rows of the column vector b contains the fixed flow into the junction from # outside the system, positive for inflow and negative for outflow. # The remaining nloops rows in the a matrix contain the flow resistance of each # pipe, positive if the flow convention is clockwise/forward in the loop, # negative if the flow convention is counter-clockwise/reverse in the loop. # Flow direction/convention is heuristically assigned by the modeler; the # actual flow direction will be determined from the problem solution. The # corresponding entries in the b column vector are zero since the flow around a # loop is conservative - no net increase or decrease. nct = 0 ssum = 100.0 kin_visc = case_dom['params']['kin_visc'] # Set kp and expp for each pipe for pipe in case_dom['pipe']: qm = abs(pipe['init_vol_flow']) pipe['vol_flow'] = qm dq = case_dom['params']['fvol_flow'] * qm q1 = qm - dq q2 = qm + dq v1 = q1 / pipe['flow_area'].to('ft**2') v2 = q2 / pipe['flow_area'].to('ft**2') Re1 = (v1 * pipe['idiameter'].to('ft') / kin_visc) \ .to_base_units().magnitude Re2 = (v2 * pipe['idiameter'].to('ft') / kin_visc) \ .to_base_units().magnitude f1 = friction_factor(Re=Re1, eD=pipe['eroughness']) f2 = friction_factor(Re=Re2, eD=pipe['eroughness']) _logger.debug('Pipe {0:d} Re = {1:0.4E}'.format(pipe['id'], Re1)) if Re1 < 2050.0: # laminar pipe['expp'] = 1.0 pipe['kp'] = (case_dom['params']['grav2'] * kin_visc * pipe['arl'] / pipe['idiameter'].to('ft')).magnitude else: # Transition/tubulent be = ((log(f1) - log(f2)) / (log(q2.to('ft**3/s').magnitude) - log(q1.to('ft**3/s').magnitude))) ae = f1 * (q1.to('ft**3/s').magnitude)**be pipe['expp'] = 2.0 - be pipe['kp'] = (ae * pipe['arl']).magnitude _logger.debug('Pipe {0:d} kp = {1:0.4E} expp = {2:0.4f}'.format( pipe['id'], pipe['kp'], pipe['expp'])) _logger.debug('Pipe {0:d} ae = {1:0.4f} be = {2:0.4f}'.format( pipe['id'], ae, be)) done = False converged = False while not done: # Step 5. Iteratively correct flows _logger.debug('Iteratively correct flows') ssum = 0.0 for iloop, looppipe in enumerate(case_dom['loop']): sum1 = 0.0 sum2 = 0.0 for pipe in looppipe: pid = pipe['pipe_id'] currpipe = case_dom['pipe'][pid] hl = ((pipe['flow_dir'] * currpipe['kp'] * currpipe['vol_flow'].to('ft**3/s').magnitude** currpipe['expp'])) currpipe['head_loss'] = Q_(abs(hl), 'ft') sum1 += hl _logger.debug('q = {0:0.4E~}'.format(currpipe['vol_flow'])) _logger.debug('expp = {0:0.4E}'.format(currpipe['expp'])) _logger.debug('hl = {0:0.4E~}'.format(currpipe['head_loss'])) sum2 += (currpipe['expp'] * abs(hl) / (currpipe['vol_flow'].to('ft**3/s').magnitude)) dq = sum1 / sum2 # 5c) Increment the deviation accumulator with the net flow # correction ssum = ssum + abs(dq) for pipe in looppipe: pid = pipe['pipe_id'] currpipe = case_dom['pipe'][pid] currpipe['vol_flow'] -= Q_(pipe['flow_dir'] * dq, 'ft**3/s') converged = ssum <= case_dom['params']['tolerance'] done = converged or (nct >= case_dom['params']['maxiter']) # End iteration # ######################################################################## if not converged: msg = 'Case not converged: ssum = {0:0.4E} > tolerance {1:0.4E}' \ .format(ssum, case_dom['params']['tolerance']) # Advance to next case raise ValueError(msg) return