Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
/.nox/
/.python-version
/.pytype/
/.vscode/
/dist/
/docs/_build/
/src/*.egg-info/
Expand Down
29 changes: 29 additions & 0 deletions src/hyperstruct/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,35 @@
__version__ = version("hyperstruct")


def composite_cg(masses: List[Tuple[float, float]]) -> Tuple[float, float]:
"""Calculate the cg of a combined set of masses, along a single axis.

The composite cg of a collection of objects can be computed if the
masses and cg locations are known. The composite location along the
length is determined by summing the moments about a reference point.

Note that unit consitency is assumed. Weights can be provided in lieu
of masses, if the user is consistently using weight densities and weight
values across the rest of the design.

Args:
masses: A 2-tuple list of masses (w, x),
where w is the mass, and x is the location.

Returns:
a tuple of the composite mass and cg
"""
moments = []
total_weight = 0
for w, x in masses:
moments.append(w * x)
total_weight += w

cg = np.sum(moments) / total_weight

return (total_weight, cg)


@dataclass
class Material:
"""The HyperStruct material model.
Expand Down
4 changes: 3 additions & 1 deletion src/hyperstruct/fuselage.py
Original file line number Diff line number Diff line change
Expand Up @@ -1424,7 +1424,7 @@ class MajorFrame(Component):
min_gauge: float = field(default=0.040, metadata={"unit": "inch"})
"""Manufacturing requirement for minimum gauge thickness, default 0.040[in]."""

def show(self, show_coords: bool = False, save: bool = False) -> None:
def show(self, show_coords: bool = False, save: bool = False) -> Tuple[Any, Any]:
"""Plot the frame and applied loads."""
if show_coords:
# coords = [(row[5], row[6]) for row in self.cuts]
Expand Down Expand Up @@ -1537,6 +1537,8 @@ def show(self, show_coords: bool = False, save: bool = False) -> None:

plt.show()

return fig, ax

def synthesis(self, num: int = 60) -> None:
"""Controls the frame weight estimating process. [FFRME].

Expand Down
129 changes: 75 additions & 54 deletions tests/test_major_frames.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
"""Test cases for the MajorFrame and Bulkhead classes."""

from copy import copy

# import matplotlib.pyplot as plt
import numpy as np
import pandas as pd

# import pandas as pd
import pytest
import pytest_check as pycheck

from hyperstruct import Material
from hyperstruct import Station
from hyperstruct import composite_cg
from hyperstruct.fuselage import Bulkhead
from hyperstruct.fuselage import MajorFrame

Expand Down Expand Up @@ -79,7 +84,6 @@ def test_bulkhead(aluminum: Material) -> None:


if __name__ == "__main__": # pragma: no cover
# This snippet just verifies the show method and get_coords.
material = Material(
rho=0.101,
E=10.5e6,
Expand All @@ -95,64 +99,81 @@ def test_bulkhead(aluminum: Material) -> None:
db_r=116,
)

obj = Station(
orientation="FS",
name="Rounded Rectangle",
number=1728.0,
width=60.0,
depth=50.0,
vertical_centroid=30.0,
radius=24.61,
)
cir = Station(
orientation="FS",
name="Circle",
number=1700.0,
width=50.0,
depth=50.0,
vertical_centroid=30.0,
radius=25.0,
)

frm = MajorFrame(
material=material,
fs_loc=cir.number,
loads=np.array(
loads_list = [
np.array( # FS420
[
# y, z, V, H, M
[15.0, 56.5, -20876.0, 0.0, 0.0],
[-15.0, 56.5, -20876.0, 0.0, 0.0],
[15.5, 26, -2500.0, 0.0, 32441.0],
[-15.5, 26, -2500.0, 0.0, -32441.0],
[5.0, 10, 1750.0, 0.0, 0.0],
[-5.0, 10, 1750.0, 0.0, 0.0],
]
),
geom=cir,
fd=6.0,
)
print(f"Upper Panel = {frm.geom.upper_panel:.3f}")
print(f" Side Panel = {frm.geom.side_panel:.3f}")
print(f"Lower Panel = {frm.geom.lower_panel:.3f}")

# Check coords
# y, z = frm.geom.get_coords(np.radians(130), debug=True)
# print(f"In quadrant 4: y={y:.1f}, z={z:.1f}\n")
# coords = [(y, z)]

# y, z = frm.geom.get_coords(np.radians(225), debug=True)
# print(f"In quadrant 3: y={y:.1f}, z={z:.1f}")
# coords = [(y, z)]
np.array( # FS517
[
# y, z, V, H, M
[15.5, 56.5, 10472.0, 0.0, 0.0],
[-15.5, 56.5, 10472.0, 0.0, 0.0],
]
),
np.array( # FS720
[
# y, z, V, H, M
[18.0, 56.5, -20876.0, 0.0, 41.2e4],
[-18.0, 56.5, -20876.0, 0.0, 41.2e4],
]
),
np.array( # FS872
[
# y, z, V, H, M
[2.0, 47.0, 0.0, 0.0, 1776.9],
[2.0, 45.5, 0.0, 3421.0, 0.0],
]
),
]
data = {
420.00: ((30, 32, 10), loads_list[0]),
517.50: ((60, 50, 20), loads_list[1]),
720.00: ((60, 50, 24), loads_list[2]),
872.00: ((30, 30, 14), loads_list[3]),
}

stations = []
frames = []
for fs, (dims, load) in data.items():
w, d, r = dims
obj = Station(
orientation="FS",
name="Rounded Rectangle",
number=fs,
width=w,
depth=d,
vertical_centroid=30.0,
radius=r,
)
stations.append(copy(obj))

frames.append(
MajorFrame(
material=material,
fs_loc=obj.number,
loads=load,
geom=obj,
fd=6.0,
)
)

num = 25

frm.synthesis(num)
print("Geometry Table")
print(frm.cuts)

sizing = pd.DataFrame(frm.results, columns=["w_j", "tcap", "t_w_str", "t_w_res"])
print("Sizing Results Table")
print(sizing)
print(f"Frame weight = {frm.weight:.1f} [lbs]")

frm.show(show_coords=True, save=False)

weights = []
for frm in frames:
frm.synthesis(num)
print(f"Frame FS{frm.fs_loc:.0f} weight = {frm.weight:.1f}[lbs]")
weights.append((frm.weight, frm.fs_loc))
_fig, ax = frm.show(show_coords=True, save=False)

total_weight, cg = composite_cg(weights)
print(f"Total Frame Weight = {total_weight:.2f}[lbs] 😬")
print(f"CG of Frames = {cg:.2f}[in] ⚖")
# Sweep over number of cuts to observe the prediction sensitivity
# nums = np.linspace(15, 90, dtype=int, num=25)
# weights = []
Expand Down
Loading