Skip to content

Commit

Permalink
Goals are now handled separately from the core truss functionality an…
Browse files Browse the repository at this point in the history
…d integrated only for reporting
  • Loading branch information
cmccomb committed Dec 29, 2023
1 parent 8173384 commit 59bd16c
Show file tree
Hide file tree
Showing 5 changed files with 118 additions and 131 deletions.
58 changes: 30 additions & 28 deletions tests/test_construction_and_io.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,14 @@

class TestSequenceFunctions(unittest.TestCase):
def test_build_methods(self):
goals = trussme.Goals(
minimum_fos_buckling=1.5,
minimum_fos_total=1.5,
minimum_fos_yielding=1.5,
maximum_mass=5.0,
maximum_deflection=6e-3,
)

# Build truss from scratch
t1 = trussme.Truss()
t1.add_pinned_support([0.0, 0.0, 0.0])
Expand Down Expand Up @@ -51,21 +59,12 @@ def test_build_methods(self):
t1.add_member(4, 10)
t1.add_member(10, 5)

t1.minimum_fos_buckling = 1.5
t1.min_fos_yielding = 1.5
t1.max_mass = 5.0
t1.maximum_deflection = 6e-3

# Build truss from file
t2 = trussme.read_trs(TEST_TRUSS_FILENAME)
t2.minimum_fos_buckling = 1.5
t2.min_fos_yielding = 1.5
t2.max_mass = 5.0
t2.maximum_deflection = 6e-3

# Save reports
t1.report_to_md(os.path.join(os.path.dirname(__file__), "report_1.md"))
t2.report_to_md(os.path.join(os.path.dirname(__file__), "report_2.md"))
t1.report_to_md(os.path.join(os.path.dirname(__file__), "report_1.md"), goals)
t2.report_to_md(os.path.join(os.path.dirname(__file__), "report_2.md"), goals)

