''' JMK Engineering Inc. Python Library for design and such. by: Jeff MacKinnon email: jeff@jmkengineering.com Circuit Design Functions ''' import pandas as pd import numpy as np import math # Need to add the various table files here. from .jepl_tables import * def voltage_drop(voltage, current, conductor_size, length, num_phase = 3, material ='cu', num_runs = 1, code = 'CEC', power_factor = 'dc', raceway = True,insul_temp = 75): ''' This function will return the drop in voltage and in percent of the supply. For CEC this function uses Table D3 and the function VD = K * F * i * L / 1000 K -> is a table lookup based on the conductor material, size raceway and powerfactor F -> is the system factor, 2 for single phase and sqrt(3) for three phase ''' material = material.upper() code = code.upper() power_factor = power_factor.upper() #conductor_size = str(conductor_size) valid_insul_temp = [60,75,90] valid_code = ['CEC', ] valid_material = ['CU', 'AL', ] valid_power_factor = ['DC', '1', '0.9', '0.8' ] if insul_temp not in valid_insul_temp: return print(temp + " is not valid. The valid temps are "+ str(valid_insul_temp)) if code not in valid_code: return print(code + " is not a valid code. The valid codes are "+ str(valid_code)) if material not in valid_material: return print(material + " is not a valid material. I should be 'al' or 'cu'.") if power_factor not in valid_power_factor: return print(valid_power_factor + " is not a valid load_type.") # # Find K # # Select the correct table if (code == 'CEC') : D3headers = cectableD3[0] D3rows = cectableD3[1:] df = pd.DataFrame(D3rows, columns=D3headers) else: return ('The variables were\'t right, but I\'m a loss to why.') # Select the correct column if (material == 'CU') & (power_factor == 'DC'): column = 'cu_dc' elif material == 'CU' & power_factor == '1': column = 'cu_100pf' elif material == 'CU' & power_factor == '0.9' & raceway == False: column = 'cu_cable_90pf' elif material == 'CU' & power_factor == '0.8' & raceway == False: column = 'cu_cable_80pf' elif material == 'CU' & power_factor == '0.9' & raceway == True: column = 'cu_raceway_90pf' elif material == 'CU' & power_factor == '0.8' & raceway == True: column = 'cu_raceway_80pf' elif material == 'AL' & power_factor == 'DC': column = 'al_dc' elif material == 'AL' & power_factor == '1': column = 'al_100pf' elif material == 'AL' & power_factor == '0.9' & raceway == False: column = 'al_cable_90pf' elif material == 'AL' & power_factor == '0.8' & raceway == False: column = 'al_cable_80pf' elif material == 'AL' & power_factor == '0.9' & raceway == True: column = 'al_raceway_90pf' elif material == 'AL' & power_factor == '0.8' & raceway == True: column = 'al_raceway_80pf' else: return ('Can\'t calculate K factor') # Determine the ampacity of the max conductor size K_loc = df.loc[df['size'] == conductor_size,column] K = K_loc.item() #print(K) # # Find F # if num_phase == 3: F = math.sqrt(3) else: F = 2 #print(F) #print(current) #print(length) voltage_drop = K * F * current * length / 1000 percent_voltage_drop = (voltage_drop / voltage) return [voltage_drop, percent_voltage_drop] def current_for_lookup(current,max_current): ''' This is a helper function for conductor_size. It is used to calculate the number of parallel runs needed, and the conductor current for those runs. ''' num_parallel = math.ceil(current / max_current) con_current = current / num_parallel return (con_current,num_parallel) def conductor_size(current, temp = 75, material = 'cu', code = 'CEC', raceway = True, ambient = 30, max = 500, load_type = None): ''' The default temp column will be the 75C column as most terminals are rated for this. The default code is CEC as that is where I am. I still need to incorporate ambient temperature deratings, but that will be a future improvement ''' material = material.upper() code = code.upper() max = str(max) valid_temp = [60,75,90] valid_temp_str = [str(x) for x in valid_temp] valid_code = ['CEC', ] valid_material = ['CU', 'AL', ] valid_load_type = ['normal','xfmr','xfmrp','xfmrs','motor',None] #check to make sure that the values are valid if temp not in valid_temp: return print(temp + " is not valid. The valid temps are "+ str(valid_temp_str)) if code not in valid_code: return print(code + " is not a valid code. The valid codes are "+ str(valid_code)) if material not in valid_material: return print(material + " is not a valid material. I should be 'al' or 'cu'.") if load_type not in valid_load_type: return print(load_type + " is not a valid load_type.") if temp == 90: column = '90C' elif temp == 75: column = '75C' else: column = '60C' ''' Per CEC rules 26-256 and 28-106 Transformer and Motor conductors should be sized 125% of the rated current. ''' list_125 = ['xfmr','xfmrp','xfmrs', 'motor'] if load_type in list_125: current = 1.25 * current # Seclect the proper table if (code == 'CEC') & (material == 'CU') & (raceway == False): df = pd.DataFrame(cectable1, columns=['size', '60C', '75C', '90C']) elif (code == 'CEC') & (material == 'CU') & (raceway == True): df = pd.DataFrame(cectable2, columns=['size', '60C', '75C', '90C']) elif (code == 'CEC') & (material == 'AL') & (raceway == False): df = pd.DataFrame(cectable3, columns=['size', '60C', '75C', '90C']) elif (code == 'CEC') & (material == 'AL') & (raceway == True): df = pd.DataFrame(cectable4, columns=['size', '60C', '75C', '90C']) elif (code =='NEC') & (material =='CU'): return (' I haven\'t created this table yet') elif (code =='NEC') & (material =='AL'): return (' I haven\'t created this table yet') else: return ('The variables were\'t right, but I\'m a loss to why.') ''' Using the correct table, calculate the number of parallel runs needed with the maximum conductor size, and the resulting wire size. ''' # Determine the ampacity of the max conductor size max_df = df.loc[df['size'] == max,column] max_current = max_df.item() #print(max_current) # The current that we will need to current_lookup = current_for_lookup(current,max_current) current = current_lookup[0] num_parallel = current_lookup[1] # Calculate the absolute difference for all values and find the index of the minimum difference sectioned_df = df.loc[df[column] >= current,column] closest_index = sectioned_df.idxmin() # Retrieve the nearest value using the found index conductor_size = df['size'].iloc[closest_index] return [conductor_size,num_parallel] def conductor_ampacity(conductor, temp = 75, material = 'cu', code = 'CEC', raceway = True, ambient = 30): ''' Calculates the ampacity of a conductor size and material using code tables. ''' material = material.upper() code = code.upper() valid_temp = [60,75,90] valid_temp_str = [str(x) for x in valid_temp] valid_code = ['CEC', ] valid_material = ['CU', 'AL', ] if temp == 90: column = '90C' elif temp == 75: column = '75C' else: column = '60C' if (code == 'CEC') & (material == 'CU') & (raceway == False): df = pd.DataFrame(cectable1, columns=['size', '60C', '75C', '90C']) elif (code == 'CEC') & (material == 'CU') & (raceway == True): df = pd.DataFrame(cectable2, columns=['size', '60C', '75C', '90C']) elif (code == 'CEC') & (material == 'AL') & (raceway == False): df = pd.DataFrame(cectable3, columns=['size', '60C', '75C', '90C']) elif (code == 'CEC') & (material == 'AL') & (raceway == True): df = pd.DataFrame(cectable4, columns=['size', '60C', '75C', '90C']) elif (code =='NEC') & (material =='CU'): return (' I haven\'t created this table yet') elif (code =='NEC') & (material =='AL'): return (' I haven\'t created this table yet') else: return ('The variables were\'t right, but I\'m a loss to why.') # Determine the ampacity of the conductor size result_df = df.loc[df['size'] == conductor,column] conductor_ampacity = result_df.item() #print(conductor_ampacity) return conductor_ampacity ''' This function calculates the bonding wire or bus based on the circuit ampacity. ''' material = material.upper() code = code.upper() valid_code = ['CEC', ] valid_material = ['CU', 'AL', ] # Select the correct table if code == 'CEC': table16headers = cectable16[0] table16rows = cectable16[1:] df = pd.DataFrame(table16rows, columns=table16headers) else: return ('The variables were\'t right, but I\'m a loss to why.') # Select the correct column if material == 'CU' and bus == False: column = 'copper wire' elif material == 'CU' and bus == True: column = 'copper bus' elif material == 'AL' and bus == False: column = 'aluminum wire' elif material == 'AL' and bus == True: column = 'caluminum bus' else: return ('Can\'t calculate K factor') # Calculate the absolute difference for all values and find the index of the minimum difference sectioned_df = df.loc[df['current'] >= conductor_ampacity,'current'] closest_index = sectioned_df.idxmin() # Retrieve the nearest value using the found index bond_size = df[column].iloc[closest_index] return bond_size ## This doesn't work yet, but its getting def conduit_size(num_cc,cc_con,bond,material='SCH80'): # Calculate fill requirements based on Table 8 valid_material = ['RMC', # Rigid Metal Conduit 'FMC', # Flexible Metal Conduit 'RPVC', # Rigid PVC 'EB1', # Type EB1 'DB2', # Type DB2 'LTMC', # Liquid Tight Metal Conduit 'LTNMC', # Liquid Tight non-metallic conduit 'EMT', # electrical metallic tubing 'ENT', # electrical non-metallic tubing 'SCH40', # HDPE Schedule 40 'SCH80', # HDPE Schedule 80 #'DR9', # HDPE DR9 #'DR11', # HDPE DR11 #'DR135', # HDPE DR13.5 #'DR155' # HDPE DR15.5 ] if material not in valid_material: return print(material + " is not a valid material. I should be 'al' or 'cu'.") import numpy as np x = np.array(valid_material) db_result_index = np.where(x == material)[0][0] if num_cc == 1: percent_fill = 0.53 elif num_cc == 2: percent_fill = 0.31 else: percent_fill = 0.4 # Wire Size and diameter wire_size = [ # ['tradesize',area mm^2] ['14',2.08], ['12',3.31], ['10',5.26], ['8',8.37], ['6',13.3], ['4',21.2], ['3',26.7], ['2',33.6], ['1',42.4], ['1/0',53.5], ['2/0',67.4], ['3/0',85], ['4/0',107], ['250',127], ['300',152], ['350',177], ['400',203], ['500',253], ['600',304], ['700',355], ['800',405], ['900',456], ['1000',507], ['1250',633], ['1500',760], ['1750',887], ['2000',1010] ] # Calculate the area of current carrying conductors x = np.array(wire_size) row = np.where(x == cc_con)[0][0] current_carrying_conductor_area = wire_size[row][1] cc_area = current_carrying_conductor_area * num_cc # Bond Area row = np.where(x == bond)[0][0] bond_area = wire_size[row][1] # Total conductor area area_conductors = cc_area + bond_area #print(area_conductors) min_trade_area = area_conductors / percent_fill # The minimum area of the conduit #print(min_trade_area) parameter = ' WHERE ' + material + ' > ' + str(min_trade_area) try: with sqlite3.connect("jepl-cec21.db") as con: cur = con.cursor() cur.execute('SELECT "Trade Size" from "Table9"'+ parameter ) table = cur.fetchone() conduit = table except sqlite3.OperationalError as e: print(e) result_raw = conduit[0] result_name = str(conduit[0]) + 'mm ' + material return result_raw,result_name def cable_schedule_naming(conductor_size,conductors,runs = 1,bond='BOND'): ''' Converts the conductor size from the above functions to something that can be added to a database/schedule. ''' if conductor_size == '1/0' or conductor_size == '2/0' or conductor_size == '3/0' or conductor_size == '4/0': unit = "AWG" elif int(conductor_size) > 24: unit = 'kcmil' else: unit = 'AWG' if bond == 'BOND': bondtext = bond elif int(bond) > 24: bondtext = '#' + str(bond) + 'kcmil' else: bondtext = '#' + str(bond) + 'AWG' if runs > 1: cable_text = str(runs) + "x " + str(conductors) + "C #" + str(conductor_size) + unit + " + " + bondtext else: cable_text = str(conductors) + "C #" + str(conductor_size) + unit + " + " + bondtext return cable_text