Refactoring, adding databases and initialization

This commit is contained in:
Jeff MacKinnon 2024-12-13 15:47:10 -04:00
parent 3a25ad98a6
commit 9635004956
14 changed files with 567 additions and 70 deletions

3
.gitignore vendored
View file

@ -1,3 +1,4 @@
/tests/ /tests/
test* test*
__pycache__ __pycache__
*.db

56
example_notebook.ipynb Normal file
View file

@ -0,0 +1,56 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import jepl.jepl as jmk # import the module. If this file is located further up the file tree it may look like \"resources.JEPL.jepl.jepl\"\n",
"%run jepl/jeplinit.py jepl/ # This is needed to initilize the databases. Again if you are running this from a different location the run command will look like %run jeplinit.py resources/JEPL/jepl/ "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now that the notebook is initialized we can run various functions. Currently we have two modules:\n",
"\n",
"- circuits\n",
"- pv\n",
"\n",
"The circuits module has functions for:\n",
"\n",
"- voltage drop\n",
"- conductor ampacity\n",
"- etc\n",
"\n",
"The pv module has functions for:\n",
"\n",
"- string voltage\n",
"- panels per string\n"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.1"
}
},
"nbformat": 4,
"nbformat_minor": 2
}

View file

@ -0,0 +1,28 @@
size,60,75,90
14,25,30,35
12,30,35,40
10,40,50,55
8,60,70,80
6,80,95,105
4,105,125,140
3,120,145,165
2,140,170,190
1,165,195,220
0,195,230,260
00,220,265,300
000,260,310,350
0000,300,360,405
250,340,405,455
300,370,445,500
350,425,505,570
400,455,545,615
500,520,620,700
600,580,690,780
700,630,755,850
750,655,785,885
800,680,815,920
1000,785,870,980
1250,890,1065,1200
1500,985,1175,1325
1750,1070,1280,1445
2000,1160,1385,1560
1 size 60 75 90
2 14 25 30 35
3 12 30 35 40
4 10 40 50 55
5 8 60 70 80
6 6 80 95 105
7 4 105 125 140
8 3 120 145 165
9 2 140 170 190
10 1 165 195 220
11 0 195 230 260
12 00 220 265 300
13 000 260 310 350
14 0000 300 360 405
15 250 340 405 455
16 300 370 445 500
17 350 425 505 570
18 400 455 545 615
19 500 520 620 700
20 600 580 690 780
21 700 630 755 850
22 750 655 785 885
23 800 680 815 920
24 1000 785 870 980
25 1250 890 1065 1200
26 1500 985 1175 1325
27 1750 1070 1280 1445
28 2000 1160 1385 1560

View file

@ -0,0 +1,29 @@
size,60,75,90
14,15,20,25
12,20,25,30
10,30,35,40
8,40,50,55
6,40,50,55
4,70,85,95
3,85,100,115
2,95,115,130
1,110,130,145
0,125,150,170
00,145,175,195
000,165,200,225
0000,195,230,260
250,215,255,290
300,240,285,320
350,260,310,350
400,280,335,380
500,320,380,430
600,350,420,475
700,385,460,520
750,400,475,535
800,410,490,555
900,435,520,585
1000,455,545,615
1250,495,590,615
1500,525,625,705
1750,545,650,735
2000,555,665,750
1 size 60 75 90
2 14 15 20 25
3 12 20 25 30
4 10 30 35 40
5 8 40 50 55
6 6 40 50 55
7 4 70 85 95
8 3 85 100 115
9 2 95 115 130
10 1 110 130 145
11 0 125 150 170
12 00 145 175 195
13 000 165 200 225
14 0000 195 230 260
15 250 215 255 290
16 300 240 285 320
17 350 260 310 350
18 400 280 335 380
19 500 320 380 430
20 600 350 420 475
21 700 385 460 520
22 750 400 475 535
23 800 410 490 555
24 900 435 520 585
25 1000 455 545 615
26 1250 495 590 615
27 1500 525 625 705
28 1750 545 650 735
29 2000 555 665 750

View file

