Finished adding conduit sizing for CEC projects.

This commit is contained in:
Jeff MacKinnon 2025-01-10 10:27:26 -04:00
commit d1a897a78f
16 changed files with 523 additions and 127 deletions

1
.gitignore vendored
View file

@ -2,3 +2,4 @@
test* test*
__pycache__ __pycache__
*.db *.db
/docs/build/

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

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

@ -0,0 +1,17 @@
.. 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:

View file

@ -791,63 +791,6 @@
"jmk.bonding_conductor(jmk.conductor_ampacity(12, temp = 75, material = 'al', code = 'CEC', raceway = False, ambient = 30),bus=False,material='al',code = 'CEC')" "jmk.bonding_conductor(jmk.conductor_ampacity(12, temp = 75, material = 'al', code = 'CEC', raceway = False, ambient = 30),bus=False,material='al',code = 'CEC')"
] ]
}, },
{
"cell_type": "code",
"execution_count": 18,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"1\n"
]
}
],
"source": [
"import pandas as pd\n",
"import numpy as np\n",
"\n",
"wire_size = [\n",
" ['14',2.08],\n",
" ['12',3.31],\n",
" ['10',5.26],\n",
" ['8',8.37],\n",
" ['6',13.3],\n",
" ['4',21.2],\n",
" ['3',26.7],\n",
" ['2',33.6],\n",
" ['1',42.4],\n",
" ['1/0',53.5],\n",
" ['2/0',67.4],\n",
" ['3/0',85],\n",
" ['4/0',107],\n",
" ['250',127],\n",
" ['300',152],\n",
" ['350',177],\n",
" ['400',203],\n",
" ['500',253],\n",
" ['600',304],\n",
" ['700',355],\n",
" ['800',405],\n",
" ['900',456],\n",
" ['1000',507],\n",
" ['1250',633],\n",
" ['1500',760],\n",
" ['1750',887],\n",
" ['2000',1010]]\n",
"\n",
"\n",
"#df = pd.DataFrame(wire_size, columns=[\"wire\",'area'])\n",
"\n",
"\n",
"x = np.array(wire_size)\n",
"row = np.where(x == '12')[0][0]\n",
"\n",
"\n",
"print(row)\n"
]
},
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 19, "execution_count": 19,
@ -857,95 +800,57 @@
"name": "stdout", "name": "stdout",
"output_type": "stream", "output_type": "stream",
"text": [ "text": [
"[None 167.0 307.0 507.0 792.0 1140.0 2027.0 2951.0 4560.0 6138.0 7870.0\n", "53\n"
" 12439.0 17613.0 31225.0]\n"
] ]
} }
], ],
"source": [ "source": [
"db_result_index = 3\n", "db_result_index = 3\n",
"\n", "\n",
"test = ' WHERE DB2 > 200'\n",
"\n",
"try:\n", "try:\n",
" with sqlite3.connect(\"jepl-cec21.db\") as con:\n", " with sqlite3.connect(\"jepl-cec21.db\") as con:\n",
" cur = con.cursor()\n", " cur = con.cursor()\n",
" cur.execute('SELECT * from \"Table9\"')\n", " cur.execute('SELECT \"Trade Size\" from \"Table9\"'+test)\n",
" table = cur.fetchall()\n", " table = cur.fetchone()\n",
" conduits = np.array(table)\n", " conduits = np.array(table)[0]\n",
"\n", "\n",
"except sqlite3.OperationalError as e:\n", "except sqlite3.OperationalError as e:\n",
" print(e)\n", " print(e)\n",
"\n", "\n",
"column = conduits[ :,db_result_index]\n",
"print(column)\n",
"#x = np.array(column)\n",
"#row = np.where(x = 200)[0][0]\n",
"\n", "\n",
"#print(row)" "print(conduits)\n"
] ]
}, },
{ {
"cell_type": "code", "cell_type": "markdown",
"execution_count": 20,
"metadata": {}, "metadata": {},
"outputs": [
{
"ename": "TypeError",
"evalue": "'>=' not supported between instances of 'NoneType' and 'float'",
"output_type": "error",
"traceback": [
"\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[1;31mTypeError\u001b[0m Traceback (most recent call last)",
"Input \u001b[1;32mIn [20]\u001b[0m, in \u001b[0;36m<cell line: 1>\u001b[1;34m()\u001b[0m\n\u001b[1;32m----> 1\u001b[0m \u001b[43mjmk\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mconduit_size\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m4\u001b[39;49m\u001b[43m,\u001b[49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43m12\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m,\u001b[49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43m12\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m)\u001b[49m\n",
"File \u001b[1;32mc:\\Business\\JMKEngineering\\Engineering\\Active\\TEP Group\\2425_TEP_Shubie-tower-project\\eng\\resources\\JEPL\\jepl\\jepl_circuits.py:602\u001b[0m, in \u001b[0;36mconduit_size\u001b[1;34m(num_cc, cc_con, bond, material)\u001b[0m\n\u001b[0;32m 599\u001b[0m column \u001b[38;5;241m=\u001b[39m conduits[ :,db_result_index]\n\u001b[0;32m 601\u001b[0m x \u001b[38;5;241m=\u001b[39m np\u001b[38;5;241m.\u001b[39marray(column)\n\u001b[1;32m--> 602\u001b[0m row \u001b[38;5;241m=\u001b[39m np\u001b[38;5;241m.\u001b[39mwhere(\u001b[43mx\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m>\u001b[39;49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43m \u001b[49m\u001b[43mmin_trade_area\u001b[49m)[\u001b[38;5;241m0\u001b[39m][\u001b[38;5;241m0\u001b[39m]\n\u001b[0;32m 604\u001b[0m \u001b[38;5;28mprint\u001b[39m(row)\n\u001b[0;32m 606\u001b[0m result \u001b[38;5;241m=\u001b[39m conduits[row][\u001b[38;5;241m0\u001b[39m]\n",
"\u001b[1;31mTypeError\u001b[0m: '>=' not supported between instances of 'NoneType' and 'float'"
]
}
],
"source": [ "source": [
"jmk.conduit_size(4,'12','12')" "The Conduit sizing module is designed to size conduits based on the CEC values for the various conductor sizes with insulation.\n",
"\n",
"It returns two values, the trade size (diameter) in mm and the text for a cable/conduit schedule"
] ]
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 22, "execution_count": 25,
"metadata": {},
"outputs": [],
"source": [
"valid_material = ['RMC', # Rigid Metal Conduit\n",
" 'FMC', # Flexible Metal Conduit\n",
" 'RPVC', # Rigid PVC\n",
" 'EB1', # Type EB1\n",
" 'DB2', # Type DB2\n",
" 'LTMC', # Liquid Tight Metal Conduit\n",
" 'LTNMC', # Liquid Tight non-metallic conduit\n",
" 'EMT', # electrical metallic tubing\n",
" 'ENT', # electrical non-metallic tubing\n",
" 'SCH40', # HDPE Schedule 40\n",
" 'SCH80', # HDPE Schedule 80\n",
" #'DR9', # HDPE DR9\n",
" #'DR11', # HDPE DR11\n",
" #'DR135', # HDPE DR13.5\n",
" #'DR155' # HDPE DR15.5\n",
" ]"
]
},
{
"cell_type": "code",
"execution_count": 23,
"metadata": {}, "metadata": {},
"outputs": [ "outputs": [
{ {
"name": "stdout", "name": "stdout",
"output_type": "stream", "output_type": "stream",
"text": [ "text": [
"0\n" "16 is the trade size\n",
"16mm SCH80 is the value you can use in your cable/conduit schedule.\n"
] ]
} }
], ],
"source": [ "source": [
"x = np.array(valid_material)\n", "conduit_size = jmk.conduit_size(4,'12','12')\n",
"row = np.where(x == 'RMC')[0][0]\n", "\n",
"print(row)" "print(str(conduit_size[0]) + \" is the trade size\")\n",
"print(conduit_size[1] + \" is the value you can use in your cable/conduit schedule.\")"
] ]
} }
], ],

