Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
*.pyc
*.ipynb_checkpoints
*.pdf
*untracked_bin

# Unit test / coverage reports
htmlcov/
Expand Down
286 changes: 279 additions & 7 deletions graphtik_calc_flow.ipynb
Original file line number Diff line number Diff line change
@@ -1,5 +1,21 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Prototyping the calculation flow to Determine Base Passing Wire Size\n",
"\n",
"This notebook contains two approaches to using the functions already written into pySolarCalc to determine the minimum wire size that will provide the required ampacity. No further calcuations (voltage drop or conduit sizing) are included yet.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Imports"
]
},
{
"cell_type": "code",
"execution_count": null,
Expand All @@ -13,6 +29,185 @@
"import nec_tables as nec"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Recreate spreadsheet approach that has user input wire size and checks if it is large enough\n",
"\n",
"This DAG re-creates the calculation stream from the spreadsheet that we agree results in the correct wire size. This DAG takes the wire size as an input, which is the most signifcant difference from the above DAG. \n",
"\n",
"Todo:\n",
"- **move DAG into osd and write tests**"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"check_wire = graphtik.compose('check_selected_wire_size',\n",
" graphtik.operation(name='ccc_derate', needs=['ccc_count', 'table_310_15_B_3_a'], provides=['ccc_derate'])(osd.lookup),\n",
" graphtik.operation(name='amb_temp_derate', needs=['amb_temp', 'wire_insulation_temp', 'table_ambient_temp'], provides=['amb_derate'])(osd.get_ambient_temp_derate),\n",
" graphtik.operation(name='get_cond_use_derate', needs=['ccc_derate', 'amb_derate'], provides=['cond_use_derate'])(operator.mul),\n",
" graphtik.operation(name='get_parallel_current', needs=['current', 'parallel_conductors'], provides=['parallel_current'])(operator.truediv),\n",
" graphtik.operation(name='get_terminal_check_current', needs=['parallel_current', 'ocpd_derate'], provides=['terminal_check_current'])(operator.truediv),\n",
" graphtik.operation(name='get_cond_use_check_current', needs=['parallel_current', 'cond_use_derate'], provides=['cond_use_check_current'])(operator.truediv),\n",
" graphtik.operation(name='get_design_current', needs=['terminal_check_current', 'cond_use_check_current'], provides=['design_current'])(max),\n",
" graphtik.operation(name='check_terminals', needs=['wire_size', 'wire_material', 'terminal_temp_rating', 'design_current', 'ampacity_table'], provides=['terminals_passing'])(osd.get_wire_ampacity),\n",
" graphtik.operation(name='get_ocpd', needs=['current', 'voltage_type', 'ocpd_derate'], provides=['ocpd'])(osd.get_ocpd),\n",
" graphtik.operation(name='get_cable_sizing_ocpd', needs=['ocpd'], provides=['cable_sizing_ocpd'])(osd.get_cable_sizing_ocpd),\n",
" graphtik.operation(name='check_ocpd', needs=['wire_size', 'wire_material', 'wire_insulation_temp', 'ocpd_check_current', 'ampacity_table', 'cond_use_derate'], provides=['derated_cable_ampacity_per_cond'])(osd.get_wire_ampacity),\n",
" graphtik.operation(name='get_derated_cable_ampacity', needs=['derated_cable_ampacity_per_cond', 'parallel_conductors'], provides=['derated_cable_ampacity'])(operator.mul),\n",
" graphtik.operation(name='check_derated_cable_ampacity', needs=['derated_cable_ampacity', 'cable_sizing_ocpd'], provides=['derated_cable_ampacity_exceeds_ocpd'])(operator.gt),\n",
" graphtik.operation(name='check_all', needs=['terminals_passing', 'derated_cable_ampacity_exceeds_ocpd'], provides=['cable_passing'])(operator.and_)\n",
" )"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"amp_table = '310_B_16'\n",
"base_table_temp = nec.ampacity_tables[amp_table]['base_temp']\n",
"\n",
"check_wire_results = check_wire(**{'ccc_count': 3, 'table_310_15_B_3_a': nec.ccc_count_derate,\n",
" 'amb_temp': 34, 'wire_insulation_temp': 90, 'table_ambient_temp': base_table_temp,\n",
" 'current': 230, 'parallel_conductors': 2,\n",
" 'ocpd_derate': 0.8,\n",
" 'wire_size':'3/0', 'ocpd_check_current':None, 'ampacity_table': amp_table,\n",
" 'wire_material': 'Al', 'terminal_temp_rating': 75,\n",
" 'voltage_type':'AC'})"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"for key, val in check_wire_results.items():\n",
" print('{}: {}'.format(key, val))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"check_wire.plot()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Put calc in loop to get base wire size\n",
"\n",
"This section of the notebook puts the above calculation inside a loop to find the base wire size without manually iterating. For this example, this DAG gives the same results as the excel spreadsheet.\n",
"- A starting wire size is looked up using the un-modified current, no derates, and the wire insulation temperature rating. For this example this approach returns a wire size that is below the base wire size, but only by a size or two. The intent is to limit iterations without missing the base size. Testing of many scenarios is necessary to be sure this doesn't ever return a wire size larger than the base size.\n",
"- The calculation is then re-run with larger wires until the wire passes the terminal/cont. duty and the cond of use vs ocpd checks.\n",
"- This could be improved by seperating it into two DAGs because the wire size is not an input until the second half of the calculation.\n",
"\n",
"I think this approach is viable as the basis for further development as opposed to working out a calculation stream that determines the base wire size directly without the loop. The loop only needs to be run once when initializing a table and then the base wire size can be stored. Then the same DAG can be re-run whenver the user changes the cable size. (Usually for VD. Should users be allowed to input a wire size below the min for ampacity? The flexability might be nice and people could use that to find the pass/fail breakpoint themselves, which is reassuring, but this also allows users to accidently leave a cable size that is not code compliant in their table. Maybe allow it and warn strongly?)."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"inputs = {'ccc_count': 3, 'table_310_15_B_3_a': nec.ccc_count_derate,\n",
" 'amb_temp': 34, 'wire_insulation_temp': 90, 'table_ambient_temp': base_table_temp,\n",
" 'current': 230, 'parallel_conductors': 2,\n",
" 'ocpd_derate': 0.8,\n",
" 'wire_size':'3/0', 'ocpd_check_current':None, 'ampacity_table': amp_table,\n",
" 'wire_material': 'Al', 'terminal_temp_rating': 75,\n",
" 'voltage_type':'AC'}"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"begin_wire_size = osd.lookup(inputs['current'], nec.cable_ampacity_310_16[inputs['wire_material']][inputs['wire_insulation_temp']], keys=False)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"inputs['wire_size'] = '12'#begin_wire_size"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"inputs['wire_size']"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"wire_sizes = list(nec.cable_ampacity_310_16[inputs['wire_material']][inputs['wire_insulation_temp']].keys())"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"begin_wire_size_ix = wire_sizes.index(begin_wire_size)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"for next_wire_size in wire_sizes[begin_wire_size_ix:]:\n",
" inputs['wire_size'] = next_wire_size\n",
" check_wire_results = check_wire(**inputs)\n",
" if check_wire_results['cable_passing']:\n",
" base_cable_size = next_wire_size\n",
" break"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"base_cable_size"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Functions\n",
"\n",
"None of these are currently used in the DAG. All functions used in the DAG are either in osd or are built ins."
]
},
{
"cell_type": "code",
"execution_count": null,
Expand All @@ -25,6 +220,17 @@
" return (wire_size, nec.cable_ampacity_310_16[mat][temp][wire_size])"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def check_wire_ampacity(current, size, mat, temp, derates=1.0):\n",
" ampacity = nec.cable_ampacity_310_16[mat][temp][size]\n",
" return (ampacity * derates) >= current"
]
},
{
"cell_type": "code",
"execution_count": null,
Expand All @@ -41,6 +247,68 @@
" return cond_b"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def check_wire_against_ocpd(cond, ocpd):\n",
" return cond[1] >= ocpd"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Check applying derates to ocpd size instead of cable\n",
"\n",
"Is there a direct way to obtain the solution without inputting a wire size and then manually or automatically bumping wire sizes to find the solution.\n",
"Would need to apply conditions of use to the next lowest ocpd size, but that might cause incorrect breakpoints in selected sizes?"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"uprated_ocpd = 90 / 0.786\n",
"uprated_ocpd"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"get_wire_size(uprated_ocpd, 'Cu', 90)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"nec.cable_ampacity_310_16['Cu'][90]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Calc flow matching the flow chart - OUT OF DATE\n",
"\n",
"This doesn't quite work because the ocpd check at the end requires bumping the conductor size, but the conductor size is not an input to the calculation.\n",
"Maybe, I didn't interpet the flow chart correctly?\n",
"\n",
"The names used here (and in the next DAG) need refinement before adding this to pySolarCalc.\n",
"\n",
"**Note: The values in the needs list are passed sequentially to the function for each operation.**"
]
},
{
"cell_type": "code",
"execution_count": null,
Expand All @@ -53,10 +321,13 @@
" graphtik.operation(name='cond_of_use_derates', needs=['ccc_derate', 'amb_derate'], provides=['total_derate'])(operator.mul),\n",
" graphtik.operation(name='current_per_conductor', needs=['current', 'parallel_conductors'], provides=['parallel_current'])(operator.mul),\n",
" graphtik.operation(name='current_terminal_check', needs=['parallel_current', 'ocpd_derate'], provides=['terminal_check_current'])(operator.truediv),\n",
" graphtik.operation(name='cond_use_current', needs=['total_derate', 'parallel_current'], provides=['cond_use_check_current'])(operator.mul),\n",
" graphtik.operation(name='cond_use_current', needs=['parallel_current', 'total_derate'], provides=['cond_use_check_current'])(operator.truediv),\n",
" graphtik.operation(name='wire_size_terminal', needs=['terminal_check_current', 'wire_material', 'terminal_temp_rating'], provides=['terminal_min_wire'])(get_wire_size),\n",
" graphtik.operation(name='wire_size_cond_use', needs=['cond_use_check_current', 'wire_material', 'wire_insulation_temp'], provides=['cond_use_min_wire'])(get_wire_size),\n",
" graphtik.operation(name='compare_wire_sizes', needs=['terminal_min_wire', 'cond_use_min_wire'], provides=['min_wire_size'])(compare_wire_sizes)\n",
" graphtik.operation(name='compare_wire_sizes', needs=['terminal_min_wire', 'cond_use_min_wire'], provides=['min_wire_size'])(compare_wire_sizes),\n",
" graphtik.operation(name='get_ocpd', needs=['current', 'voltage_type', 'ocpd_derate'], provides=['ocpd'])(osd.get_ocpd),\n",
" graphtik.operation(name='cable_sizing_ocpd', needs=['ocpd'], provides=['cable_sizing_ocpd'])(osd.get_cable_sizing_ocpd),\n",
" graphtik.operation(name='ocpd_check', needs=['min_wire_size', 'cable_sizing_ocpd'], provides=['wire_works'])(check_wire_against_ocpd)\n",
" )"
]
},
Expand All @@ -66,11 +337,12 @@
"metadata": {},
"outputs": [],
"source": [
"results = graph(**{'ccc_count': 4, 'table': nec.ccc_count_derate,\n",
" 'amb_temp': 32, 'wire_insulation_temp': 90,\n",
" 'current': 60, 'parallel_conductors': 1,\n",
" 'ocpd_derate': 0.8,\n",
" 'wire_material': 'Cu', 'terminal_temp_rating': 75})"
"results = graph(**{'ccc_count': 3, 'table': nec.ccc_count_derate,\n",
" 'amb_temp': 30, 'wire_insulation_temp': 90,\n",
" 'current': 60, 'parallel_conductors': 1,\n",
" 'ocpd_derate': 0.8,\n",
" 'wire_material': 'Cu', 'terminal_temp_rating': 75,\n",
" 'voltage_type':'DC'})"
]
},
{
Expand Down
23 changes: 19 additions & 4 deletions nec_tables.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Cable ampacities from NEC Table 310.16
cable_ampacity_310_16 = {'Cu': {60: {'12': 20,
desc_310_16 = "Table 310.15(B)(16) (formerly 310.16) Allowable Ampacities of \
Insulated Conductors Rated Up to and Including 2000 Volts, 60C Through 90C \
(140F Through 194F), Not More Than Three Current-Carrying Conductors in \
Raceway, Cable, or Earth (Directly Buried), Based on Ambient Temperature \
of 30C (86F)."

cable_ampacity_310_16 = {'desc': desc_310_16,
'base_temp': 30,
'Cu': {60: {'12': 20,
'10': 30,
'8': 40,
'6': 55,
Expand Down Expand Up @@ -162,8 +169,13 @@
'1750': 615,
'2000': 630}}}

# Cable ampacities from NEC Table 310.17
cable_ampacity_310_17 = {'Cu': {60: {'12': 30,
desc_310_17 = "Table 310.15(B)(17) (formerly Table 310.17) Allowable \
Ampacities of Single-Insulated Conductors Rated Up to and Including 2000 Volts\
in Free Air, Based on Ambient Temperature of 30C (86F)."

cable_ampacity_310_17 = {'desc': desc_310_17,
'base_temp': 30,
'Cu': {60: {'12': 30,
'10': 40,
'8': 60,
'6': 80,
Expand Down Expand Up @@ -326,6 +338,9 @@
'1750': 1185,
'2000': 1295}}}

ampacity_tables = {'310_B_16': cable_ampacity_310_16,
'310_B_17': cable_ampacity_310_17}

# Standard OCPD sizes from NEC Table 240.6(A)
ocpd_sizes = [15, 20, 25, 30, 35,
40, 45, 50, 60, 70,
Expand Down
Loading