@ -0,0 +1,27 @@
size,60,75,90
12,25,30,35
10,35,40,45
8,45,55,60
4,65,75,85
3,95,115,130
2,115,135,150
1,1340,155,175
0,150,180,205
00,175,210,235
000,200,240,270
0000,235,280,315
250,265,315,355
300,295,350,395
350,330,395,445
400,355,425,480
500,405,485,545
600,455,545,615
700,500,595,670
750,520,620,700
800,540,645,725
900,585,700,790
1000,630,750,845
1250,715,855,965
1500,795,950,1070
1750,880,1050,1185
2000,965,1150,1295
1 size 60 75 90
2 12 25 30 35
3 10 35 40 45
4 8 45 55 60
5 4 65 75 85
6 3 95 115 130
7 2 115 135 150
8 1 1340 155 175
9 0 150 180 205
10 00 175 210 235
11 000 200 240 270
12 0000 235 280 315
13 250 265 315 355
14 300 295 350 395
15 350 330 395 445
16 400 355 425 480
17 500 405 485 545
18 600 455 545 615
19 700 500 595 670
20 750 520 620 700
21 800 540 645 725
22 900 585 700 790
23 1000 630 750 845
24 1250 715 855 965
25 1500 795 950 1070
26 1750 880 1050 1185
27 2000 965 1150 1295

View file

@ -0,0 +1,28 @@
size,60,75,90
12,15,20,25
10,25,30,35
8,35,40,45
6,40,50,55
4,55,65,75
3,65,75,85
2,75,90,100
1,85,100,115
0,100,120,165
00,115,165,150
000,160,155,175
0000,150,180,205
250,170,205,230
300,195,230,260
350,210,250,280
400,225,270,305
500,260,310,350
600,285,340,385
700,315,375,425
750,320,385,435
800,330,395,445
900,355,425,480
1000,375,445,500
1250,405,485,545
1500,435,520,585
1750,455,545,615
2000,470,560,630
1 size 60 75 90
2 12 15 20 25
3 10 25 30 35
4 8 35 40 45
5 6 40 50 55
6 4 55 65 75
7 3 65 75 85
8 2 75 90 100
9 1 85 100 115
10 0 100 120 165
11 00 115 165 150
12 000 160 155 175
13 0000 150 180 205
14 250 170 205 230
15 300 195 230 260
16 350 210 250 280
17 400 225 270 305
18 500 260 310 350
19 600 285 340 385
20 700 315 375 425
21 750 320 385 435
22 800 330 395 445
23 900 355 425 480
24 1000 375 445 500
25 1250 405 485 545
26 1500 435 520 585
27 1750 455 545 615
28 2000 470 560 630

View file