View file

@ -0,0 +1,7 @@
Voltage,Movable LA SI,Movable LA Imperial,Fixed LA SI,Fixed LA Imperial,Restricted SI,Restricted Imperial
50,Not Specified,Not Specified,Not Specified,Not Specified,Not Specified,Not Specified
150,3,120,1,42,0,0
750,3,120,1,42,0.3,12
15000,3,120,1.5,60,0.7,26
35000,3,120,1.8,72,0.8,31
46000,3,120,2.5,96,0.8,33
1 Voltage Movable LA SI Movable LA Imperial Fixed LA SI Fixed LA Imperial Restricted SI Restricted Imperial
2 50 Not Specified Not Specified Not Specified Not Specified Not Specified Not Specified
3 150 3 120 1 42 0 0
4 750 3 120 1 42 0.3 12
5 15000 3 120 1.5 60 0.7 26
6 35000 3 120 1.8 72 0.8 31
7 46000 3 120 2.5 96 0.8 33

View file

@ -0,0 +1,7 @@
Voltage,Movable LA SI,Movable LA Imperial,Fixed LA SI,Fixed LA Imperial,Restricted SI,Restricted Imperial
50,Not Specified,Not Specified,Not Specified,Not Specified,Not Specified,Not Specified
300,3,120,1,42,0,0
1000,3,120,1,42,0.3,12
5000,3,120,1.5,60,0.4,17
15000,3,120,1.5,60,0.7,26
45000,3,120,2.5,96,0.8,33
1 Voltage Movable LA SI Movable LA Imperial Fixed LA SI Fixed LA Imperial Restricted SI Restricted Imperial
2 50 Not Specified Not Specified Not Specified Not Specified Not Specified Not Specified
3 300 3 120 1 42 0 0
4 1000 3 120 1 42 0.3 12
5 5000 3 120 1.5 60 0.4 17
6 15000 3 120 1.5 60 0.7 26
7 45000 3 120 2.5 96 0.8 33

