feature: grounding and Docs initial

This commit is contained in:
Jeff MacKinnon 2026-02-12 18:17:18 -04:00
parent f7972a1635
commit 16666d98d0
11 changed files with 540 additions and 2 deletions

20
docs/Makefile Normal file
View file

@ -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)

35
docs/make.bat Normal file
View file

@ -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

27
docs/source/conf.py Normal file
View file

@ -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']

View file

@ -0,0 +1,9 @@
JEPL General Functions
================================
.. toctree::
:maxdepth: 2
:caption: Contents:
va

View file

@ -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

19
docs/source/index.rst Normal file
View file

@ -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 <https://www.sphinx-doc.org/en/master/usage/restructuredtext/index.html>`_
documentation for details.
.. toctree::
:maxdepth: 2
:caption: Contents:
general/index

View file

@ -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",

View file

@ -1,5 +1,6 @@
from .circuits import *
from .general import *
from .grounding import *
from .tables import *
def main() -> None:

62
src/jmktools/grounding.py Normal file
View file

@ -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

View file

@ -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.
'''
#

346
tools.py Normal file
View file

@ -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