@ -0,0 +1,13 @@
Stock Number,Conductor Size,Stands,Insul Thickness,Ground,Inner Jacket Thickness,Diameter over Armour,Jacket Thickness,Approx Outer Diameter,Approx Weight,Min Bending Radius,Max Pull Tension,DC Resistance,AC Resistance,Inductive Reactance,Ampacity
584846,6,7,1.52,1x6,1.65,27.66,1.4,30.45,798,210.82,2100,2.21,2.66,0.1673,55
674938,4,7,1.52,1x6,2.16,31.14,1.4,33.93,1018,236.22,3342,1.39,1.67,0.1575,75
584848,2,6,1.52,1x6,2.16,34.16,1.4,36.96,1217,256.54,5313,0.88,1.05,0.1476,100
584852,1,8,2.03,1x4,2.16,38.18,1.65,41.53,1534,289.56,6702,0.69,0.83,0.1509,115
584873,1/0,18,2.03,1x4,2.16,40.21,1.65,43.56,1702,304.8,8455,0.55,0.66,0.1444,135
584876,2/0,12,2.03,1x4,2.16,42.39,1.65,45.75,1926,320.04,10658,0.44,0.52,0.1411,150
584883,3/0,16,2.03,1x4,2.16,44.98,1.65,48.34,2171,337.82,13439,0.34,0.41,0.1378,175
584891,4/0,18,2.03,1x4,2.16,48.08,1.65,51.43,2554,358.14,16946,0.28,0.33,0.1345,205
583957,250,35,2.29,1x2,2.92,53.31,1.65,56.67,3131,396.24,20025,0.23,0.28,0.1345,230
615167,350,35,2.29,1x2,1.27,58.57,1.91,62.38,3857,434.34,28035,0.16,0.2,0.0951,280
583961,500,35,2.29,1x1,2.92,65.15,2.03,69.37,4887,485.14,40050,0.11,0.14,0.128,350
668789,750,53,2.29,1x1/0,2.92,74.6,2.03,78.82,6426,551.18,60075,0.08,0.1,0.1247,435
1 Stock Number Conductor Size Stands Insul Thickness Ground Inner Jacket Thickness Diameter over Armour Jacket Thickness Approx Outer Diameter Approx Weight Min Bending Radius Max Pull Tension DC Resistance AC Resistance Inductive Reactance Ampacity
2 584846 6 7 1.52 1x6 1.65 27.66 1.4 30.45 798 210.82 2100 2.21 2.66 0.1673 55
3 674938 4 7 1.52 1x6 2.16 31.14 1.4 33.93 1018 236.22 3342 1.39 1.67 0.1575 75
4 584848 2 6 1.52 1x6 2.16 34.16 1.4 36.96 1217 256.54 5313 0.88 1.05 0.1476 100
5 584852 1 8 2.03 1x4 2.16 38.18 1.65 41.53 1534 289.56 6702 0.69 0.83 0.1509 115
6 584873 1/0 18 2.03 1x4 2.16 40.21 1.65 43.56 1702 304.8 8455 0.55 0.66 0.1444 135
7 584876 2/0 12 2.03 1x4 2.16 42.39 1.65 45.75 1926 320.04 10658 0.44 0.52 0.1411 150
8 584883 3/0 16 2.03 1x4 2.16 44.98 1.65 48.34 2171 337.82 13439 0.34 0.41 0.1378 175
9 584891 4/0 18 2.03 1x4 2.16 48.08 1.65 51.43 2554 358.14 16946 0.28 0.33 0.1345 205
10 583957 250 35 2.29 1x2 2.92 53.31 1.65 56.67 3131 396.24 20025 0.23 0.28 0.1345 230
11 615167 350 35 2.29 1x2 1.27 58.57 1.91 62.38 3857 434.34 28035 0.16 0.2 0.0951 280
12 583961 500 35 2.29 1x1 2.92 65.15 2.03 69.37 4887 485.14 40050 0.11 0.14 0.128 350
13 668789 750 53 2.29 1x1/0 2.92 74.6 2.03 78.82 6426 551.18 60075 0.08 0.1 0.1247 435

86
jepl/init.ipynb Normal file
View file

@ -0,0 +1,86 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import pandas as pd\n",
"\n",
"import sqlite3\n",
"\n",
"def insert(database,table,data):\n",
" '''\n",
" This function creates a database from the current \n",
" source data to be used in the modules.\n",
"\n",
" Running this function will replace any data that is already in the database.\n",
" '''\n",
"\n",
" conn = sqlite3.connect(database) \n",
" data.to_sql(table, conn, if_exists='replace', index=False) \n",
" conn.close()\n",
"\n",
"def create_database(name,tables):\n",
" '''\n",
" This function loops over the source data lists to create tables in the database.\n",
" '''\n",
"\n",
" for i in range(len(tables)):\n",
" db = tables[i][0]\n",
" table = tables[i][1]\n",
" source = tables[i][2]\n",
" data = pd.read_csv(source)\n",
"\n",
" insert(db,table,data) # Call the database insert function to add the data\n",
"\n",
"\n",
"'''\n",
" This is all the tables and their sources that will be added to the databases.\n",
"\n",
" \n",
"'''\n",
"\n",
"db_name = 'jepl-cec21.db'\n",
"CEC21_database = [\n",
" [db_name,'Table1','Tables/CEC-Tables/CEC21-table1.csv'],\n",
" [db_name,'Table2','Tables/CEC-Tables/CEC21-table2.csv'],\n",
" [db_name,'Table3','Tables/CEC-Tables/CEC21-table2.csv'],\n",
" [db_name,'Table4','Tables/CEC-Tables/CEC21-table2.csv'],\n",
" ]\n",
"\n",
"create_database(db_name,CEC21_database)\n",
"\n",
"\n",
"db_name = 'cable.db'\n",
"cable_database = [\n",
" [db_name,'SW-Spec 25055','Tables/Manufacturer/SW-Spec-23055.csv'],\n",
" ]\n",
"create_database(db_name,cable_database)\n",
"\n"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.1"
}
},
"nbformat": 4,
"nbformat_minor": 2
}

