diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000..d0c3cbf --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = source +BUILDDIR = build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/make.bat b/docs/make.bat new file mode 100644 index 0000000..dc1312a --- /dev/null +++ b/docs/make.bat @@ -0,0 +1,35 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=source +set BUILDDIR=build + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.https://www.sphinx-doc.org/ + exit /b 1 +) + +if "%1" == "" goto help + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% + +:end +popd diff --git a/docs/source/conf.py b/docs/source/conf.py new file mode 100644 index 0000000..872241a --- /dev/null +++ b/docs/source/conf.py @@ -0,0 +1,27 @@ +# Configuration file for the Sphinx documentation builder. +# +# For the full list of built-in configuration values, see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +# -- Project information ----------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information + +project = 'JMK Engineering Python Library' +copyright = '2025, Jeff MacKinnon' +author = 'Jeff MacKinnon' + +# -- General configuration --------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration + +extensions = [] + +templates_path = ['_templates'] +exclude_patterns = [] + + + +# -- Options for HTML output ------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output + +html_theme = 'alabaster' +html_static_path = ['_static'] diff --git a/docs/source/general/index.rst b/docs/source/general/index.rst new file mode 100644 index 0000000..5447e2b --- /dev/null +++ b/docs/source/general/index.rst @@ -0,0 +1,9 @@ +JEPL General Functions +================================ + + +.. toctree:: + :maxdepth: 2 + :caption: Contents: + + va diff --git a/docs/source/general/va.rst b/docs/source/general/va.rst new file mode 100644 index 0000000..016ebf3 --- /dev/null +++ b/docs/source/general/va.rst @@ -0,0 +1,17 @@ +va - Volt-Amps +================ + + +.. function:: va(voltage, current, phases=3) + + :module: JEPL.general + :param voltage: The voltage of the circuit + :type voltage: float + :param current: The current of the circuit + :type current: float + :returns: volt-Amps in VA + :rtype: float + + Describe the function + + diff --git a/docs/source/index.rst b/docs/source/index.rst new file mode 100644 index 0000000..ab122cb --- /dev/null +++ b/docs/source/index.rst @@ -0,0 +1,19 @@ +.. JMK Engineering Python Library documentation master file, created by + sphinx-quickstart on Wed Jan 1 14:10:19 2025. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +JMK Engineering Python Library documentation +============================================ + +Add your content using ``reStructuredText`` syntax. See the +`reStructuredText `_ +documentation for details. + + +.. toctree:: + :maxdepth: 2 + :caption: Contents: + + general/index + diff --git a/pyproject.toml b/pyproject.toml index c6eb1b1..f40afe4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,13 +1,13 @@ [project] name = "jmktools" -version = "0.1.0" +version = "0.2b1" description = "JMK Engineering Python Toolboox" readme = "README.md" license = "AGPL-3.0-or-later" license-files = [ "LICENSE", ] -requires-python = ">=3.14" +requires-python = ">=3.13" dependencies = [ "numpy>=2.4.2", "pandas>=3.0.0", diff --git a/src/jmktools/__init__.py b/src/jmktools/__init__.py index 6d03e83..84e0c09 100644 --- a/src/jmktools/__init__.py +++ b/src/jmktools/__init__.py @@ -1,5 +1,6 @@ from .circuits import * from .general import * +from .grounding import * from .tables import * def main() -> None: diff --git a/src/jmktools/grounding.py b/src/jmktools/grounding.py new file mode 100644 index 0000000..a56c0ee --- /dev/null +++ b/src/jmktools/grounding.py @@ -0,0 +1,62 @@ +''' +JMK Engineering Inc. Python Tools +by: Jeff MacKinnon + +email: jeff@jmkengineering.com + +Grounding functions + +''' + +import pandas as pd +import math + + +def resistivity_wenner(spacing,resistance): + ''' + This function calculates the resistivity in ohm-m from the wenner resistance measurements. + + The spacing must be in meters and the resistance in ohms. + ''' + + rho = 2 * math.pi * spacing * resistance + return rho + +def laurent (area,len_conductor,resistivity): + ''' + This function calculates the expected Rg of a grounding system using the Laurent Method, also known as the IEEE 80 Simplified method. + + Rg_area = (soil_resistivity / (4 * sqrt(Area_of_grounding_system)) + + Rg_conductors = soil_resistivity / total_buried_conductor_length + + Rg = Rg_area + Rg_conductors + + ''' + + rg_area = resistivity / (4 * math.sqrt(area)) + rg_conductor = resistivity / len_conductor + + rg = rg_area + rg_conductor + + return rg + +def sverak(area,len_conductor,depth,resistivity): + ''' + This is IEEE 80, Equation 57 or Sverak method to calcuate the Rg. + + It is: + + Rg = soil_resistivity*[1/total_buried_conductor_length + {(1/sqrt(20*Area_of_grounding_system)) * (1 + 1/(1+buried_depth_grounding_system * sqrt(20 / Area_of_grounding_system)))}] + + This function breaks the function into three parts to make it easier to read. + ''' + + sv_part1 = 1/ len_conductor + sv_part2 = 1 / math.sqrt(20 * area) + sv_part3 = 1 + (1 / (1 + depth * math.sqrt(20 / area))) + + rg = resistivity * (sv_part1 + sv_part2 * sv_part3) + + return rg + diff --git a/src/jmktools/tables.py b/src/jmktools/tables.py index dd408cb..3010914 100644 --- a/src/jmktools/tables.py +++ b/src/jmktools/tables.py @@ -3,6 +3,8 @@ JMK Engineering Inc. Python Library for design and such. by: Jeff MacKinnon email: jeff@jmkengineering.com + +This module imports all the various tables as shown in the various standards and codes. ''' # diff --git a/tools.py b/tools.py new file mode 100644 index 0000000..43e781f --- /dev/null +++ b/tools.py @@ -0,0 +1,346 @@ +''' +JMK Engineering Inc. Python Library for design and such. +by: Jeff MacKinnon + +email: jeff@jmkengineering.com + +Commandline tools for JEPL functions + +''' + +import argparse +import jmktools as jmk + + +# +# functions for the functions +# + +# General + +def va(args): + x = [1,3,None] + if args.phases in x: + if args.phases == None: + phases = 3 + else: + phases = args.phases + result = jmk.va(args.voltage, args.current, phases=phases) + print ( str(round(result,2))+ "VA") + else: + print(args.phases) + +def xfmr_sc(args): + if args.phases == None: + phases = 3 + elif arg.phases == 1: + phases = 1 + else: + phases = 3 + + #print(args.kva) + #print(args.voltage) + #print(args.impedance) + + result = jmk.xfmr_sc(args.kva, args.voltage, args.impedance, phases = phases) + print(result) + + +# Circuits + +def condsize(args): + if args.temp == None: + temp = 60 + else: + temp = args.temp + if args.material == None: + material = 'cu' + else: + material = args.material + if args.code == None: + code = 'CEC' + else: + code = args.code + if args.raceway == None: + raceway = True + elif args.raceway == 'n': + raceway = False + else: + raceway = True + if args.ambient == None: + ambient = 30 + else: + ambient = args.ambient + if args.maxsize == None: + maxsize = 500 + else: + maxsize = 500 + if args.type == None: + loadtype = None + else: + valid_load_type = ['normal','xfmr','xfmrp','xfmrs','motor',None] + if args.type not in valid_load_type: + print(args.type + " is not a valid load_type.") + else: + loadtype = args.type + + result = jmk.conductor_size(args.current, temp = temp, material = material, code = code, raceway = raceway, max = maxsize, load_type = loadtype ) + print(result) + +def condamp(args): + if args.temp == None: + temp = 60 + else: + temp = args.temp + if args.material == None: + material = 'cu' + else: + material = args.material + if args.code == None: + code = 'CEC' + else: + code = args.code + if args.raceway == None: + raceway = True + elif args.raceway == 'n': + raceway = False + else: + raceway = True + if args.ambient == None: + ambient = 30 + else: + ambient = args.ambient + result = jmk.conductor_ampacity(args.conductor, temp = temp, material = material, code = code, raceway = raceway) + print(result) + +def bonding(args): + + if args.material == None: + material = 'cu' + else: + material = args.material + if args.code == None: + code = 'CEC' + else: + code = args.code + if args.bus == None: + bus = False + else: + bus = args.bus + + result = jmk.bonding_conductor(args.circuit_ampacity, bus = bus, material = material, code = code) + print(result) + + +def voltagedrop(args): + + voltage = args.voltage + current = args.current + size = args.conductor_size + length = args.length + if args.phases == None: + num_phase = 3 + elif args.phases == '1': + num_phase = 1 + elif args.phases == '3': + num_phase = 3 + else: + print('--phase must be 1 or 2.') + if args.material == None: + material = 'cu' + else: + material = args.material + if args.runs == None: + num_runs = 1 + else: + num_runs = args.runs + if args.runs == None: + num_runs = 1 + else: + num_runs = args.runs + if args.code == None: + code = 'CEC' + else: + code = args.code + if args.powerfactor == None: + power_factor = 'dc' + else: + power_factor = args.powerfactor + if args.raceway == None: + raceway = True + elif args.raceway == 'n': + raceway = False + else: + raceway = True + + if args.temp == None: + insul_temp = 75 + else: + insul_temp = args.temp + + result = jmk.voltage_drop(voltage, current, size, length, num_phase = num_phase, material = material, num_runs = num_runs, code = code, power_factor = power_factor, raceway = raceway, insul_temp = insul_temp) + print(result) + +def conduit_size(args): + + if args.insulation == None: + insulation = "RW90" + else: + insulation = args.insulation + if args.voltage == None: + voltage = 1000 + else: + voltage = args.voltage + if args.jacketed == None: + jacketed = False + else: + jacketed = args.jacketed + if args.material == None: + material = 'SCH80' + else: + material = args.material + if args.code == None: + code = 'CEC' + else: + code = args.code + + result = jmk.conduit_size(args.num_conductors,args.size,args.bond,insulation=insulation, voltage = voltage, jacketed = jacketed,material=material, code = code) + percent_fill = str(round(result[3],2) * 100) + "%" + " fill." + print(percent_fill,result[1]) + + +# Solar + +def temp_adj_Voc(args): + if args.beta == None: + beta = -0.5 + else: + beta = args.beta + if args.stctemp == None: + stctemp = 25 + else: + stctemp = args.stctemp + result = jmk.temp_adj_Voc(args.voc, args.temperature, beta, stctemp) + print(str(round(result,2))+"V") + + +# +# Parse the options +# + +try: + parser = argparse.ArgumentParser() + subparsers = parser.add_subparsers( + title="tools", + help="Commandline Functions" + ) + +# +# This section is for the jepl_general functions +# + +# Calcuating VA + + va_parser = subparsers.add_parser("va", help="calculate VA (S)") + va_parser.add_argument("voltage", type=float,help="Voltage of the circuit") + va_parser.add_argument("current", type=float,help="Current of the circuit.") + va_parser.add_argument("-p","--phases", type=int,help="Phases of the circuit. It should be either 1 or 3, and we will assume 3 if it is not used.") + va_parser.set_defaults(func=va) + + +# Calcuating xfmr_sc + + xfmrsc_parser = subparsers.add_parser("xfmr_sc", help="Calculates the secondary short circuit current of a transformer") + xfmrsc_parser.add_argument("voltage", type=float,help="Voltage of the circuit") + xfmrsc_parser.add_argument("kva", type=float,help="Transformer rating in kVA.") + xfmrsc_parser.add_argument("impedance", type=float,help="Transformer impedance in percent Z.") + xfmrsc_parser.add_argument("-p","--phases", type=int,help="Phases of the circuit. It should be either 1 or 3, and we will assume 3 if it is not used.") + xfmrsc_parser.set_defaults(func=xfmr_sc) + +# +# Circuit Tools +# + + + condsize_parser = subparsers.add_parser("condsize", help="Determine the conductor size for a defined current and load type.") + condsize_parser.add_argument("current", type=float, help="The current of the circuit.") + condsize_parser.add_argument("-t", "--temp", type=int, help="The temperature rating for the insulation, if none included is 60C. default: 60" ) + condsize_parser.add_argument("-m", "--material", type=str, help="This should be cu or al, for copper or aluminum. If none defined cu will be used.") + condsize_parser.add_argument("-c", "--code", type=str, help="Currently this will either be CEC or NEC. default CEC") + condsize_parser.add_argument("-r", "--raceway", type=str, help="If the conductors are in a raceway use y for yes and n for no.") + condsize_parser.add_argument("-a", "--ambient", type=float, help="Ambient temperature in celsious. default: 30") + condsize_parser.add_argument("-x", "--maxsize", type=str, help="Maximum conductor size in AWG/kcmil. default: 500") + condsize_parser.add_argument("--type",type=str, help="Load type {}'normal','xfmr','xfmrp','xfmrs','motor',None}, default: None") + condsize_parser.set_defaults(func=condsize) + + condamp_parser = subparsers.add_parser("condamp", help="Determine the ampacity of a conductor.") + condamp_parser.add_argument("conductor", type=str, help="The conductor size in AWG/kcmil") + condamp_parser.add_argument("-t", "--temp", type=int, help="The temperature rating for the insulation, if none included is 60C. default: 60" ) + condamp_parser.add_argument("-m", "--material", type=str, help="This should be cu or al, for copper or aluminum. If none defined cu will be used.") + condamp_parser.add_argument("-c", "--code", type=str, help="Currently this will either be CEC or NEC. default CEC") + condamp_parser.add_argument("-r", "--raceway", type=str, help="If the conductors are in a raceway use y for yes and n for no.") + condamp_parser.add_argument("-a", "--ambient", type=float, help="Ambient temperature in celsious. default: 30") + condamp_parser.set_defaults(func=condamp) + + bonding_parser = subparsers.add_parser("bonding", help="Bonding conductor for the circuit, wire or bus.") + bonding_parser.add_argument("circuit_ampacity", type=float, help="Circuit ampacity.") + bonding_parser.add_argument("-m", "--material", type=str, help="This should be cu or al, for copper or aluminum. If none defined cu will be used.") + bonding_parser.add_argument("-c", "--code", type=str, help="Currently this will either be CEC or NEC. default CEC") + bonding_parser.add_argument("-b", "--bus", type=bool, help="Bus or wire bond, bus =True, wire = False.") + bonding_parser.set_defaults(func=bonding) + + + + voltagedrop_parser = subparsers.add_parser("voltagedrop", help="Determine the voltage drop of a circuit.") + voltagedrop_parser.add_argument("voltage", type=float, help="The circuit voltage.") + voltagedrop_parser.add_argument("current", type=float, help="The circuit current") + voltagedrop_parser.add_argument("conductor_size", type=str, help="The conductor size.") + voltagedrop_parser.add_argument("length", type=float, help="The circuit length, in metres.") + voltagedrop_parser.add_argument("-t", "--temp", type=int, help="The temperature rating for the insulation, if none included is 60C. default: 75" ) + voltagedrop_parser.add_argument("-m", "--material", type=str, help="This should be cu or al, for copper or aluminum. If none defined cu will be used.") + voltagedrop_parser.add_argument("-c", "--code", type=str, help="Currently this will either be CEC or NEC. default CEC") + voltagedrop_parser.add_argument("-r", "--raceway", type=str, help="If the conductors are in a raceway use y for yes and n for no.") + voltagedrop_parser.add_argument("-pf","--powerfactor", type=str, help="Power Factor. Valid options DC, 1, 0.9, 0.8. default dc") + voltagedrop_parser.add_argument("--phases", type=str, help="Number of phases, 1 or 3, default = 3.") + voltagedrop_parser.add_argument("--runs", type=int, help="Number of circuits in parallel, default 1.") + voltagedrop_parser.set_defaults(func=voltagedrop) + + + consize_parser = subparsers.add_parser("conduitsize", help="Calculate the proper conduit size.") + consize_parser.add_argument("num_conductors", type=int, help="Number of current carrying conductors, typically 1 to 4.") + consize_parser.add_argument("size", type=str, help="Size of the current carrying conductors in AWG or kcmil." ) + consize_parser.add_argument("bond", type=str, help="Size of the bond conductor, or equipment grounding conductor in AWG or kcmil") + consize_parser.add_argument("-i", "--insulation", type=str, help="Insulation type. default RW90") + consize_parser.add_argument("-v", "--voltage", type=int, help="Voltage rating of the insulation, 600 or 1000") + consize_parser.add_argument("-j", "--jacketed", type=bool, help="Are the conductors jacketed? Default: False") + consize_parser.add_argument("-m", "--material", type=str, help="Material or conduit type, ie EMT, ENT, SCH40, etc. Default SCH80") + consize_parser.add_argument("-c", "--code", type=str, help="Currently this will either be CEC or NEC. default CEC") + consize_parser.set_defaults(func=conduit_size) + + + + + + +# +# Solar Tools +# + + tempvoc = subparsers.add_parser("tempVoc", help="Calculate the temperature adjusted Voc of a PV panel.") + tempvoc.add_argument("voc", type=float,help="Open circuit voltage of the panel.") + tempvoc.add_argument("temperature", type=float, help="Temperature the panel will be at.") + tempvoc.add_argument("-b", "--beta", type=float, help="Panel Beta value, if none is added -0.5 will be used.") + tempvoc.add_argument("-t", "--stctemp", type=float, help="The STC temp. If none is added 25C will be used.") + tempvoc.set_defaults(func=temp_adj_Voc) + + + +# This runs the function that was selected with the arguments in the command. + args = parser.parse_args() + args.func(args) + + + +except argparse.ArgumentError as e: + #log.error("Error parsing arguments") + raise e \ No newline at end of file