From 1620e7413082fbe2a9df89c96540092b452e6f1b Mon Sep 17 00:00:00 2001 From: youandvern Date: Sat, 23 Mar 2024 23:18:39 -0700 Subject: [PATCH 1/2] Reset cached PyPI badge in readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3c6412c..b2782bf 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Tests    Coverage Status    License: MIT    - PyPI version + PyPI version

From 2f6126351127d0e84533f068f147925fa00d1b7d Mon Sep 17 00:00:00 2001 From: youandvern Date: Tue, 9 Apr 2024 20:52:53 -0700 Subject: [PATCH 2/2] Add aluminum section properties --- efficalc/sections/__init__.py | 10 + efficalc/sections/alum_angle.py | 189 +++++++++++ efficalc/sections/alum_channel.py | 126 +++++++ efficalc/sections/alum_circular.py | 310 ++++++++++++++++++ efficalc/sections/alum_rectangular.py | 174 ++++++++++ efficalc/sections/alum_wide_flange.py | 142 ++++++++ efficalc/sections/csv/alum_shapes_L.csv | 2 +- .../sections/csv/alum_shapes_circular.csv | 2 +- efficalc/sections/csv/alum_shapes_wf.csv | 2 +- efficalc/sections/section_properties.db | Bin 647168 -> 802816 bytes efficalc/sections/section_query.py | 149 ++++++++- pythonize_csv.py | 35 -- tests/sections/test_section_query.py | 78 +++++ utils/__init__.py | 0 .../build_section_tables.py | 50 ++- utils/pythonize_csv.py | 58 ++++ 16 files changed, 1277 insertions(+), 50 deletions(-) create mode 100644 efficalc/sections/alum_angle.py create mode 100644 efficalc/sections/alum_channel.py create mode 100644 efficalc/sections/alum_circular.py create mode 100644 efficalc/sections/alum_rectangular.py create mode 100644 efficalc/sections/alum_wide_flange.py delete mode 100644 pythonize_csv.py create mode 100644 utils/__init__.py rename build_section_tables.py => utils/build_section_tables.py (57%) create mode 100644 utils/pythonize_csv.py diff --git a/efficalc/sections/__init__.py b/efficalc/sections/__init__.py index ce27f49..64ba092 100644 --- a/efficalc/sections/__init__.py +++ b/efficalc/sections/__init__.py @@ -5,6 +5,11 @@ from .aisc_rectangular import ALL_AISC_RECTANGULAR_NAMES, AiscRectangular from .aisc_tee import ALL_AISC_TEE_NAMES, AiscTee from .aisc_wide_flange import ALL_AISC_WIDE_FLANGE_NAMES, AiscWideFlange +from .alum_angle import ALL_ALUMINUM_ANGLE_NAMES, AluminumAngle +from .alum_channel import ALL_ALUMINUM_CHANNEL_NAMES, AluminumChannel +from .alum_circular import ALL_ALUMINUM_CIRCULAR_NAMES, AluminumCircular +from .alum_rectangular import ALL_ALUMINUM_RECTANGULAR_NAMES, AluminumRectangular +from .alum_wide_flange import ALL_ALUMINUM_WIDE_FLANGE_NAMES, AluminumWideFlange from .section_query import ( get_aisc_angle, get_aisc_channel, @@ -13,4 +18,9 @@ get_aisc_rectangular, get_aisc_tee, get_aisc_wide_flange, + get_aluminum_angle, + get_aluminum_channel, + get_aluminum_circular, + get_aluminum_rectangular, + get_aluminum_wide_flange, ) diff --git a/efficalc/sections/alum_angle.py b/efficalc/sections/alum_angle.py new file mode 100644 index 0000000..4869cf8 --- /dev/null +++ b/efficalc/sections/alum_angle.py @@ -0,0 +1,189 @@ +import dataclasses + + +@dataclasses.dataclass +class AluminumAngle(object): + """This is a dataclass containing the properties of an Aluminium Angle section. + + :param A: Cross-sectional area (in^2) + :type A: float + :param Ix: Moment of inertia about the x-axis (in^4) + :type Ix: float + :param Iy: Moment of inertia about the y-axis (in^4) + :type Iy: float + :param Iz: Moment of inertia about the z-axis (in^4) + :type Iz: float + :param R1: Fillet radius (in) + :type R1: float + :param R2: Tip radius (in) + :type R2: float + :param Size: The section size name + :type Size: str + :param Sx: Elastic section modulus about the x-axis (in^3) + :type Sx: float + :param Sy: Elastic section modulus about the y-axis (in^3) + :type Sy: float + :param Type: The type of section shape (e.g. American Standard, Aluminum Association, etc.) + :type Type: str + :param W: Nominal weight (lb/ft) + :type W: float + :param alpha: Angle of the z-axis (deg) + :type alpha: float + :param b: Width (in) + :type b: float + :param d: Depth (in) + :type d: float + :param rx: Radius of gyration about the x-axis (in) + :type rx: float + :param ry: Radius of gyration about the y-axis (in) + :type ry: float + :param rz: Radius of gyration about the z-axis (in) + :type rz: float + :param t: Thickness (in) + :type t: float + :param x: Location of the centroid along the x-axis (in) + :type x: float + :param y: Location of the centroid along the y-axis (in) + :type y: float + """ + + A: float + Ix: float + Iy: float + Iz: float + R1: float + R2: float + Size: str + Sx: float + Sy: float + Type: str + W: float + alpha: float + b: float + d: float + rx: float + ry: float + rz: float + t: float + x: float + y: float + + +ALL_ALUMINUM_ANGLE_NAMES = ( + "L 1 1/2 x 1 1/2 x 1/8", + "L 1 3/4 x 1 3/4 x 1/8", + "L 1 3/4 x 1 3/4 x 1/4", + "L 1 1/2 x 1 1/2 x 1/4", + "L 2 x 2 x 1/8", + "L 1 3/4 x 1 3/4 x 3/8", + "L 2 x 2 x 3/16", + "L 2 x 2 x 1/4", + "L 2 x 2 x 3/8", + "L 2 x 2 x 5/16", + "L 2 1/2 x 2 1/2 x 1/8", + "L 2 1/2 x 2 1/2 x 3/16", + "L 2 1/2 x 2 1/2 x 1/4", + "L 2 1/2 x 2 1/2 x 5/16", + "L 2 1/2 x 2 1/2 x 3/8", + "L 2 1/2 x 2 1/2 x 1/2", + "L 3 x 3 x 3/16", + "L 3 x 3 x 1/4", + "L 3 x 3 x 5/16", + "L 3 x 3 x 3/8", + "L 3 x 3 x 1/2", + "L 3 1/2 x 3 1/2 x 1/4", + "L 3 1/2 x 3 1/2 x 5/16", + "L 3 1/2 x 3 1/2 x 3/8", + "L 3 1/2 x 3 1/2 x 1/2", + "L 4 x 4 x 1/4", + "L 4 x 4 x 5/16", + "L 4 x 4 x 3/8", + "L 4 x 4 x 1/2", + "L 4 x 4 x 7/16", + "L 4 x 4 x 5/8", + "L 4 x 4 x 9/16", + "L 5 x 5 x 3/8", + "L 4 x 4 x 11/16", + "L 4 x 4 x 3/4", + "L 5 x 5 x 9/16", + "L 5 x 5 x 7/16", + "L 5 x 5 x 1/2", + "L 5 x 5 x 3/4", + "L 5 x 5 x 5/8", + "L 6 x 6 x 7/16", + "L 8 x 8 x 1/2", + "L 6 x 6 x 3/8", + "L 6 x 6 x 1/2", + "L 1 3/4 x 1 1/4 x 1/8", + "L 8 x 8 x 1", + "L 6 x 6 x 5/8", + "L 6 x 6 x 3/4", + "L 8 x 8 x 5/8", + "L 2 x 1 1/4 x 1/8", + "L 1 3/4 x 1 1/4 x 1/4", + "L 2 x 1 x 3/16", + "L 1 3/4 x 1 1/4 x 3/16", + "L 2 x 1 1/4 x 1/4", + "L 2 x 1 1/2 x 1/8", + "L 2 x 1 1/2 x 1/4", + "L 2 x 1 1/2 x 3/16", + "L 2 x 1 1/2 x 3/8", + "L 8 x 8 x 3/4", + "L 2 x 1 3/4 x 1/4", + "L 2 1/4 x 1 1/2 x 1/4", + "L 2 1/2 x 1 1/4 x 1/8", + "L 2 1/2 x 1 1/2 x 1/8", + "L 2 1/2 x 1 1/2 x 3/16", + "L 2 1/2 x 1 1/2 x 1/4", + "L 2 1/2 x 1 1/2 x 5/16", + "L 2 1/2 x 1 1/2 x 3/8", + "L 2 1/2 x 2 x 1/8", + "L 2 1/2 x 2 x 1/4", + "L 2 1/2 x 2 x 3/16", + "L 2 1/2 x 2 x 5/16", + "L 2 1/2 x 2 x 3/8", + "L 3 x 1 1/2 x 1/4", + "L 3 x 2 x 3/16", + "L 3 x 2 x 1/4", + "L 3 x 2 x 5/16", + "L 3 x 2 x 3/8", + "L 3 x 2 x 1/2", + "L 3 x 2 1/2 x 1/4", + "L 3 x 2 1/2 x 5/16", + "L 3 x 2 1/2 x 3/8", + "L 3 1/2 x 3 x 1/4", + "L 3 1/2 x 3 x 5/16", + "L 3 1/2 x 3 x 3/8", + "L 3 1/2 x 3 x 1/2", + "L 4 x 3 x 1/4", + "L 4 x 3 x 3/8", + "L 4 x 3 x 5/16", + "L 4 x 3 1/2 x 5/16", + "L 4 x 3 x 5/8", + "L 4 x 3 x 1/2", + "L 4 x 3 x 7/16", + "L 5 x 3 x 3/8", + "L 4 x 3 1/2 x 3/8", + "5 x 3 1/2 x 5/16", + "L 4 x 3 1/2 x 1/2", + "L 5 x 3 x 1/2", + "L 5 x 3 x 1/4", + "5 x 3 1/2 x 1/2", + "L 5 x 3 x 5/16", + "5 x 3 1/2 x 3/8", + "5 x 3 1/2 x 5/8", + "6 x 3 x 3/8", + "6 x 3 1/2 x 5/16", + "6 x 3 1/2 x 3/8", + "6 x 3 1/2 x 5/8", + "6 x 3 1/2 x 1/2", + "6 x 4 x 3/8", + "6 x 4 x 5/8", + "7 x 4 x 1/2", + "6 x 4 x 7/16", + "6 x 4 x 3/4", + "6 x 4 x 1/2", + "8 x 6 x 5/8", + "8 x 6 x 11/16", + "8 x 6 x 3/4", +) diff --git a/efficalc/sections/alum_channel.py b/efficalc/sections/alum_channel.py new file mode 100644 index 0000000..3737a70 --- /dev/null +++ b/efficalc/sections/alum_channel.py @@ -0,0 +1,126 @@ +import dataclasses + + +@dataclasses.dataclass +class AluminumChannel(object): + """ + This is a dataclass containing the properties of an Aluminum Channel section. + + :param A: Cross-sectional area (in^2) + :type A: float + :param Ix: Moment of inertia about the x-axis (in^4) + :type Ix: float + :param Iy: Moment of inertia about the y-axis (in^4) + :type Iy: float + :param R1: Fillet radius (in) + :type R1: float + :param R2: Tip radius (in) + :type R2: float + :param Size: The section size name + :type Size: str + :param Sx: Elastic section modulus about the x-axis (in^3) + :type Sx: float + :param Sy: Elastic section modulus about the y-axis (in^3) + :type Sy: float + :param Type: The type of section shape (e.g. American Standard, Aluminum Association, etc.) + :type Type: str + :param W: Nominal weight (lb/ft) + :type W: float + :param b: Width (in) + :type b: float + :param d: Depth (in) + :type d: float + :param d1: Nominal depth between flange fillets (in) + :type d1: float + :param rx: Radius of gyration about the x-axis (in) + :type rx: float + :param ry: Radius of gyration about the y-axis (in) + :type ry: float + :param tf: Average flange thickness (in) + :type tf: float + :param tftip: Flange thickness at the tip (in) + :type tftip: float + :param tw: Thickness of the web (in) + :type tw: float + """ + + A: float + Ix: float + Iy: float + R1: float + R2: float + Size: str + Sx: float + Sy: float + Type: str + W: float + b: float + d: float + d1: float + rx: float + ry: float + tf: float + tftip: float + tw: float + x: float + + +ALL_ALUMINUM_CHANNEL_NAMES = ( + "CS 2 X 1.07", + "CS 2 X 0.577", + "CS 3 X 1.60", + "CS 4 X 1.74", + "CS 4 X 2.33", + "CS 5 X 3.09", + "CS 5 X 2.21", + "CS 6 X 4.03", + "CS 6 X 2.83", + "CS 7 X 4.72", + "CS 7 X 3.21", + "CS 8 X 5.79", + "CS 8 X 4.15", + "CS 10 X 6.14", + "CS 9 X 6.97", + "CS 9 X 4.98", + "CS 12 X 8.27", + "CS 10 X 8.36", + "CS 14 X 13.91", + "CS 12 X 11.8", + "C 3 X 1.42", + "C 2 X 1.22", + "CS 3 X 1.14", + "C 3 X 1.73", + "C 3 X 2.07", + "C 4 X 1.85", + "C 4 X 2.16", + "C 5 X 2.32", + "C 4 X 2.50", + "C 5 X 3.11", + "C 5 X 3.97", + "C 6 X 2.83", + "C 6 X 3.00", + "C 6 X 3.63", + "C 6 X 4.50", + "C 8 X 4.25", + "C 10 X 5.28", + "C 7 X 3.54", + "C 10 X 8.64", + "C 10 X 10.4", + "C 12 X 7.41", + "C 7 X 5.10", + "C 12 X 8.64", + "C 12 X 10.4", + "C 12 X 12.1", + "C 15 X 11.7", + "C 7 X 4.23", + "C 7 X 5.96", + "C 8 X 4.75", + "C 9 X 6.91", + "C 9 X 8.65", + "C 8 X 5.62", + "C 9 X 4.60", + "C 9 X 5.19", + "C 8 X 6.48", + "C 10 X 6.91", + "C 15 X 17.3", +) diff --git a/efficalc/sections/alum_circular.py b/efficalc/sections/alum_circular.py new file mode 100644 index 0000000..e490c08 --- /dev/null +++ b/efficalc/sections/alum_circular.py @@ -0,0 +1,310 @@ +import dataclasses + + +@dataclasses.dataclass +class AluminumCircular(object): + """This is a dataclass containing the properties of an Aluminum Circular section. + + :param A: Cross-sectional area (in^2) + :type A: float + :param I: Moment of inertia (in^4) + :type I: float + :param ID: Inner diameter (in) + :type ID: float + :param J: Torsional constant (in^4) + :type J: float + :param OD: Outer diameter (in) + :type OD: float + :param Rb_t: Ratio of bend radius to thickness = (OD + ID) / (4 * t) + :type Rb_t: float + :param S: Elastic section modulus (in^3) + :type S: float + :param Size: The section size name + :type Size: str + :param W: Nominal weight (lb/ft) + :type W: float + :param Z: Plastic section modulus (in^3) + :type Z: float + """ + + A: float + I: float + ID: float + J: float + OD: float + Rb_t: float + S: float + Size: str + W: float + Z: float + r: float + t: float + + +ALL_ALUMINUM_CIRCULAR_NAMES = ( + "HSS 1.5 x 0.062", + "HSS 1.5 x 0.094", + "HSS 1.5 x 0.156", + "HSS 1.5 x 0.125", + "HSS 1.5 x 0.188", + "HSS 1.5 x 0.25", + "HSS 1.625 x 0.125", + "HSS 1.625 x 0.188", + "HSS 1.625 x 0.25", + "HSS 1.75 x 0.125", + "HSS 1.75 x 0.25", + "HSS 1.5 x 0.375", + "HSS 1.75 x 0.375", + "HSS 1.875 x 0.125", + "HSS 1.75 x 0.188", + "HSS 1.875 x 0.188", + "HSS 1.875 x 0.25", + "HSS 1.875 x 0.375", + "HSS 2.25 x 0.5", + "HSS 2.375 x 0.188", + "HSS 2.375 x 0.25", + "HSS 2.375 x 0.375", + "HSS 2.375 x 0.5", + "HSS 2.5 x 0.125", + "HSS 2.5 x 0.188", + "HSS 2.5 x 0.25", + "HSS 2.5 x 0.312", + "HSS 2.5 x 0.375", + "HSS 2 x 0.5", + "HSS 2.5 x 0.5", + "HSS 2.5 x 0.75", + "HSS 2.5 x 0.625", + "HSS 2.625 x 0.25", + "HSS 2 x 0.125", + "HSS 2 x 0.188", + "HSS 2 x 0.375", + "HSS 2 x 0.25", + "HSS 2 x 0.312", + "HSS 2.25 x 0.312", + "HSS 2.75 x 0.188", + "HSS 2.25 x 0.125", + "HSS 2.75 x 0.125", + "HSS 2.25 x 0.25", + "HSS 2.75 x 0.25", + "HSS 2.75 x 0.375", + "HSS 2.25 x 0.375", + "HSS 2.75 x 0.625", + "HSS 2.75 x 0.5", + "HSS 2.875 x 0.25", + "HSS 2.75 x 0.75", + "HSS 2.75 x 0.312", + "HSS 2.875 x 0.5", + "HSS 3 x 0.125", + "HSS 3 x 0.188", + "HSS 3 x 0.25", + "HSS 2.25 x 0.188", + "HSS 3 x 0.375", + "HSS 3 x 0.5", + "HSS 3 x 0.625", + "HSS 3 x 0.75", + "HSS 3 x 1", + "HSS 3.25 x 0.25", + "HSS 3.25 x 0.5", + "HSS 3.25 x 0.375", + "HSS 3.5 x 0.125", + "HSS 3.5 x 0.188", + "HSS 3.5 x 0.312", + "HSS 3.5 x 0.25", + "HSS 3.5 x 0.5", + "HSS 3.5 x 0.375", + "HSS 3.75 x 0.5", + "HSS 4 x 0.125", + "HSS 3.75 x 0.375", + "HSS 3.75 x 0.188", + "HSS 3.75 x 0.25", + "HSS 3.75 x 0.125", + "HSS 4 x 0.312", + "HSS 4 x 0.625", + "HSS 4 x 0.188", + "HSS 4 x 0.25", + "HSS 4 x 0.5", + "HSS 4 x 0.75", + "HSS 4 x 0.375", + "HSS 4.25 x 0.125", + "HSS 4.25 x 0.25", + "HSS 4.25 x 0.375", + "HSS 4.25 x 0.5", + "HSS 4.5 x 0.125", + "HSS 4.5 x 0.25", + "HSS 4.5 x 0.188", + "HSS 4.5 x 0.312", + "HSS 4.5 x 0.375", + "HSS 4.5 x 0.5", + "HSS 4.5 x 0.625", + "HSS 4.5 x 0.75", + "HSS 4.5 x 1", + "HSS 4.75 x 0.125", + "HSS 4.75 x 0.188", + "HSS 4.75 x 0.25", + "HSS 4.75 x 0.375", + "HSS 3.5 x 0.75", + "HSS 4.75 x 0.5", + "HSS 5 x 0.188", + "HSS 5 x 0.25", + "HSS 5 x 0.125", + "HSS 5 x 0.312", + "HSS 5 x 0.375", + "HSS 5 x 0.5", + "HSS 6 x 0.188", + "HSS 6 x 0.25", + "HSS 6 x 0.312", + "HSS 6 x 0.375", + "HSS 6 x 0.5", + "HSS 6 x 0.625", + "HSS 5.5 x 0.25", + "HSS 6 x 0.75", + "HSS 6.5 x 0.25", + "HSS 6 x 1", + "HSS 5 x 1", + "HSS 6.5 x 0.375", + "HSS 6.5 x 0.5", + "HSS 6.75 x 0.5", + "HSS 6.75 x 0.75", + "HSS 5.5 x 0.125", + "HSS 6.5 x 0.75", + "HSS 5.5 x 0.188", + "HSS 5 x 0.625", + "HSS 5 x 0.75", + "HSS 5.5 x 0.75", + "HSS 5.5 x 1", + "HSS 5.5 x 0.375", + "HSS 6 x 0.125", + "HSS 7 x 0.375", + "HSS 5.5 x 0.5", + "HSS 7 x 0.25", + "HSS 7 x 1", + "HSS 7.5 x 0.25", + "HSS 7 x 0.5", + "HSS 7.5 x 0.5", + "HSS 8 x 0.25", + "HSS 8 x 0.125", + "HSS 7.5 x 0.375", + "HSS 8 x 0.375", + "HSS 8 x 0.625", + "HSS 8 x 0.5", + "HSS 8 x 0.75", + "HSS 8 x 1", + "HSS 7 x 0.75", + "HSS 8.5 x 0.25", + "HSS 9 x 0.25", + "HSS 9 x 0.5", + "HSS 10 x 0.25", + "HSS 10 x 0.375", + "HSS 10 x 0.5", + "HSS 10 x 0.625", + "HSS 10 x 0.75", + "HSS 10.5 x 0.25", + "HSS 10 x 1", + "HSS 10.5 x 0.375", + "HSS 10.5 x 0.5", + "HSS 10.5 x 0.75", + "HSS 11 x 0.5", + "HSS 11 x 0.375", + "HSS 11 x 0.75", + "HSS 9 x 0.375", + "HSS 11 x 1", + "HSS 12 x 0.25", + "HSS 12 x 0.375", + "HSS 12 x 0.5", + "HSS 12 x 0.75", + "HSS 12 x 1", + "PIPE 1/8 Sched 40", + "PIPE 1/8 Sched 80", + "PIPE 1/4 Sched 40", + "PIPE 1/4 Sched 80", + "PIPE 3/8 Sched 80", + "PIPE 1/2 Sched 5", + "PIPE 3/8 Sched 40", + "PIPE 1/2 Sched 10", + "PIPE 1/2 Sched 40", + "PIPE 1/2 Sched 80", + "PIPE 1/2 Sched 160", + "PIPE 3/4 Sched 5", + "PIPE 3/4 Sched 10", + "PIPE 3/4 Sched 40", + "PIPE 3/4 Sched 80", + "PIPE 3/4 Sched 160", + "PIPE 1 Sched 5", + "PIPE 1 Sched 40", + "PIPE 1 Sched 10", + "PIPE 1 Sched 80", + "PIPE 1 Sched 160", + "PIPE 1.25 Sched 5", + "PIPE 1.25 Sched 10", + "PIPE 1.25 Sched 40", + "PIPE 1.25 Sched 80", + "PIPE 1.25 Sched 160", + "PIPE 1.5 Sched 5", + "PIPE 1.5 Sched 10", + "PIPE 1.5 Sched 40", + "PIPE 1.5 Sched 80", + "PIPE 1.5 Sched 160", + "PIPE 2 Sched 5", + "PIPE 2 Sched 40", + "PIPE 2 Sched 80", + "PIPE 2 Sched 160", + "PIPE 2 Sched 10", + "PIPE 2.5 Sched 5", + "PIPE 2.5 Sched 10", + "PIPE 2.5 Sched 40", + "PIPE 2.5 Sched 160", + "PIPE 2.5 Sched 80", + "PIPE 3 Sched 5", + "PIPE 3 Sched 10", + "PIPE 3 Sched 40", + "PIPE 3 Sched 80", + "PIPE 3 Sched 160", + "PIPE 3.5 Sched 10", + "PIPE 3.5 Sched 5", + "PIPE 3.5 Sched 40", + "PIPE 3.5 Sched 80", + "PIPE 4 Sched 5", + "PIPE 4 Sched 80", + "PIPE 4 Sched 40", + "PIPE 4 Sched 10", + "PIPE 4 Sched 160", + "PIPE 4 Sched 120", + "PIPE 5 Sched 5", + "PIPE 5 Sched 10", + "PIPE 5 Sched 120", + "PIPE 5 Sched 40", + "PIPE 5 Sched 160", + "PIPE 6 Sched 5", + "PIPE 5 Sched 80", + "PIPE 6 Sched 10", + "PIPE 6 Sched 80", + "PIPE 6 Sched 120", + "PIPE 6 Sched 40", + "PIPE 6 Sched 160", + "PIPE 8 Sched 5", + "PIPE 8 Sched 10", + "PIPE 8 Sched 20", + "PIPE 8 Sched 30", + "PIPE 8 Sched 40", + "PIPE 8 Sched 60", + "PIPE 8 Sched 80", + "PIPE 8 Sched 120", + "PIPE 8 Sched 160", + "PIPE 10 Sched 5", + "PIPE 10 Sched 10", + "PIPE 8 Sched 140", + "PIPE 10 Sched 20", + "PIPE 10 Sched 30", + "PIPE 8 Sched 100", + "PIPE 10 Sched 60", + "PIPE 10 Sched 40", + "PIPE 10 Sched 80", + "PIPE 10 Sched 100", + "PIPE 12 Sched 5", + "PIPE 12 Sched 10", + "PIPE 12 Sched 20", + "PIPE 12 Sched 30", + "PIPE 12 Sched 40", + "PIPE 12 Sched 60", + "PIPE 12 Sched 80", +) diff --git a/efficalc/sections/alum_rectangular.py b/efficalc/sections/alum_rectangular.py new file mode 100644 index 0000000..c081587 --- /dev/null +++ b/efficalc/sections/alum_rectangular.py @@ -0,0 +1,174 @@ +import dataclasses + + +@dataclasses.dataclass +class AluminumRectangular(object): + """This is a dataclass containing the properties of an Aluminum Rectangular section, typically representing + rectangular HSS (Hollow Structural Section) properties. + + :param A: Cross-sectional area (in^2) + :type A: float + :param Ix: Moment of inertia about the x-axis (in^4) + :type Ix: float + :param Iy: Moment of inertia about the y-axis (in^4) + :type Iy: float + :param J: Torsional constant (in^4) + :type J: float + :param Size: The section size name + :type Size: str + :param Sx: Elastic section modulus about the x-axis (in^3) + :type Sx: float + :param Sy: Elastic section modulus about the y-axis (in^3) + :type Sy: float + :param Type: The type of section shape (e.g. American Standard, Aluminum Association, etc.) + :type Type: str + :param W: Nominal weight (lb/ft) + :type W: float + :param Zx: Plastic section modulus about the x-axis (in^4) + :type Zx: float + :param Zy: Plastic section modulus about the y-axis (in^4) + :type Zy: float + :param b: Width (in) + :type b: float + :param d: Depth (in) + :type d: float + :param rx: Radius of gyration about the x-axis (in) + :type rx: float + :param ry: Radius of gyration about the y-axis (in) + :type ry: float + :param t: Thickness (in) + :type t: float + """ + + A: float + Ix: float + Iy: float + J: float + Size: str + Sx: float + Sy: float + Type: str + W: float + Zx: float + Zy: float + b: float + d: float + rx: float + ry: float + t: float + + +ALL_ALUMINUM_RECTANGULAR_NAMES = ( + "RT 1 x 1 x 0.065", + "RT 1 x 1 x 0.125", + "RT 1.25 x 1.25 x 0.065", + "RT 1 x 1 x 0.0 95", + "RT 1.25 x 1.25 x 0.095", + "RT 1.25 x 1.25 x 0.125", + "RT 1.5 x 1.5 x 0.065", + "RT 1.375 x 1.375 x 0.125", + "RT 1.5 x 1.5 x 0.095", + "RT 1.5 x 1.5 x 0.078", + "RT 1.5 x 1.5 x 0.125", + "RT 1.5 x 1.5 x 0.25", + "RT 2 x 2 x 0.095", + "RT 2 x 2 x 0.125", + "RT 2 x 2 x 0.156", + "RT 2 x 2 x 0.25", + "RT 2.25 x 2.25 x 0.125", + "RT 2.5 x 2.5 x 0.125", + "RT 2.5 x 2.5 x 0.188", + "RT 2.5 x 2.5 x 0.25", + "RT 2.75 x 2.75 x 0.188", + "RT 2.75 x 2.75 x 0.125", + "RT 3 x 3 x 0.125", + "RT 3 x 3 x 0.095", + "RT 2 x 2 x 0.188", + "RT 3 x 3 x 0.188", + "RT 3 x 3 x 0.25", + "RT 3.5 x 3.5 x 0.125", + "RT 3 x 3 x 0.375", + "RT 3.5 x 3.5 x 0.25", + "RT 3.5 x 3.5 x 0.375", + "RT 4 x 4 x 0.125", + "RT 4 x 4 x 0.188", + "RT 4 x 4 x 0.25", + "RT 4 x 4 x 0.5", + "RT 4 x 4 x 0.375", + "RT 1.75 x 1.75 x 0.125", + "RT 5 x 5 x 0.125", + "RT 5 x 5 x 0.188", + "RT 5 x 5 x 0.25", + "RT 5 x 5 x 0.375", + "RT 6 x 6 x 0.188", + "RT 6 x 6 x 0.125", + "RT 6 x 6 x 0.25", + "RT 6 x 6 x 0.5", + "RT 8 x 8 x 0.188", + "RT 8 x 8 x 0.25", + "RT 8 x 8 x 0.375", + "RT 8 x 8 x 0.5", + "RT 1 1/2 x 1 x 1/8", + "RT 1 3/4 x 1 1/2 x 1/8", + "RT 2 x 1 x 1/8", + "RT 2 x 1 1/4 x 1/8", + "RT 2 x 1 1/2 x 1/8", + "RT 2 x 1 1/2 x 1/4", + "RT 2 x 1 3/4 x 1/8", + "RT 2 1/4 x 1 3/4 x 1/8", + "RT 2 1/2 x 1 x 1/8", + "RT 2 1/2 x 1 1/4 x 1/8", + "RT 2 1/2 x 1 1/2 x 1/8", + "RT 6 x 6 x 0.375", + "RT 2 1/2 x 1 3/4 x 1/8", + "RT 2 3/4 x 1 3/4 x 1/8", + "RT 3 x 1 x 1/8", + "RT 3 x 1 1/4 x 1/8", + "RT 3 x 1 1/2 x 1/8", + "RT 3 x 1 1/2 x 3/16", + "RT 3 x 1 3/4 x 1/8", + "RT 3 x 2 x 1 /8", + "RT 3 x 2 x 1/4", + "RT 3 1/2 x 1 3/4 x 1/8", + "RT 4 x 1 x 1/8", + "RT 4 x 1 1/2 x 1/8", + "RT 4 x 1 3/4 x 1/8", + "RT 4 x 2 x 3/16", + "RT 4 x 3 x 3/16", + "RT 4 x 2 x 1/4", + "RT 4 x 2 1/2 x 1/8", + "RT 4 x 2 x 1/8", + "RT 5 x 2 1/2 x 1/8", + "RT 5 x 3 x 1/8", + "RT 5 x 3 x 3/16", + "RT 4 1/2 x 1 3/4 x 1/8", + "RT 4 x 3 x 1/8", + "RT 4 x 3 x 1/4", + "RT 4 x 3 x 1/2", + "RT 4 x 3 x 3/8", + "RT 5 x 2 x 3/16", + "RT 5 x 3 x 1/4", + "RT 5 x 1 3/4 x 1/8", + "RT 5 x 2 x 1/4", + "RT 5 x 2 x 1/8", + "RT 6 x 1 1/2 x 1/8", + "RT 6 x 1 3/4 x 1/8", + "RT 5 x 1 3/4 x 3/ 16", + "RT 5 x 4 x 1/4", + "RT 6 x 2 x 3/16", + "RT 6 x 2 x 1/4", + "RT 6 x 3 x 3/16", + "RT 6 x 4 x 1/8", + "RT 6 x 3 x 1/8", + "RT 6 x 4 x 3/16", + "RT 6 x 4 x 1/4", + "RT 6 x 4 x 1/2", + "RT 8 x 2 x 1/8", + "RT 8 x 4 x 3/16", + "RT 8 x 3 x 1/4", + "RT 8 x 4 x 3/8", + "RT 8 x 4 x 1/4", + "RT 8 x 4 x 1/2", + "RT 6 x 2 x 1/8", + "RT 8 x 5 x 3/8", +) diff --git a/efficalc/sections/alum_wide_flange.py b/efficalc/sections/alum_wide_flange.py new file mode 100644 index 0000000..810e3bc --- /dev/null +++ b/efficalc/sections/alum_wide_flange.py @@ -0,0 +1,142 @@ +import dataclasses + + +@dataclasses.dataclass +class AluminumWideFlange(object): + """This is a dataclass containing the properties of an Aluminum Wide Flange section. + + :param A: Cross-sectional area (in^2) + :type A: float + :param Cw: Warping constant (in^6) + :type Cw: float + :param Ix: Moment of inertia about the x-axis (in^4) + :type Ix: float + :param Iy: Moment of inertia about the y-axis (in^4) + :type Iy: float + :param J: Torsional constant (in^4) + :type J: float + :param R1: Fillet radius (in) + :type R1: float + :param R2: Tip radius (in) + :type R2: float + :param Size: The section size name + :type Size: str + :param Sx: Elastic section modulus about the x-axis (in^3) + :type Sx: float + :param Sy: Elastic section modulus about the y-axis (in^3) + :type Sy: float + :param Type: The type of section shape (e.g. American Standard, Aluminum Association, etc.) + :type Type: str + :param W: Nominal weight (lb/ft) + :type W: float + :param Zx: Plastic section modulus about the x-axis (in^3) + :type Zx: float + :param Zy: Plastic section modulus about the y-axis (in^3) + :type Zy: float + :param b: Width (in) + :type b: float + :param d: Depth (in) + :type d: float + :param d1: Nominal depth between flange fillets (in) + :type d1: float + :param rx: Radius of gyration about the x-axis (in) + :type rx: float + :param ry: Radius of gyration about the y-axis (in) + :type ry: float + :param tf: Average flange thickness (in) + :type tf: float + :param tw: Thickness of the web (in) + :type tw: float + """ + + A: float + Cw: float + Ix: float + Iy: float + J: float + R1: float + R2: float + Size: str + Sx: float + Sy: float + Type: str + W: float + Zx: float + Zy: float + b: float + d: float + d1: float + rx: float + ry: float + tf: float + tw: float + + +ALL_ALUMINUM_WIDE_FLANGE_NAMES = ( + "I 3 x 1.64", + "I 10 x 10.3", + "I 12 x 11.7", + "I 12 x 14.3", + "I 14 x 16.0", + "I 6 x 4.03", + "I 3 x 2.03", + "WF 5 x 6.49", + "WF 6 x 4.16", + "WF 6 x 5.40", + "WF 6 x 7.85", + "WF 6 x 8.30", + "WF 6 x 9.18", + "WF 8 x 5.90", + "WF 8 x 8.32", + "WF 8 x 10.7", + "WF 8 x 11.2", + "WF 8 x 11.8", + "I 5 x 3.70", + "I 4 x 2.31", + "I 8 x 7.02", + "I 6 x 4.69", + "I 7 x 5.80", + "I 8 x 6.18", + "I 10 x 8.65", + "I 9 x 8.36", + "WF 2 x 1.43", + "WF 4 x 4.76", + "I 4 x 2.79", + "WF 8 x 13.0", + "WF 10 x 11.4", + "WF 10 x 7.30", + "WF 12 x 13.8", + "WF 12 x 18.3", + "WF(A-N) 2 x 0.928", + "WF(A-N) 3 x 0.769", + "WF(A-N) 3 x 1.00", + "WF(A-N) 4 x 1.14", + "WF(A-N) 4 x 1.79", + "WF(A-N) 4 x 2.35", + "WF(A-N) 4 x 3.06", + "WF(A-N) 4 x 4.14", + "WF(A-N) 5 x 5.36", + "S 4 x 3.28", + "S 3 x 1.96", + "S 4 x 2.64", + "S 3 x 2.59", + "S 6 x 4.30", + "S 5 x 4.23", + "S 5 x 3.43", + "S 8 x 6.35", + "S 6 x 5.10", + "S 6 x 5.96", + "S 5 x 5.10", + "S 7 x 6.05", + "S 8 x 8.81", + "S 10 x 8.76", + "S 9 x 7.51", + "S 8 x 7.96", + "S 12 x 11.0", + "S 10 x 10.4", + "S 10 x 12.1", + "S 12 x 12.1", + "S 12 x 14.1", + "S 12 x 15.6", + "S 12 x 17.3", +) diff --git a/efficalc/sections/csv/alum_shapes_L.csv b/efficalc/sections/csv/alum_shapes_L.csv index ff7f84b..074352d 100644 --- a/efficalc/sections/csv/alum_shapes_L.csv +++ b/efficalc/sections/csv/alum_shapes_L.csv @@ -1,4 +1,4 @@ -A,Ix,Iy,Iz,R1,R2,Size,Sx,Sy,Type,W,a,b,d,rx,ry,rz,t,x,y +A,Ix,Iy,Iz,R1,R2,Size,Sx,Sy,Type,W,alpha,b,d,rx,ry,rz,t,x,y 0.36,0.0745,0.0745,0.0282,0.188,0.125,L 1 1/2 x 1 1/2 x 1/8,0.0684,0.0684,Aluminum Association,0.42,45,1.5,1.5,0.455,0.455,0.28,0.125,0.411,0.411 0.423,0.121,0.121,0.0462,0.188,0.125,L 1 3/4 x 1 3/4 x 1/8,0.0948,0.0948,Aluminum Association,0.5,45,1.75,1.75,0.535,0.535,0.33,0.125,0.473,0.473 0.813,0.223,0.223,0.0904,0.188,0.125,L 1 3/4 x 1 3/4 x 1/4,0.182,0.182,Aluminum Association,0.96,45,1.75,1.75,0.523,0.523,0.333,0.25,0.524,0.524 diff --git a/efficalc/sections/csv/alum_shapes_circular.csv b/efficalc/sections/csv/alum_shapes_circular.csv index 8882d2a..8b21349 100644 --- a/efficalc/sections/csv/alum_shapes_circular.csv +++ b/efficalc/sections/csv/alum_shapes_circular.csv @@ -1,4 +1,4 @@ -A,I,ID,J,OD,Rblt,S,Size,W,Z,r,t +A,I,ID,J,OD,Rb_t,S,Size,W,Z,r,t 0.28,0.0725,1.376,0.145,1.5,11.6,0.097,HSS 1.5 x 0.062,0.329,0.128,0.509,0.062 0.415,0.103,1.312,0.205,1.5,7.5,0.137,HSS 1.5 x 0.094,0.488,0.186,0.498,0.094 0.659,0.151,1.188,0.297,1.5,4.3,0.201,HSS 1.5 x 0.156,0.775,0.283,0.478,0.156 diff --git a/efficalc/sections/csv/alum_shapes_wf.csv b/efficalc/sections/csv/alum_shapes_wf.csv index e60f46d..290ef71 100644 --- a/efficalc/sections/csv/alum_shapes_wf.csv +++ b/efficalc/sections/csv/alum_shapes_wf.csv @@ -1,4 +1,4 @@ -A,Cw,Ix,Iy,J,R1,R2,Size,Sx,Sy,Type,W,Zx,Zy,bf,d,d1,rx,ry,tf,tw +A,Cw,Ix,Iy,J,R1,R2,Size,Sx,Sy,Type,W,Zx,Zy,b,d,d1,rx,ry,tf,tw 1.39,1.02,2.24,0.522,0.019,0.25,0.01,I 3 x 1.64,1.49,0.418,Aluminum Association,1.64,1.62,0.63,2.5,3,2.1,1.27,0.613,0.2,0.13 8.75,407,156,18,0.62,0.4,0.01,I 10 x 10.3,31.2,6.01,Aluminum Association,10.3,34.4,9.095,6,10,8.2,4.22,1.44,0.5,0.29 9.92,894,256,26.9,0.621,0.4,0.01,I 12 x 11.7,42.6,7.69,Aluminum Association,11.7,46.8,11.631,7,12,10.26,5.07,1.65,0.47,0.29 diff --git a/efficalc/sections/section_properties.db b/efficalc/sections/section_properties.db index 065c75730a19ffc3e179aad3b5bb54cc00d60cff..05e2bf53f575ec2e2ab3b26ea4b9948beedcd65a 100644 GIT binary patch delta 135962 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 delta 1909 zcma)+TWl0n7{~X_oS8GT-FBuR7JH%FLZLtlTPPF=m*pNK(B&c&M7tn}+?P8vD0^mC z#2ek((iK#wX_|e|ht)M5g3@ea^vQ>fG5Wf1KA4z&zy}K{{=Xr_nD}6m^V@H}@ARDS z@}G&v+v53IshY|iKA-P4*3-WgeJu;&oOh(4!NshH6o`Kr>$S>flBdqvMV`i7z}{kA z(UQmUpeMUPkz5ZN$pxU9tONZ?11w9N1p|q0usrcRSdmx=T8UcFPMBb&LwhDU`@qT0 z7VsHoDLBQM0Y2+kV3q6vr^*B1bCUK{H^>hB)W}A#R+fWxmw;*ef%+GKhTM4luk}-d z6>0-pu&KSPy=&3@w#BCqnO?1SDCwbUHl=@JmG1H)dt{9k7`HP#&wwmph}kYrF~xTE zIGeCCJVxFqAwmdKJmkfLUfgE(2xoSVmlS(gFWNc1FkubzygkgbSVLGTtTF z1-ruI*4GLTWq8RuQ4vAb>;@vr&sO#94aj!Ra$qiXwxt zS%q>PQXX3wEv48QEskd`raZJpi%OZzmbihE@{?8DA?M(P#nJ{BT>#pnz(N|%0(bne zcWg|3WDSN@-PNP&gyr&xN@s^*XLv|O^+6Rk+SoN3S8?`6H;_{AS?wDnPVucj53D-| ztfgtJA%(1_p@r81tCj#OTY(icyXEfq9hFLna-0_7x}DL(7=<1p1W}o8^9gH&Q;j7R zt=EdUTr0qI^}Lr}mJ*5a!TRpK2@>i{&KYozC#l^|C+seVy12`k4eoTNfG;`-* zx63`?Hc9Q?D%-$_B!PBH67d!~Z1XF?Cil-?ZUxb64gHB|95T_+jT9lSA;FkNjf!g2 zs0ew(=7May@awdyhySuP;q%q|C{CGn5cHS^U5XfHMr=|xGp%9&w1}< zIOnLa&q|uz8F>UeEnfyt$p{#e)Z~+LF?d2!y~icJkjG>tcvN!m$VK4rQQ&nd>(Ex< zwROP34&VS?_(rY!(9%uR8W+@DK^aB`bfrfGjffBuNH{3GvJe{4>A9qPWnqrIpi@~o z2Bl@ej6OuuO~I?jyg+@|HywJHK5|xoX(t2@dY`fz&Q#a~ z4t=Zo9R_|V&w+jNFqo2i!Cpyfy)M^)AINs_eMyzRCu_iWWd-<-B;{VCe|`H9aCJBE z7Cw2+z9ik5m&g6CB!nHl`Q|Ie6{~~{{>MH%iPb3$_!!1$*rQQvsLM5YF)ZV zGiuo6RUUNt5v=(#NYqh>U)7WWU$7*W{&X>reJ2|{oc+W>joB@TiIBh)Mbh z!_-42sU%2V8{QG!M5JUOQZz_U1v)^_dv?~dhhXDX!xkR*95F8#_3Vfj3{x=VIf7oW zfCL6p28Rk^1*mV#DuKS+_+pvASxmYQ?wGf1>PVjbTQ#5KStqGnRi4P}w6-s~{i(u~ Zo_{3uouWDi|4UB&1OBl4;9E1G{td9l%3}Zk diff --git a/efficalc/sections/section_query.py b/efficalc/sections/section_query.py index a69187e..4a5b0ed 100644 --- a/efficalc/sections/section_query.py +++ b/efficalc/sections/section_query.py @@ -8,6 +8,11 @@ from efficalc.sections.aisc_rectangular import AiscRectangular from efficalc.sections.aisc_tee import AiscTee from efficalc.sections.aisc_wide_flange import AiscWideFlange +from efficalc.sections.alum_angle import AluminumAngle +from efficalc.sections.alum_channel import AluminumChannel +from efficalc.sections.alum_circular import AluminumCircular +from efficalc.sections.alum_rectangular import AluminumRectangular +from efficalc.sections.alum_wide_flange import AluminumWideFlange SECTIONS_DB_NAME = "section_properties.db" @@ -18,8 +23,14 @@ AISC_RECTANGULAR_TABLE = "aisc_rectangular" AISC_TEE_TABLE = "aisc_tee" AISC_WIDE_FLANGE_TABLE = "aisc_wide_flange" +ALUM_ANGLE_TABLE = "aluminum_angle" +ALUM_CHANNEL_TABLE = "aluminum_channel" +ALUM_CIRCULAR_TABLE = "aluminum_circular" +ALUM_RECTANGULAR_TABLE = "aluminum_rectangular" +ALUM_WIDE_FLANGE_TABLE = "aluminum_wide_flange" -SECTION_SIZE_NAME_COLUMN = "AISC_name" +AISC_SECTION_SIZE_NAME_COLUMN = "AISC_name" +ALUM_SECTION_SIZE_NAME_COLUMN = "Size" def get_aisc_angle(section_size: str) -> AiscAngle: @@ -32,7 +43,7 @@ def get_aisc_angle(section_size: str) -> AiscAngle: :rtype: `efficalc.sections.AiscAngle` :raises ValueError: If the specified section size cannot be found in the sections database. """ - row = _fetch_section_row(AISC_ANGLE_TABLE, section_size) + row = _fetch_aisc_section_row(AISC_ANGLE_TABLE, section_size) if row: return AiscAngle(**row) @@ -54,7 +65,7 @@ def get_aisc_channel(section_size: str) -> AiscChannel: :raises ValueError: If the specified section size cannot be found in the sections database. """ - row = _fetch_section_row(AISC_CHANNEL_TABLE, section_size) + row = _fetch_aisc_section_row(AISC_CHANNEL_TABLE, section_size) if row: return AiscChannel(**row) @@ -76,7 +87,7 @@ def get_aisc_circular(section_size: str) -> AiscCircular: :raises ValueError: If the specified section size cannot be found in the sections database. """ - row = _fetch_section_row(AISC_CIRCULAR_TABLE, section_size) + row = _fetch_aisc_section_row(AISC_CIRCULAR_TABLE, section_size) if row: return AiscCircular(**row) @@ -98,7 +109,7 @@ def get_aisc_double_angle(section_size: str) -> AiscDoubleAngle: :raises ValueError: If the specified section size cannot be found in the sections database. """ - row = _fetch_section_row(AISC_DOUBLE_ANGLE_TABLE, section_size) + row = _fetch_aisc_section_row(AISC_DOUBLE_ANGLE_TABLE, section_size) if row: return AiscDoubleAngle(**row) @@ -120,7 +131,7 @@ def get_aisc_rectangular(section_size: str) -> AiscRectangular: :raises ValueError: If the specified section size cannot be found in the sections database. """ - row = _fetch_section_row(AISC_RECTANGULAR_TABLE, section_size) + row = _fetch_aisc_section_row(AISC_RECTANGULAR_TABLE, section_size) if row: return AiscRectangular(**row) @@ -142,7 +153,7 @@ def get_aisc_tee(section_size: str) -> AiscTee: :raises ValueError: If the specified section size cannot be found in the sections database. """ - row = _fetch_section_row(AISC_TEE_TABLE, section_size) + row = _fetch_aisc_section_row(AISC_TEE_TABLE, section_size) if row: return AiscTee(**row) @@ -164,7 +175,7 @@ def get_aisc_wide_flange(section_size: str) -> AiscWideFlange: :raises ValueError: If the specified section size cannot be found in the sections database. """ - row = _fetch_section_row(AISC_WIDE_FLANGE_TABLE, section_size) + row = _fetch_aisc_section_row(AISC_WIDE_FLANGE_TABLE, section_size) if row: return AiscWideFlange(**row) @@ -174,7 +185,125 @@ def get_aisc_wide_flange(section_size: str) -> AiscWideFlange: ) -def _fetch_section_row(table: str, section_size: str): +def get_aluminum_angle(section_size: str) -> AluminumAngle: + """ + Fetches the properties of a specified Aluminum Angle section from the sections database and returns a + :class:`efficalc.sections.AluminumAngle` instance populated with these properties. + + :param section_size: The designation of the aluminum section size as the Size property defined in the database. + :type section_size: str + :return: An AluminumAngle populated with the properties of the specified section size. + :rtype: `efficalc.sections.AluminumAngle` + :raises ValueError: If the specified section size cannot be found in the sections database. + """ + + row = _fetch_aluminum_section_row(ALUM_ANGLE_TABLE, section_size) + + if row: + return AluminumAngle(**row) + else: + raise ValueError( + f"The aluminum angle section size named {section_size} could not be found." + ) + + +def get_aluminum_channel(section_size: str) -> AluminumChannel: + """ + Fetches the properties of a specified Aluminum Channel section from the sections database and returns a + :class:`efficalc.sections.AluminumChannel` instance populated with these properties. + + :param section_size: The designation of the aluminum section size as the Size property defined in the database. + :type section_size: str + :return: An AluminumChannel populated with the properties of the specified section size. + :rtype: `efficalc.sections.AluminumChannel` + :raises ValueError: If the specified section size cannot be found in the sections database. + """ + + row = _fetch_aluminum_section_row(ALUM_CHANNEL_TABLE, section_size) + + if row: + return AluminumChannel(**row) + else: + raise ValueError( + f"The aluminum channel section size named {section_size} could not be found." + ) + + +def get_aluminum_circular(section_size: str) -> AluminumCircular: + """ + Fetches the properties of a specified Aluminum Circular section from the sections database and returns a + :class:`efficalc.sections.AluminumCircular` instance populated with these properties. + + :param section_size: The designation of the aluminum section size as the Size property defined in the database. + :type section_size: str + :return: An AluminumCircular populated with the properties of the specified section size. + :rtype: `efficalc.sections.AluminumCircular` + :raises ValueError: If the specified section size cannot be found in the sections database. + """ + + row = _fetch_aluminum_section_row(ALUM_CIRCULAR_TABLE, section_size) + + if row: + return AluminumCircular(**row) + else: + raise ValueError( + f"The aluminum circular section size named {section_size} could not be found." + ) + + +def get_aluminum_rectangular(section_size: str) -> AluminumRectangular: + """ + Fetches the properties of a specified Aluminum Rectangular section from the sections database and returns a + :class:`efficalc.sections.AluminumRectangular` instance populated with these properties. + + :param section_size: The designation of the aluminum section size as the Size property defined in the database. + :type section_size: str + :return: An AluminumRectangular populated with the properties of the specified section size. + :rtype: `efficalc.sections.AluminumRectangular` + :raises ValueError: If the specified section size cannot be found in the sections database. + """ + + row = _fetch_aluminum_section_row(ALUM_RECTANGULAR_TABLE, section_size) + + if row: + return AluminumRectangular(**row) + else: + raise ValueError( + f"The aluminum rectangular section size named {section_size} could not be found." + ) + + +def get_aluminum_wide_flange(section_size: str) -> AluminumWideFlange: + """ + Fetches the properties of a specified Aluminum Wide Flange section from the sections database and returns a + :class:`efficalc.sections.AluminumWideFlange` instance populated with these properties. + + :param section_size: The designation of the aluminum section size as the Size property defined in the database. + :type section_size: str + :return: An AluminumWideFlange populated with the properties of the specified section size. + :rtype: `efficalc.sections.AluminumWideFlange` + :raises ValueError: If the specified section size cannot be found in the sections database. + """ + + row = _fetch_aluminum_section_row(ALUM_WIDE_FLANGE_TABLE, section_size) + + if row: + return AluminumWideFlange(**row) + else: + raise ValueError( + f"The aluminum wide flange section size named {section_size} could not be found." + ) + + +def _fetch_aisc_section_row(table: str, section_size: str): + return _fetch_section_row(table, section_size, AISC_SECTION_SIZE_NAME_COLUMN) + + +def _fetch_aluminum_section_row(table: str, section_size: str): + return _fetch_section_row(table, section_size, ALUM_SECTION_SIZE_NAME_COLUMN) + + +def _fetch_section_row(table: str, section_size: str, size_field_name: str): database_dir = os.path.dirname(os.path.abspath(__file__)) db_path = os.path.join(database_dir, SECTIONS_DB_NAME) conn = sqlite3.connect(db_path) @@ -184,7 +313,7 @@ def _fetch_section_row(table: str, section_size: str): cursor = conn.cursor() cursor.execute( - f"SELECT * FROM {table} WHERE {SECTION_SIZE_NAME_COLUMN} = ?;", + f"SELECT * FROM {table} WHERE {size_field_name} = ?;", (section_size,), ) row = cursor.fetchone() diff --git a/pythonize_csv.py b/pythonize_csv.py deleted file mode 100644 index fae8bd4..0000000 --- a/pythonize_csv.py +++ /dev/null @@ -1,35 +0,0 @@ -import csv - -from efficalc.sections.aisc_wide_flange import AiscWideFlange - - -def get_all_sizes_from_csv(filepath, output_filepath): - """ - This method reads the csv section property files and generates a Python list of all the section size names from the - AISC_name column. - - :param filepath: the path to the csv file - :type filepath: str - :param output_filepath: the path to the output Python file - :type output_filepath: str - :return: None - """ - contents = "" - contents += "(" - with open(filepath, mode="r", encoding="utf-8") as file: - reader = csv.DictReader(file) - for row in reader: - contents += f'"{row["AISC_name"]}",\n' - - contents += ")" - - # Write the string to a Python file as a static variable - with open(output_filepath, "w", encoding="utf-8") as py_file: - py_file.write(contents) - - -if __name__ == "__main__": - get_all_sizes_from_csv( - r"efficalc\sections\csv\aisc_sections_WF.csv", - r"all_sections.py", - ) diff --git a/tests/sections/test_section_query.py b/tests/sections/test_section_query.py index 7a326de..71acfea 100644 --- a/tests/sections/test_section_query.py +++ b/tests/sections/test_section_query.py @@ -8,6 +8,11 @@ get_aisc_rectangular, get_aisc_tee, get_aisc_wide_flange, + get_aluminum_angle, + get_aluminum_channel, + get_aluminum_circular, + get_aluminum_rectangular, + get_aluminum_wide_flange, ) @@ -100,3 +105,76 @@ def test_wide_flange_lookup_size_not_found(): with pytest.raises(ValueError) as e_info: get_aisc_wide_flange("NOT_FOUND") assert "section size named NOT_FOUND could not be found" in str(e_info.value) + + +def test_aluminum_angle_lookup(): + section = get_aluminum_angle("L 3 x 2 x 3/8") + assert section.d == 3 + assert section.b == 2 + assert section.t == 0.375 + assert section.A == 1.74 + assert section.W == 2.05 + + +def test_aluminum_angle_lookup_size_not_found(): + with pytest.raises(ValueError) as e_info: + get_aluminum_angle("NOT_FOUND") + assert "section size named NOT_FOUND could not be found" in str(e_info.value) + + +def test_aluminum_channel_lookup(): + section = get_aluminum_channel("C 4 X 2.50") + assert section.d == 4 + assert section.W == 2.50 + assert section.A == 2.13 + assert section.b == 1.72 + + +def test_aluminum_channel_lookup_size_not_found(): + with pytest.raises(ValueError) as e_info: + get_aluminum_channel("NOT_FOUND") + assert "section size named NOT_FOUND could not be found" in str(e_info.value) + + +def test_aluminum_circular_lookup(): + section = get_aluminum_circular("HSS 6 x 0.25") + assert section.OD == 6 + assert section.t == 0.25 + assert section.ID == 5.5 + assert section.W == 5.31 + assert section.A == 4.52 + + +def test_aluminum_circular_lookup_size_not_found(): + with pytest.raises(ValueError) as e_info: + get_aluminum_circular("NOT_FOUND") + assert "section size named NOT_FOUND could not be found" in str(e_info.value) + + +def test_aluminum_rectangular_lookup(): + section = get_aluminum_rectangular("RT 6 x 2 x 1/4") + assert section.d == 6 + assert section.b == 2 + assert section.t == 0.25 + assert section.W == 4.41 + assert section.A == 3.75 + + +def test_aluminum_rectangular_lookup_size_not_found(): + with pytest.raises(ValueError) as e_info: + get_aluminum_rectangular("NOT_FOUND") + assert "section size named NOT_FOUND could not be found" in str(e_info.value) + + +def test_aluminum_wide_flange_lookup(): + section = get_aluminum_wide_flange("WF 6 x 8.30") + assert section.d == 6 + assert section.W == 8.30 + assert section.b == 6 + assert section.A == 7.06 + + +def test_aluminum_wide_flange_lookup_size_not_found(): + with pytest.raises(ValueError) as e_info: + get_aluminum_wide_flange("NOT_FOUND") + assert "section size named NOT_FOUND could not be found" in str(e_info.value) diff --git a/utils/__init__.py b/utils/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/build_section_tables.py b/utils/build_section_tables.py similarity index 57% rename from build_section_tables.py rename to utils/build_section_tables.py index 82022ec..0f2d402 100644 --- a/build_section_tables.py +++ b/utils/build_section_tables.py @@ -1,5 +1,5 @@ """ -This script can be used to build the sections database that is packaged with the library. It reads the individual CSV +This script creates a clean build of the sections database packaged with the library. It reads the individual CSV files for each section type and writes them to a table in the sections database with an index on the AISC_name column. """ @@ -17,10 +17,20 @@ AISC_RECTANGULAR_TABLE, AISC_TEE_TABLE, AISC_WIDE_FLANGE_TABLE, + ALUM_ANGLE_TABLE, + ALUM_CHANNEL_TABLE, + ALUM_CIRCULAR_TABLE, + ALUM_RECTANGULAR_TABLE, + ALUM_SECTION_SIZE_NAME_COLUMN, + ALUM_WIDE_FLANGE_TABLE, SECTIONS_DB_NAME, ) -if __name__ == "__main__": + +def clean_build_section_tables(): + + print("Building section tables...") + conn = sqlite3.connect(f"efficalc/sections/{SECTIONS_DB_NAME}") df = pd.read_csv("efficalc/sections/csv/aisc_sections_2L.csv") @@ -65,4 +75,40 @@ f"CREATE UNIQUE INDEX unique_aisc_name_{AISC_WIDE_FLANGE_TABLE} ON {AISC_WIDE_FLANGE_TABLE}(AISC_name);" ) + df = pd.read_csv("efficalc/sections/csv/alum_shapes_channel.csv") + df.to_sql(ALUM_CHANNEL_TABLE, conn, if_exists="replace", index=False) + conn.execute( + f"CREATE UNIQUE INDEX unique_alum_name_{ALUM_CHANNEL_TABLE} ON {ALUM_CHANNEL_TABLE}({ALUM_SECTION_SIZE_NAME_COLUMN});" + ) + + df = pd.read_csv("efficalc/sections/csv/alum_shapes_circular.csv") + df.to_sql(ALUM_CIRCULAR_TABLE, conn, if_exists="replace", index=False) + conn.execute( + f"CREATE UNIQUE INDEX unique_alum_name_{ALUM_CIRCULAR_TABLE} ON {ALUM_CIRCULAR_TABLE}({ALUM_SECTION_SIZE_NAME_COLUMN});" + ) + + df = pd.read_csv("efficalc/sections/csv/alum_shapes_L.csv") + df.to_sql(ALUM_ANGLE_TABLE, conn, if_exists="replace", index=False) + conn.execute( + f"CREATE UNIQUE INDEX unique_alum_name_{ALUM_ANGLE_TABLE} ON {ALUM_ANGLE_TABLE}({ALUM_SECTION_SIZE_NAME_COLUMN});" + ) + + df = pd.read_csv("efficalc/sections/csv/alum_shapes_rectangular.csv") + df.to_sql(ALUM_RECTANGULAR_TABLE, conn, if_exists="replace", index=False) + conn.execute( + f"CREATE UNIQUE INDEX unique_alum_name_{ALUM_RECTANGULAR_TABLE} ON {ALUM_RECTANGULAR_TABLE}({ALUM_SECTION_SIZE_NAME_COLUMN});" + ) + + df = pd.read_csv("efficalc/sections/csv/alum_shapes_wf.csv") + df.to_sql(ALUM_WIDE_FLANGE_TABLE, conn, if_exists="replace", index=False) + conn.execute( + f"CREATE UNIQUE INDEX unique_alum_name_{ALUM_WIDE_FLANGE_TABLE} ON {ALUM_WIDE_FLANGE_TABLE}({ALUM_SECTION_SIZE_NAME_COLUMN});" + ) + conn.close() + + print("Section tables built successfully.") + + +if __name__ == "__main__": + clean_build_section_tables() diff --git a/utils/pythonize_csv.py b/utils/pythonize_csv.py new file mode 100644 index 0000000..da6199c --- /dev/null +++ b/utils/pythonize_csv.py @@ -0,0 +1,58 @@ +import csv + + +def get_all_aisc_sizes_from_csv(filepath, output_filepath): + """ + This method reads the csv section property files and generates a Python list of all the section size names from the + AISC_name column. + + :param filepath: the path to the csv file + :type filepath: str + :param output_filepath: the path to the output Python file + :type output_filepath: str + :return: None + """ + contents = "" + contents += "(" + with open(filepath, mode="r", encoding="utf-8") as file: + reader = csv.DictReader(file) + for row in reader: + contents += f'"{row["AISC_name"]}",\n' + + contents += ")" + + # Write the string to a Python file as a static variable + with open(output_filepath, "w", encoding="utf-8") as py_file: + py_file.write(contents) + + +def get_all_alum_sizes_from_csv(filepath, output_filepath): + """ + This method reads the csv section property files and generates a Python list of all the section size names from the + Size column. + + :param filepath: the path to the csv file + :type filepath: str + :param output_filepath: the path to the output Python file + :type output_filepath: str + :return: None + """ + contents = "" + contents += "(" + with open(filepath, mode="r", encoding="utf-8") as file: + reader = csv.DictReader(file) + for row in reader: + contents += f'"{row["Size"]}",\n' + + contents += ")" + + # Write the string to a Python file as a static variable + with open(output_filepath, "w", encoding="utf-8") as py_file: + py_file.write(contents) + + +if __name__ == "__main__": + get_all_alum_sizes_from_csv( + r"efficalc\sections\csv\alum_shapes_wf.csv", + r"utils\all_sections.py", + )