View file

@ -5,8 +5,11 @@ by: Jeff MacKinnon
email: jeff@jmkengineering.com email: jeff@jmkengineering.com
''' '''
import sys import sys
from .jepl_general import *
from .jeplpv import * from .jeplpv import *
from .jepl_circuits import * from .jepl_circuits import *
from .jepl_templates import *
from .jepl_safety import *
def Test(): def Test():
print( "JMK Python Module works! !") print( "JMK Python Module works! !")

View file

@ -496,8 +496,8 @@ def bonding_conductor(conductor_ampacity,bus=False,material='cu',code = 'CEC'):
return bond_size return bond_size
## This doesn't work yet, but its getting there ## This doesn't work yet, but its getting
def conduit_size(num_cc,cc_con,bond,material='EMT'): def conduit_size(num_cc,cc_con,bond,material='SCH80'):
# Calculate fill requirements based on Table 8 # Calculate fill requirements based on Table 8
@ -582,35 +582,34 @@ def conduit_size(num_cc,cc_con,bond,material='EMT'):
# Total conductor area # Total conductor area
area_conductors = cc_area + bond_area area_conductors = cc_area + bond_area
#print(area_conductors)
min_trade_area = area_conductors / percent_fill # The minimum area of the conduit min_trade_area = area_conductors / percent_fill # The minimum area of the conduit
#print(min_trade_area)
parameter = ' WHERE ' + material + ' > ' + str(min_trade_area)
try: try:
with sqlite3.connect("jepl-cec21.db") as con: with sqlite3.connect("jepl-cec21.db") as con:
cur = con.cursor() cur = con.cursor()
cur.execute('SELECT * from "Table9"') cur.execute('SELECT "Trade Size" from "Table9"'+ parameter )
table = cur.fetchall() table = cur.fetchone()
conduits = np.array(table) conduit = table
except sqlite3.OperationalError as e: except sqlite3.OperationalError as e:
print(e) print(e)
column = conduits[ :,db_result_index]
x = np.array(column)
row = np.where(x >= min_trade_area)[0][0]
print(row)
result = conduits[row][0] result_raw = conduit[0]
result_name = str(conduit[0]) + 'mm ' + material
return result return result_raw,result_name
def cable_schedule_naming(conductor_size,conductors,runs = 1,bond='BOND'): def cable_schedule_naming(conductor_size,conductors,runs = 1,bond='BOND'):

30
jepl/jepl_general.py Normal file
View file

@ -0,0 +1,30 @@
'''
JMK Engineering Inc. Python Library for design and such.
by: Jeff MacKinnon
email: jeff@jmkengineering.com
Some General Functions and things
'''
import pandas as pd
import numpy as np
import math
import sqlite3
def va(voltage, current,phases=3):
'''Calculate the Volt-Amp of a circuit
'''
if phases == 3:
va = (math.pi * voltage * current)
elif phases == 1:
vs = voltage * current
else:
print("Phases needs to be 1 or 3 for now.")
return va

129
jepl/jepl_safety.py Normal file
View file

