659 lines
20 KiB
Python
659 lines
20 KiB
Python
'''
|
|
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
|
|
import sqlite3
|
|
import bisect
|
|
|
|
|
|
def vd(current,length,resistance,runs=1):
|
|
|
|
'''
|
|
Calculates the voltage drop across the conductor length.
|
|
|
|
If there are parallel runs we assume that they are approximately the same length.
|
|
Therefore from the general equation of:
|
|
|
|
1/Req = 1/R1 + 1/R2 ... and all R1-Rn are the same.
|
|
Req = R/n or in the equation below r = resistance / runs.
|
|
'''
|
|
|
|
r = (length*resistance/1000) / runs
|
|
vd=round(2*current*r,8)
|
|
return vd
|
|
|
|
def percentvd(vd,nominal):
|
|
|
|
percent = (vd/nominal)*100
|
|
return percent
|
|
|
|
|
|
def voltage_drop(nominal_voltage, current, conductor_size, length,material ='cu', num_runs = 1, code = 'CEC'):
|
|
|
|
'''
|
|
This function will return the drop in voltage and in percent of the supply.
|
|
|
|
nominal_voltage = int
|
|
current = float
|
|
conductor_size = string
|
|
|
|
Voltage drop equation is:
|
|
|
|
Vdrop = 2 * I * L * R/1000
|
|
|
|
I = load current
|
|
L = circuit length in meters
|
|
R = Resistance in ohms/km
|
|
|
|
'''
|
|
|
|
# Check to see if the db we need exists
|
|
|
|
import os
|
|
import sys
|
|
if os.path.isfile('jepl-cable.db') == False:
|
|
return (print("Run init. \nCopy jeplinit.py to the same folder as this file and add \n%run jeplinit.py jepl/folder/location/\nto the notebook. Make sure there is a trailing slash."))
|
|
|
|
if (material == 'al'):
|
|
|
|
try:
|
|
with sqlite3.connect("jepl-cable.db") as con:
|
|
cur = con.cursor()
|
|
cur.execute('SELECT "AC Resistance" FROM "SW-Spec 25051" WHERE "Conductor Number" = 3 AND "Conductor Size"=?', (conductor_size,))
|
|
resistance = cur.fetchone()[0]
|
|
|
|
#print(resistance)
|
|
|
|
except sqlite3.OperationalError as e:
|
|
print(e)
|
|
|
|
elif (material == 'cu'):
|
|
try:
|
|
with sqlite3.connect("jepl-cable.db") as con:
|
|
cur = con.cursor()
|
|
cur.execute('SELECT "AC Resistance" FROM "SW-Spec 25055" WHERE "Conductor Number" = 3 AND "Conductor Size"=?', (conductor_size,))
|
|
resistance = cur.fetchone()[0]
|
|
|
|
except sqlite3.OperationalError as e:
|
|
print(e)
|
|
|
|
else:
|
|
return (print("error, choose material as cu or al"))
|
|
|
|
voltage = vd(current,length,resistance,num_runs)
|
|
percent = percentvd(voltage,nominal_voltage)
|
|
|
|
return [voltage, percent]
|
|
#
|
|
# voltage_drop_conductors is wrong
|
|
#
|
|
#
|
|
def voltage_drop_conductors(voltage,current,distance,v_drop_percent = 0.03,runs = 1,material='cu'):
|
|
'''
|
|
Calculates the minimum conductor size to accomodate for voltage drop based on:
|
|
|
|
voltage -> system nominal voltage
|
|
current -> the peak/design load for the circuit
|
|
distance -> meters
|
|
v_drop_percent -> The design voltage drop (default 0.03 or 3%)
|
|
runs -> number of parallel runs (default 1)
|
|
material -> the conductor material, either 'al' or 'cu'. (default 'cu')
|
|
|
|
First we calculate the necessary resistivity:
|
|
|
|
|
|
|
|
resistivity = ohms/km
|
|
but the distance is 2x (there and back)
|
|
resistivity = ohms/[(2 * distance)/1000] {ohms/km}
|
|
ohms = v/I or (v_drop/voltage)/current
|
|
|
|
v_drop = v_drop_percent * voltage
|
|
|
|
resistivity = (v_drop/current)/[(2 * distance)/1000]
|
|
|
|
This works for 1 run, but for parallel runs Rtot = R/n where n is the number of runs.
|
|
in this equation we are looking for R, not Rtot, so we multiply the top by the number of runs.
|
|
|
|
therefore:
|
|
resistivity = [(v_drop/current)*runs]/[(2 * distance)/1000]
|
|
|
|
'''
|
|
|
|
# Determine the resistivity needed in ohms/km
|
|
|
|
v_drop = v_drop_percent * voltage
|
|
|
|
resistivity = ((v_drop/current)*runs)/((2 * distance)/1000)
|
|
|
|
if resistivity < 0.1214:
|
|
print("add parallel runs")
|
|
|
|
import os
|
|
import sys
|
|
if os.path.isfile('jepl-cable.db') == False:
|
|
return (print("Run init. \nCopy jeplinit.py to the same folder as this file and add \n%run jeplinit.py jepl/folder/location/\nto the notebook. Make sure there is a trailing slash."))
|
|
|
|
# Lookup the conductor size that meets this resistivity.
|
|
|
|
if (material == 'al'):
|
|
|
|
try:
|
|
with sqlite3.connect("jepl-cable.db") as con:
|
|
cur = con.cursor()
|
|
cur.execute('SELECT "Conductor Size" FROM "SW-Spec 25055" WHERE "Conductor Number" = 3 AND "AC Resistance"<?', (resistivity,))
|
|
conductor = str(cur.fetchone()[0])
|
|
|
|
#print(conductor)
|
|
|
|
except sqlite3.OperationalError as e:
|
|
print(e)
|
|
|
|
elif (material == 'cu'):
|
|
try:
|
|
with sqlite3.connect("jepl-cable.db") as con:
|
|
cur = con.cursor()
|
|
cur.execute('SELECT "Conductor Size" FROM "SW-Spec 25051" WHERE "Conductor Number" = 3 AND "AC Resistance"<?', (resistivity,))
|
|
conductor = str(cur.fetchone()[0])
|
|
|
|
#print(conductor)
|
|
|
|
except sqlite3.OperationalError as e:
|
|
print(e)
|
|
|
|
else:
|
|
return (print("error, choose material as cu or al"))
|
|
|
|
return (conductor)
|
|
|
|
# These functions need to be re-written with the databases.
|
|
|
|
|
|
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:
|
|
conductor_current_index = 3
|
|
elif temp == 75:
|
|
conductor_current_index = 2
|
|
else:
|
|
conductor_current_index = 1
|
|
|
|
|
|
'''
|
|
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'):
|
|
import Tables.CEC21Tables
|
|
|
|
if (material == 'CU') & (raceway == False):
|
|
table = table1
|
|
|
|
|
|
elif (material == 'CU') & (raceway == True):
|
|
table = table3
|
|
|
|
# Determine the ampacity of the max conductor size
|
|
|
|
|
|
|
|
# select the correct code table
|
|
if (code == 'CEC'): # CEC Table 1
|
|
|
|
|
|
try:
|
|
with sqlite3.connect("jepl-cec21.db") as con:
|
|
cur = con.cursor()
|
|
cur.execute('SELECT * FROM "Table1" WHERE "size" = ? ', (max,))
|
|
max_conductor_current = cur.fetchone()
|
|
max_current = max_conductor_current[conductor_current_index]
|
|
|
|
except sqlite3.OperationalError as e:
|
|
print(e)
|
|
|
|
current_lookup = current_for_lookup(current,max_current)
|
|
current = current_lookup[0]
|
|
num_parallel = current_lookup[1]
|
|
|
|
try:
|
|
with sqlite3.connect("jepl-cec21.db") as con:
|
|
cur = con.cursor()
|
|
if temp == 90:
|
|
cur.execute('SELECT size FROM "Table1" WHERE "90" > ? ', (current,))
|
|
|
|
elif temp ==75:
|
|
cur.execute('SELECT size FROM "Table1" WHERE "75" > ? ', (current,))
|
|
|
|
else:
|
|
cur.execute('SELECT size FROM "Table1" WHERE "60" > ? ', (current,))
|
|
conductor_size = cur.fetchone()[0]
|
|
|
|
#print(conductor_size)
|
|
|
|
except sqlite3.OperationalError as e:
|
|
print(e)
|
|
|
|
elif (code == 'CEC') & (material == 'CU') & (raceway == True): # CEC Table 2
|
|
try:
|
|
with sqlite3.connect("jepl-cec21.db") as con:
|
|
cur = con.cursor()
|
|
cur.execute('SELECT * FROM "Table2" WHERE "size" = ? ', (max,))
|
|
max_conductor_current = cur.fetchone()
|
|
max_current = max_conductor_current[conductor_current_index]
|
|
|
|
except sqlite3.OperationalError as e:
|
|
print(e)
|
|
|
|
current_lookup = current_for_lookup(current,max_current)
|
|
current = current_lookup[0]
|
|
num_parallel = current_lookup[1]
|
|
|
|
try:
|
|
with sqlite3.connect("jepl-cec21.db") as con:
|
|
cur = con.cursor()
|
|
if temp == 90:
|
|
cur.execute('SELECT size FROM "Table2" WHERE "90" > ? ', (current,))
|
|
|
|
elif temp ==75:
|
|
cur.execute('SELECT size FROM "Table2" WHERE "75" > ? ', (current,))
|
|
|
|
else:
|
|
cur.execute('SELECT size FROM "Table2" WHERE "60" > ? ', (current,))
|
|
conductor_size = cur.fetchone()[0]
|
|
|
|
#print(conductor_size)
|
|
|
|
except sqlite3.OperationalError as e:
|
|
print(e)
|
|
elif (code =='CEC') & (material =='AL') & (raceway == False): # CEC Table 3
|
|
try:
|
|
with sqlite3.connect("jepl-cec21.db") as con:
|
|
cur = con.cursor()
|
|
cur.execute('SELECT * FROM "Table3" WHERE "size" = ? ', (max,))
|
|
max_conductor_current = cur.fetchone()
|
|
max_current = max_conductor_current[conductor_current_index]
|
|
|
|
except sqlite3.OperationalError as e:
|
|
print(e)
|
|
|
|
current_lookup = current_for_lookup(current,max_current)
|
|
current = current_lookup[0]
|
|
num_parallel = current_lookup[1]
|
|
|
|
try:
|
|
with sqlite3.connect("jepl-cec21.db") as con:
|
|
cur = con.cursor()
|
|
if temp == 90:
|
|
cur.execute('SELECT size FROM "Table3" WHERE "90" > ? ', (current,))
|
|
|
|
elif temp ==75:
|
|
cur.execute('SELECT size FROM "Table3" WHERE "75" > ? ', (current,))
|
|
|
|
else:
|
|
cur.execute('SELECT size FROM "Table3" WHERE "60" > ? ', (current,))
|
|
conductor_size = cur.fetchone()[0]
|
|
|
|
#print(conductor_size)
|
|
|
|
except sqlite3.OperationalError as e:
|
|
print(e)
|
|
|
|
|
|
elif (code =='CEC') & (material =='AL') & (raceway == True): # CEC Table 4
|
|
try:
|
|
with sqlite3.connect("jepl-cec21.db") as con:
|
|
cur = con.cursor()
|
|
cur.execute('SELECT * FROM "Table4" WHERE "size" = ? ', (max,))
|
|
max_conductor_current = cur.fetchone()
|
|
max_current = max_conductor_current[conductor_current_index]
|
|
|
|
except sqlite3.OperationalError as e:
|
|
print(e)
|
|
|
|
current_lookup = current_for_lookup(current,max_current)
|
|
current = current_lookup[0]
|
|
num_parallel = current_lookup[1]
|
|
|
|
try:
|
|
with sqlite3.connect("jepl-cec21.db") as con:
|
|
cur = con.cursor()
|
|
if temp == 90:
|
|
cur.execute('SELECT size FROM "Table4" WHERE "90" > ? ', (current,))
|
|
|
|
elif temp ==75:
|
|
cur.execute('SELECT size FROM "Table4" WHERE "75" > ? ', (current,))
|
|
|
|
else:
|
|
cur.execute('SELECT size FROM "Table4" WHERE "60" > ? ', (current,))
|
|
conductor_size = str(cur.fetchone()[0])
|
|
|
|
#print(conductor_size)
|
|
|
|
except sqlite3.OperationalError as e:
|
|
print(e)
|
|
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.')
|
|
|
|
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:
|
|
conductor_current_index = 3
|
|
elif temp == 75:
|
|
conductor_current_index = 2
|
|
else:
|
|
conductor_current_index = 1
|
|
|
|
if (code == 'CEC') & (material == 'CU') & (raceway == False): # CEC Table 1
|
|
try:
|
|
with sqlite3.connect("jepl-cec21.db") as con:
|
|
cur = con.cursor()
|
|
cur.execute('SELECT * FROM "Table1" WHERE "size" = ? ', (conductor,))
|
|
max_conductor_current = cur.fetchone()
|
|
conductor_ampacity = max_conductor_current[conductor_current_index]
|
|
|
|
except sqlite3.OperationalError as e:
|
|
print(e)
|
|
|
|
elif (code == 'CEC') & (material == 'CU') & (raceway == True): # CEC Table 2
|
|
try:
|
|
with sqlite3.connect("jepl-cec21.db") as con:
|
|
cur = con.cursor()
|
|
cur.execute('SELECT * FROM "Table2" WHERE "size" = ? ', (conductor,))
|
|
max_conductor_current = cur.fetchone()
|
|
conductor_ampacity = max_conductor_current[conductor_current_index]
|
|
|
|
except sqlite3.OperationalError as e:
|
|
print(e)
|
|
|
|
elif (code =='CEC') & (material =='AL') & (raceway == False): # CEC Table 3
|
|
try:
|
|
with sqlite3.connect("jepl-cec21.db") as con:
|
|
cur = con.cursor()
|
|
cur.execute('SELECT * FROM "Table3" WHERE "size" = ? ', (conductor,))
|
|
max_conductor_current = cur.fetchone()
|
|
conductor_ampacity = max_conductor_current[conductor_current_index]
|
|
|
|
except sqlite3.OperationalError as e:
|
|
print(e)
|
|
|
|
elif (code =='CEC') & (material =='AL') & (raceway == True): # CEC Table 4
|
|
try:
|
|
with sqlite3.connect("jepl-cec21.db") as con:
|
|
cur = con.cursor()
|
|
cur.execute('SELECT * FROM "Table4" WHERE "size" = ? ', (conductor,))
|
|
max_conductor_current = cur.fetchone()
|
|
conductor_ampacity = max_conductor_current[conductor_current_index]
|
|
|
|
except sqlite3.OperationalError as e:
|
|
print(e)
|
|
|
|
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.')
|
|
|
|
return conductor_ampacity
|
|
|
|
def bonding_conductor(conductor_ampacity,bus=False,material='cu',code = 'CEC'):
|
|
|
|
'''
|
|
This function
|
|
|
|
'''
|
|
|
|
material = material.upper()
|
|
code = code.upper()
|
|
valid_code = ['CEC',
|
|
]
|
|
valid_material = ['CU',
|
|
'AL',
|
|
]
|
|
|
|
if (material == 'CU') & (bus == False):
|
|
db_index = 1
|
|
elif (material == 'AL') & (bus == False):
|
|
db_index = 3
|
|
elif (material == 'CU') & (bus == True):
|
|
db_index = 2
|
|
elif (material == 'AL') & (bus == True):
|
|
db_index = 4
|
|
else:
|
|
return ('The variables were\'t right, but I\'m a loss to why.')
|
|
|
|
if (code == 'CEC'):
|
|
try:
|
|
with sqlite3.connect("jepl-cec21.db") as con:
|
|
cur = con.cursor()
|
|
cur.execute('SELECT * FROM "Table16" WHERE "current" >= ? ', (conductor_ampacity,))
|
|
bond_result = cur.fetchone()
|
|
bond_size = bond_result[db_index]
|
|
|
|
except sqlite3.OperationalError as e:
|
|
print(e)
|
|
|
|
else:
|
|
return ('The variables were\'t right, but I\'m a loss to why.')
|
|
|
|
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
|