View file

@ -4,76 +4,11 @@ by: Jeff MacKinnon
email: jeff@jmkengineering.com email: jeff@jmkengineering.com
''' '''
import pandas as pd import sys
import numpy as np from .jeplpv import *
import math from .jepl_circuits import *
def Test(): def Test():
print( "JMK Python Module works! !") print( "JMK Python Module works! !")
def voltage_drop(nominal_voltage, current, material ='cu', code = 'CEC'):
print(test)
from .tables import *
def conductor_size(current, temp = 75, material = 'cu', code = 'CEC', raceway = True, ambient = 30, max = 500):
'''
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',
]
#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'.")
# select the correct code table
if (code == 'CEC') & (material == 'CU') & (raceway == False):
code_table = CEC_table1
elif (code == 'CEC') & (material == 'CU') & (raceway == True):
code_table = CEC_table2
elif (code =='CEC') & (material =='AL') & (raceway == False):
code_table = CEC_table3
elif (code =='CEC') & (material =='AL') & (raceway == True):
code_table = CEC_table4
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.')
temp = str(temp)
max_current_loc = code_table.loc[code_table['size'] == max][str(temp)]
max_current = max_current_loc.iloc[0]
num_parallel = math.ceil(current / max_current)
con_current = current / num_parallel
size = code_table[code_table[temp].ge(con_current)]['size'].iloc[0]
return [size,num_parallel]

144
jepl/jepl_circuits.py Normal file
View file

@ -0,0 +1,144 @@
'''
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
def vd(current,length,resistance):
vd=2*current*length*resistance/1000
return vd
def percentvd(vd,nominal):
percent = (vd/nominal)*100
return percent
def voltage_drop(nominal_voltage, current, conductor_size, material ='cu', 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('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("cable.db") as con:
cur = con.cursor()
cur.execute('SELECT "AC Resistance" FROM "SW-Spec 25055" WHERE "Conductor Size"=?', (conductor_size,))
resistance = cur.fetchone()[0]
except sqlite3.OperationalError as e:
print(e)
voltage = vd(nominal_voltage,current,resistance)
percent = percentvd(voltage,nominal_voltage)
return [voltage, percent]
'''
'''
# These functions need to be re-written with the databases.
'''
'''
def conductor_size(current, temp = 75, material = 'cu', code = 'CEC', raceway = True, ambient = 30, max = 500):
'''
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',
]
#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'.")
# select the correct code table
if (code == 'CEC') & (material == 'CU') & (raceway == False):
code_table = CEC_table1
elif (code == 'CEC') & (material == 'CU') & (raceway == True):
code_table = CEC_table2
elif (code =='CEC') & (material =='AL') & (raceway == False):
code_table = CEC_table3
elif (code =='CEC') & (material =='AL') & (raceway == True):
code_table = CEC_table4
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.')
temp = str(temp)
max_current_loc = code_table.loc[code_table['size'] == max][str(temp)]
max_current = max_current_loc.iloc[0]
num_parallel = math.ceil(current / max_current)
con_current = current / num_parallel
size = code_table[code_table[temp].ge(con_current)]['size'].iloc[0]
return [size,num_parallel]
'''
'''
# ^
# These functions need to be re-written with the databases.
'''
'''

66
jepl/jeplinit.py Normal file
View file

@ -0,0 +1,66 @@
import pandas as pd
import sys
import sqlite3
'''
When this init file is used for projects, the location should be passed as an argument, similar to:
%run jeplinit.py resources/JEPL/jepl/
If there is no argument, we assume that it is part of development and we will use the root folder.
'''
location = ''
if sys.argv[1]:
location = sys.argv[1] # This needs to be passed if its not the same as the location of jepl.py
'''
The functions used in the initialization
'''
def insert(database,table,data):
'''
This function creates a database from the current
source data to be used in the modules.
Running this function will replace any data that is already in the database.
'''
conn = sqlite3.connect(database)
data.to_sql(table, conn, if_exists='replace', index=False)
conn.close()
def create_database(name,tables):
'''
This function loops over the source data lists to create tables in the database.
'''
for i in range(len(tables)):
db = tables[i][0]
table = tables[i][1]
source = tables[i][2]
data = pd.read_csv(source)
insert(db,table,data) # Call the database insert function to add the data
'''
This is all the tables and their sources that will be added to the databases.
'''
CEC21_database = [
['jepl-cec21.db','Table1',location+'Tables/CEC-Tables/CEC21-table1.csv'],
['jepl-cec21.db','Table2',location+'Tables/CEC-Tables/CEC21-table2.csv'],
['jepl-cec21.db','Table3',location+'Tables/CEC-Tables/CEC21-table2.csv'],
['jepl-cec21.db','Table4',location+'Tables/CEC-Tables/CEC21-table2.csv'],
]
create_database('jepl-cec21.db',CEC21_database)
cable_database = [
['cable.db','SW-Spec 25055',location+'Tables/Manufacturer/SW-Spec-23055.csv'],
]
create_database('cable.db',cable_database)

50
jepl/jeplpv.py Normal file
View file

@ -0,0 +1,50 @@
'''
JMK Engineering Inc. Python Library for design and such.
by: Jeff MacKinnon
email: jeff@jmkengineering.com
PV Design functions
'''
def temp_adj_Voc(Voc,temp,beta=-0.5,STCtemp = 25):
'''
The Voc of a panel will increase as the temperature decreases.
The datasheet Voc is typically at STC, or 25C. As the temperature decreases this Voc needs to be adjusted.
This adjustment is linear based on the beta value with the units %/C.
This beta value will be shown as a negative indicating that as the temperature increases, then the Voc will decrease.
This Voc decrease results in derating of power during temperature above 25C.
Voc = The STC panel open circuit voltage
temp = The min or max temperature for the PV plant.
beta = The panel temperature coefficient. default -0.5%/C, typical values will range from -0.2 to -0.45.
The adjusted Voc is Voc minus the voltage shift. When the outside temperature is above the STC value then the Vocadj will be lower.
'''
tempadj = STCtemp - temp #-
percent_shift = tempadj * beta/100 #+
volt_shift = Voc * percent_shift
Vocadj = Voc - volt_shift # The voltage adjust is the
return Vocadj
def panels_per_string(Vmax,Voc,beta=-0.5,temp = 25,STCtemp = 25):
'''
This function calculates the maximum number of panels that a string can have.
It will automatically calculate the temperature shifted number when beta and temp are given.
'''
Vocadj = temp_adj_Voc(Voc, temp,beta, STCtemp = 25)
panels_per_string_max = Vmax // Vocadj
return panels_per_string_max

View file

@ -1,4 +1,18 @@
import pandas as pd import pandas as pd
import sqlite3
'''
The purpose of this file is to create the database(s) needed for the project.
There are two databases that are created, the first is public information and the other is copyrighted.
If you don't own the code version listed you can't use this portion of the library.
'''
CEC21_table1 = pd.DataFrame([ CEC21_table1 = pd.DataFrame([
['14',25,30,35], ['14',25,30,35],
@ -131,4 +145,24 @@ CEC21_table4 = pd.DataFrame([
CEC_table1 = CEC21_table1 CEC_table1 = CEC21_table1
CEC_table2 = CEC21_table2 CEC_table2 = CEC21_table2
CEC_table3 = CEC21_table3 CEC_table3 = CEC21_table3
CEC_table4 = CEC21_table4 CEC_table4 = CEC21_table4
# 3C TECK90 1000V Aluminum power cable
# Southwire Spec 25055 Revision 1.000.010
al_3C_cable_table = pd.DataFrame([
['6', 2.66],
['4',1.67],
['2',1.05],
['1',0.83],
['0',0.66],
['00',0.52],
['000',0.41],
['0000',0.33],
['250',0.28],
['350',0.2],
['500',0.14],
['750',0.1]
],
columns = ['size','AC Resistance']
)