@ -0,0 +1,129 @@
'''
JMK Engineering Inc. Python Library for design and such.
by: Jeff MacKinnon
email: jeff@jmkengineering.com
Electrical Safety
These functions are used to calculate boundaries, etc.
'''
import sqlite3
def limitedApproach(voltage,fixed=False,DC=False,unitsSI=True,year=2015):
if year == 2015:
database = "jepl-z46215.db"
if DC == True:
try:
with sqlite3.connect(database) as con:
cur = con.cursor()
cur.execute('SELECT * FROM "Table1B" WHERE "Voltage" > ? ', (voltage,))
row = cur.fetchone()
except sqlite3.OperationalError as e:
print(e)
else:
try:
with sqlite3.connect(database) as con:
cur = con.cursor()
cur.execute('SELECT * FROM "Table1A" WHERE "Voltage" > ? ', (voltage,))
row = cur.fetchone()
except sqlite3.OperationalError as e:
print(e)
if (fixed == False) & (unitsSI == True):
column = 1
elif (fixed == False) & (unitsSI == False):
column = 2
elif (fixed == True) & (unitsSI == True):
column = 3
elif (fixed == True) & (unitsSI == False):
column = 4
distance = row[column]
return(distance)
def restrictedApproach(voltage,DC=False,unitsSI=True,year=2015):
if year == 2015:
database = "jepl-z46215.db"
if DC == True:
try:
with sqlite3.connect(database) as con:
cur = con.cursor()
cur.execute('SELECT * FROM "Table1B" WHERE "Voltage" > ? ', (voltage,))
row = cur.fetchone()
except sqlite3.OperationalError as e:
print(e)
else:
try:
with sqlite3.connect(database) as con:
cur = con.cursor()
cur.execute('SELECT * FROM "Table1A" WHERE "Voltage" > ? ', (voltage,))
row = cur.fetchone()
except sqlite3.OperationalError as e:
print(e)
if (unitsSI == True):
column = 5
else:
column = 6
distance = row[column]
return(distance)
def gloveClass(voltage,DC=False):
'''
The glove class is based on the ASTM standard D120.
'''
if DC == True:
if voltage <= 750:
gloveclass = '00'
elif voltage <= 1500:
gloveclass = '0'
elif voltage <= 11250:
gloveclass = '1'
elif voltage <= 25500:
gloveclass = '2'
elif voltage <= 39750:
gloveclass = '3'
elif voltage <= 54000:
gloveclass = '4'
else:
gloveclass = "Voltage too High"
else:
if voltage <= 500:
gloveclass = '00'
elif voltage <= 1000:
gloveclass = '0'
elif voltage <= 7500:
gloveclass = '1'
elif voltage <= 17000:
gloveclass = '2'
elif voltage <= 26500:
gloveclass = '3'
elif voltage <= 36000:
gloveclass = '4'
else:
gloveclass = "Voltage too High"
return(gloveclass)

84
jepl/jepl_templates.py Normal file
View file

@ -0,0 +1,84 @@
'''
JMK Engineering Inc. Python Library for design and such.
by: Jeff MacKinnon
email: jeff@jmkengineering.com
Template Generation
These functions are used to create labels, reports, etc.
'''
from .jepl_safety import *
def af_label(equipment,working_distance,incident_energy,af_boundary,energy_level,voltage, author="Jeff MacKinnon",warning="warning",project="Arc Flash Warning Label",project_num="2500",DC=False,fixedElectrode = True, SI=True,project_year=2015,test=False):
'''
This function is for printing a single label with all the information in the function above. Include the units (in, Vac, Vdc, ft, m, mm, etc.) in the values. All values are strings.
'''
from datetime import date
date = date.today()
revdate = date.strftime("%m/%d/%Y")
if DC == True:
voltage_units = " Vdc"
else:
voltage_units = " Vac"
label_voltage = str(voltage) + voltage_units
glove_class = gloveClass(voltage,DC=DC)
limited_approach = limitedApproach(voltage,fixed=fixedElectrode,DC=DC,unitsSI=SI,year=project_year)
restricted_approach = restrictedApproach(voltage,DC=DC,unitsSI=SI,year=project_year)
if warning =="warning":
warning_colour = '242,85,31'
warning_text = '\warning WARNING'
elif warning == "danger":
warning_colour = '242,85,31' # This colour needs to change
warning_text = '\warning DANGER'
else:
warning_colour = '255,255,255' # This colour needs to change
warning_text = ''
from jinja2 import Environment, FileSystemLoader
environment = Environment( block_start_string = '\BLOCK{',
block_end_string = '}',
variable_start_string = '\VAR((',
variable_end_string = '))',
comment_start_string = '\#{',
comment_end_string = '}',
line_comment_prefix = '%#',
loader=FileSystemLoader("jepl/templates/")
)
template = environment.get_template("template-label-ArcFlash.tex")
if test == True:
filename = "test"+str(date) + "-"+project_num+"-label-ArcFlash.tex"
else:
filename = str(date) + "-"+project_num+"-label-ArcFlash.tex"
content = template.render(
DATE = revdate,
EQUIPMENT = equipment,
WORKING_DISTANCE = working_distance,
INCIDENT_ENERGY = incident_energy,
AF_BOUNDARY = af_boundary,
ENERGY_LEVEL = energy_level,
VOLTAGE = label_voltage,
LIMITED_APPROACH = limited_approach,
RESTRICTED_APPROACH = restricted_approach,
GLOVE_CLASS = glove_class,
AUTHOR = author,
TITLE = project,
WARNING_COLOUR = warning_colour,
WARNING_TEXT = warning_text,
)
with open(filename, mode="w", encoding="utf-8") as message:
message.write(content)

