diff --git a/.gitignore b/.gitignore index 0a818c5..b8a936a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /tests/ test* -__pycache__ \ No newline at end of file +__pycache__ +*.db diff --git a/example_notebook.ipynb b/example_notebook.ipynb new file mode 100644 index 0000000..259ee11 --- /dev/null +++ b/example_notebook.ipynb @@ -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 +} diff --git a/jepl/Tables/CEC-Tables/CEC21-table1.csv b/jepl/Tables/CEC-Tables/CEC21-table1.csv new file mode 100644 index 0000000..b2ccf0b --- /dev/null +++ b/jepl/Tables/CEC-Tables/CEC21-table1.csv @@ -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 diff --git a/jepl/Tables/CEC-Tables/CEC21-table2.csv b/jepl/Tables/CEC-Tables/CEC21-table2.csv new file mode 100644 index 0000000..e1bdf81 --- /dev/null +++ b/jepl/Tables/CEC-Tables/CEC21-table2.csv @@ -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 diff --git a/jepl/Tables/CEC-Tables/CEC21-table3.csv b/jepl/Tables/CEC-Tables/CEC21-table3.csv new file mode 100644 index 0000000..70c51a1 --- /dev/null +++ b/jepl/Tables/CEC-Tables/CEC21-table3.csv @@ -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 diff --git a/jepl/Tables/CEC-Tables/CEC21-table4.csv b/jepl/Tables/CEC-Tables/CEC21-table4.csv new file mode 100644 index 0000000..c241be7 --- /dev/null +++ b/jepl/Tables/CEC-Tables/CEC21-table4.csv @@ -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 diff --git a/jepl/Tables/Manufacturer/SW-Spec-23055.csv b/jepl/Tables/Manufacturer/SW-Spec-23055.csv new file mode 100644 index 0000000..11a4118 --- /dev/null +++ b/jepl/Tables/Manufacturer/SW-Spec-23055.csv @@ -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 diff --git a/jepl/Tables/Manufacturer/source/SOUTHWIRE_SPEC25055.pdf b/jepl/Tables/Manufacturer/source/SOUTHWIRE_SPEC25055.pdf new file mode 100644 index 0000000..aabeb93 Binary files /dev/null and b/jepl/Tables/Manufacturer/source/SOUTHWIRE_SPEC25055.pdf differ diff --git a/jepl/init.ipynb b/jepl/init.ipynb new file mode 100644 index 0000000..c3f6c51 --- /dev/null +++ b/jepl/init.ipynb @@ -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 +} diff --git a/jepl/jepl.py b/jepl/jepl.py index 62c0aed..2039d7e 100644 --- a/jepl/jepl.py +++ b/jepl/jepl.py @@ -4,76 +4,11 @@ by: Jeff MacKinnon email: jeff@jmkengineering.com ''' -import pandas as pd -import numpy as np -import math +import sys +from .jeplpv import * +from .jepl_circuits import * def Test(): 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] - - - - diff --git a/jepl/jepl_circuits.py b/jepl/jepl_circuits.py new file mode 100644 index 0000000..06a555b --- /dev/null +++ b/jepl/jepl_circuits.py @@ -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. + +''' +''' + + + + diff --git a/jepl/jeplinit.py b/jepl/jeplinit.py new file mode 100644 index 0000000..4f02a02 --- /dev/null +++ b/jepl/jeplinit.py @@ -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) + diff --git a/jepl/jeplpv.py b/jepl/jeplpv.py new file mode 100644 index 0000000..c5e30a3 --- /dev/null +++ b/jepl/jeplpv.py @@ -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 \ No newline at end of file diff --git a/jepl/tables.py b/jepl/tables.py index 5f5827a..92a27cc 100644 --- a/jepl/tables.py +++ b/jepl/tables.py @@ -1,4 +1,18 @@ 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([ ['14',25,30,35], @@ -131,4 +145,24 @@ CEC21_table4 = pd.DataFrame([ CEC_table1 = CEC21_table1 CEC_table2 = CEC21_table2 CEC_table3 = CEC21_table3 -CEC_table4 = CEC21_table4 \ No newline at end of file +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'] + )