# Test for sameness
file_are_the_same = filecmp.cmp(
Expand All @@ -79,25 +78,24 @@ def test_build_methods(self):
os.remove(os.path.join(os.path.dirname(__file__), "report_2.md"))

def test_save_to_trs_and_rebuild(self):
goals = trussme.Goals(
minimum_fos_buckling=1.5,
minimum_fos_total=1.5,
minimum_fos_yielding=1.5,
maximum_mass=5.0,
maximum_deflection=6e-3,
)

# Build truss from file
t2 = trussme.read_trs(TEST_TRUSS_FILENAME)
t2.minimum_fos_buckling = 1.5
t2.min_fos_yielding = 1.5
t2.max_mass = 5.0
t2.maximum_deflection = 6e-3

# Save
t2.report_to_md(os.path.join(os.path.dirname(__file__), "report_2.md"))
t2.report_to_md(os.path.join(os.path.dirname(__file__), "report_2.md"), goals)
t2.to_trs(os.path.join(os.path.dirname(__file__), "asdf.trs"))

# Rebuild
t3 = trussme.read_trs(os.path.join(os.path.dirname(__file__), "asdf.trs"))
t3.minimum_fos_buckling = 1.5
t3.min_fos_yielding = 1.5
t3.max_mass = 5.0
t3.maximum_deflection = 6e-3

t3.report_to_md(os.path.join(os.path.dirname(__file__), "report_3.md"))
t3.report_to_md(os.path.join(os.path.dirname(__file__), "report_3.md"), goals)

with open(os.path.join(os.path.dirname(__file__), "report_3.md")) as f:
print(f.read())
Expand All @@ -112,15 +110,19 @@ def test_save_to_trs_and_rebuild(self):
self.assertTrue(file_are_the_same)

def test_save_to_json_and_rebuild(self):
goals = trussme.Goals(
minimum_fos_buckling=1.5,
minimum_fos_total=1.5,
minimum_fos_yielding=1.5,
maximum_mass=5.0,
maximum_deflection=6e-3,
)

# Build truss from file
t2 = trussme.read_trs(TEST_TRUSS_FILENAME)
t2.minimum_fos_buckling = 1.5
t2.min_fos_yielding = 1.5
t2.max_mass = 5.0
t2.maximum_deflection = 6e-3

# Save
t2.report_to_md(os.path.join(os.path.dirname(__file__), "report_4.md"))
t2.report_to_md(os.path.join(os.path.dirname(__file__), "report_4.md"), goals)
t2.to_trs(os.path.join(os.path.dirname(__file__), "asdf.json"))

# Rebuild
Expand All @@ -130,7 +132,7 @@ def test_save_to_json_and_rebuild(self):
t3.max_mass = 5.0
t3.maximum_deflection = 6e-3

t3.report_to_md(os.path.join(os.path.dirname(__file__), "report_5.md"))
t3.report_to_md(os.path.join(os.path.dirname(__file__), "report_5.md"), goals)

with open(os.path.join(os.path.dirname(__file__), "report_5.md")) as f:
print(f.read())
Expand Down
21 changes: 18 additions & 3 deletions tests/test_custom_materials_and_shapes.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,15 @@ def test_custom_material(self):
truss.add_member(1, 2, material=unobtanium)
truss.add_member(2, 0, material=unobtanium)

print(truss.report)
goals = trussme.Goals(
minimum_fos_buckling=1.5,
minimum_fos_total=1.5,
minimum_fos_yielding=1.5,
maximum_mass=5.0,
maximum_deflection=6e-3,
)

print(truss.report(goals))

def test_custom_shape(self):
# Build truss from scratch
Expand All @@ -36,7 +44,6 @@ def test_custom_shape(self):
truss.joints[1].loads[1] = -20000

class MagicalRod(trussme.Shape):

def __init__(self):
self.h = None
self.w = None
Expand All @@ -56,4 +63,12 @@ def name(self) -> str:
truss.add_member(1, 2, shape=MagicalRod())
truss.add_member(2, 0, shape=MagicalRod())

print(truss.report)
goals = trussme.Goals(
minimum_fos_buckling=1.5,
minimum_fos_total=1.5,
minimum_fos_yielding=1.5,
maximum_mass=5.0,
maximum_deflection=6e-3,
)

print(truss.report(goals))
2 changes: 1 addition & 1 deletion trussme/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@
"""

from .components import material_library, Shape, Material, Box, Pipe, Bar, Square
from .truss import Truss, read_trs
from .truss import Truss, read_trs, read_json, Goals
90 changes: 52 additions & 38 deletions trussme/report.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,16 @@
import trussme.visualize


def generate_summary(truss) -> str:
def generate_summary(truss, goals) -> str:
"""
Generate a summary of the analysis.
Parameters
----------
truss: Truss
The truss to be summarized
goals: Goals
The goals against which to evaluate the truss
Returns
-------
Expand All @@ -31,27 +33,27 @@ def generate_summary(truss) -> str:

success_string = []
failure_string = []
if truss.minimum_fos_total < truss.fos_total:
if goals.minimum_fos_total < truss.fos_total:
success_string.append("total FOS")
else:
failure_string.append("total FOS")

if truss.minimum_fos_buckling < truss.fos_buckling:
if goals.minimum_fos_buckling < truss.fos_buckling:
success_string.append("buckling FOS")
else:
failure_string.append("buckling FOS")

if truss.minimum_fos_yielding < truss.fos_yielding:
if goals.minimum_fos_yielding < truss.fos_yielding:
success_string.append("yielding FOS")
else:
failure_string.append("yielding FOS")

if truss.maximum_mass > truss.mass:
if goals.maximum_mass > truss.mass:
success_string.append("mass")
else:
failure_string.append("mass")

if truss.maximum_deflection > truss.deflection:
if goals.maximum_deflection > truss.deflection:
success_string.append("deflection")
else:
failure_string.append("deflection")
Expand Down Expand Up @@ -106,37 +108,37 @@ def generate_summary(truss) -> str:
]
data.append(
[
truss.minimum_fos_total,
goals.minimum_fos_total,
truss.fos_total,
"Yes" if truss.fos_total > truss.minimum_fos_total else "No",
"Yes" if truss.fos_total > goals.minimum_fos_total else "No",
]
)
data.append(
[
truss.minimum_fos_buckling,
goals.minimum_fos_buckling,
truss.fos_buckling,
"Yes" if truss.fos_buckling > truss.minimum_fos_buckling else "No",
"Yes" if truss.fos_buckling > goals.minimum_fos_buckling else "No",
]
)
data.append(
[
truss.minimum_fos_yielding,
goals.minimum_fos_yielding,
truss.fos_yielding,
"Yes" if truss.fos_yielding > truss.minimum_fos_yielding else "No",
"Yes" if truss.fos_yielding > goals.minimum_fos_yielding else "No",
]
)
data.append(
[
truss.maximum_mass,
goals.maximum_mass,
truss.mass,
"Yes" if truss.mass < truss.maximum_mass else "No",
"Yes" if truss.mass < goals.maximum_mass else "No",
]
)
data.append(
[
truss.maximum_deflection,
goals.maximum_deflection,
truss.deflection,
"Yes" if truss.deflection < truss.maximum_deflection else "No",
"Yes" if truss.deflection < goals.maximum_deflection else "No",
]
)