View file

@ -59,6 +59,11 @@ CEC21_database = [
] ]
create_database('jepl-cec21.db',CEC21_database) create_database('jepl-cec21.db',CEC21_database)
Z46215_database = [
['jepl-z46215.db','Table1A',location+'Tables/Z462-Tables/Z46215-table1A.csv'],
['jepl-z46215.db','Table1B',location+'Tables/Z462-Tables/Z46215-table1B.csv'],
]
create_database('jepl-z46215.db',Z46215_database)
cable_database = [ cable_database = [

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View file

@ -0,0 +1,127 @@
\documentclass[12pt]{article}
\pagestyle{empty}
%\usepackage[utf8]{inputenc}
\usepackage{tikz}
\usepackage{fourier}
\usepackage{geometry}
\usepackage{anyfontsize}
\usepackage{multicol}
\usepackage[export]{adjustbox}
\usetikzlibrary{calc}
\geometry{%
paperheight=105mm,
paperwidth=160mm,
top=2mm,
bottom=2mm,
right=2mm,
left=2mm,
}
\usepackage{layout}
\setlength{\parindent}{1pt}
\title{\VAR((TITLE))}
\date{\VAR((DATE))}
\author{\VAR((AUTHOR))}
\begin{document}
%% Header Warning Banner %%
\definecolor{warningcolour}{RGB}{\VAR((WARNING_COLOUR))}
% \thispagestyle{empty}
\begin{tikzpicture}[overlay, remember picture]%
\node[]
at ($(current page.north)+(0cm, -13mm)$) {%
\begin{tikzpicture}
\node[rectangle,
draw = white,
text = black,
minimum width = \textwidth,
minimum height = 23mm,
fill = warningcolour] (r) at (0,0)
{%
\fontfamily{phv}\fontsize{36pt}{40pt}\selectfont \textbf{\VAR((WARNING_TEXT))}% This is the warning text
};
\end{tikzpicture}
};
% This is the main message
\node[%
text width=\textwidth,
align=center,
]
at ($(current page.center) + (0cm,18mm)$) {%
\fontfamily{phv}
\fontsize{20pt}{100pt}\selectfont
\textbf{%
Arc Flash and Shock Hazard Present \\
Appropriate PPE Required
}
};
% This is the study Results
\node[text width=\textwidth, align=center]
at ($(current page.center) + (5mm,-7mm)$) {%
\begin{multicols}{2}
\textbf{ARC FLASH PROTECTION} \\
\vspace{3mm}
\begin{tabular}{ l l }
Working Distance & \textbf{\VAR((WORKING_DISTANCE))} \\ % Working Distance
Incident Energy & \textbf{\VAR((INCIDENT_ENERGY)) cal/cm$^2$} \\ % Incident Energy Value
Arc Flash Hazard Boundary & \textbf{\VAR((AF_BOUNDARY))} \\ % Arc Flash Boundary
Energy Level & \textbf{Level \VAR((ENERGY_LEVEL))} \\ % Energy Level
\end{tabular}
\columnbreak
\textbf{SHOCK PROTECTION}\\
\vspace{4mm}
\begin{tabular}{ l l }
Shock Hazard & \textbf{\VAR((VOLTAGE))} \\ % Shock voltage
Limited Approach & \textbf{\VAR((LIMITED_APPROACH))} \\ % Limited Approach boundary
Restricted Approach & \textbf{\VAR((RESTRICTED_APPROACH))} \\ % Restricted Approach boundard
Glove Class & \textbf{\VAR((GLOVE_CLASS))} \\ % Glove Class
\end{tabular}
\end{multicols}
};
% This is the equipment information
\node[text width=0.5\textwidth, align=left]
at ($(current page.west) + (45mm,-40mm)$) {%
\large
Equipment: \textbf{\VAR((EQUIPMENT))} \\
Date: \textbf{\VAR((DATE))} \\
\vspace{2mm}
\scriptsize
Arc flash analysis by \VAR((AUTHOR))
};
% This is the logo
\node[text width=0.5\textwidth, align=right]
at ($(current page.east) + (-40mm,-4cm)$) {%
\includegraphics[width=70mm,left]{jepl/templates/static/logo.png}
};
\end{tikzpicture}
\newpage
\end{document}