Expand All @@ -152,24 +154,29 @@ def generate_summary(truss) -> str:
return summary


def generate_instantiation_information(the_truss) -> str:
def generate_instantiation_information(truss) -> str:
"""
Generate a summary of the instantiation information.
:param the_truss: The truss to be reported on
:type the_truss: Truss
:return: A report of the instantiation information
:rtype: str
Parameters
----------
truss: Truss
The truss to be reported on
Returns
-------
str
A report of the instantiation information
"""
instantiation = "# INSTANTIATION INFORMATION\n"

instantiation += trussme.visualize.plot_truss(the_truss) + "\n"
instantiation += trussme.visualize.plot_truss(truss) + "\n"

# Print joint information
instantiation += "## JOINTS\n"
data = []
rows = []
for j in the_truss.joints:
for j in truss.joints:
rows.append("Joint_" + "{0:02d}".format(j.idx))
data.append(
[
Expand All @@ -192,7 +199,7 @@ def generate_instantiation_information(the_truss) -> str:
instantiation += "\n## MEMBERS\n"
data = []
rows = []
for m in the_truss.members:
for m in truss.members:
rows.append("Member_" + "{0:02d}".format(m.idx))
data.append(
[
Expand Down Expand Up @@ -228,7 +235,7 @@ def generate_instantiation_information(the_truss) -> str:
instantiation += "\n## MATERIALS\n"
data = []
rows = []
for mat in the_truss.materials:
for mat in truss.materials:
rows.append(mat["name"])
data.append(
[
Expand All @@ -251,22 +258,29 @@ def generate_instantiation_information(the_truss) -> str:
return instantiation


def generate_stress_analysis(the_truss) -> str:
def generate_stress_analysis(truss, goals) -> str:
"""
Generate a summary of the stress analysis information.
:param the_truss: The truss to be reported on
:type the_truss: Truss
:return: A report of the stress analysis information
:rtype: str
Parameters
----------
truss: Truss
The truss to be reported on
goals: Goals
The goals against which to evaluate the truss
Returns
-------
str
A report of the stress analysis information
"""
analysis = "# STRESS ANALYSIS INFORMATION\n"

# Print information about loads
analysis += "## LOADING\n"
data = []
rows = []
for j in the_truss.joints:
for j in truss.joints:
rows.append("Joint_" + "{0:02d}".format(j.idx))
data.append(
[
Expand All @@ -288,7 +302,7 @@ def generate_stress_analysis(the_truss) -> str:
analysis += "\n## REACTIONS\n"
data = []
rows = []
for j in the_truss.joints:
for j in truss.joints:
rows.append("Joint_" + "{0:02d}".format(j.idx))
data.append(
[
Expand All @@ -314,18 +328,18 @@ def generate_stress_analysis(the_truss) -> str:
analysis += "\n## FORCES AND STRESSES\n"
data = []
rows = []
for m in the_truss.members:
for m in truss.members:
rows.append("Member_" + "{0:02d}".format(m.idx))
data.append(
[
m.area,
format(m.moment_of_inertia, ".2e"),
format(m.force / pow(10, 3), ".2f"),
m.fos_yielding,
"Yes" if m.fos_yielding > the_truss.minimum_fos_yielding else "No",
"Yes" if m.fos_yielding > goals.minimum_fos_yielding else "No",
m.fos_buckling if m.fos_buckling > 0 else "N/A",
"Yes"
if m.fos_buckling > the_truss.minimum_fos_buckling or m.fos_buckling < 0
if m.fos_buckling > goals.minimum_fos_buckling or m.fos_buckling < 0
else "No",
]
)
Expand All @@ -347,11 +361,11 @@ def generate_stress_analysis(the_truss) -> str:
# Print information about members
analysis += "\n## DEFLECTIONS\n"

analysis += trussme.visualize.plot_truss(the_truss, deflected_shape=True) + "\n"
analysis += trussme.visualize.plot_truss(truss, deflected_shape=True) + "\n"

data = []
rows = []
for j in the_truss.joints:
for j in truss.joints:
rows.append("Joint_" + "{0:02d}".format(j.idx))
data.append(
[
Expand All @@ -365,7 +379,7 @@ def generate_stress_analysis(the_truss) -> str:
if j.translation[2] == 0.0
else "N/A",
"Yes"
if numpy.linalg.norm(j.deflections) < the_truss.maximum_deflection
if numpy.linalg.norm(j.deflections) < goals.maximum_deflection
else "No",
]
)
Expand Down
Loading

0 comments on commit 59bd16c

Please sign in to comment.