From a0ebaa8419c92fd906fc9133aa4dc11674c51f0b Mon Sep 17 00:00:00 2001 From: Benjamin Crews Date: Tue, 14 May 2024 22:03:29 -0400 Subject: [PATCH 1/6] Version attribute and fuselage fix. Added __version__ to the package init instead of package main. Fixed the Fuselage.Cover.panel_flutter dynamic pressure. Pressures are provided in thousands of feet. --- src/hyperstruct/__init__.py | 4 ++++ src/hyperstruct/fuselage.py | 12 +++++++++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/hyperstruct/__init__.py b/src/hyperstruct/__init__.py index 6d456bf..378c206 100644 --- a/src/hyperstruct/__init__.py +++ b/src/hyperstruct/__init__.py @@ -1,6 +1,10 @@ """Hyperstruct.""" from dataclasses import dataclass +from importlib.metadata import version + + +__version__ = version("hyperstruct") @dataclass diff --git a/src/hyperstruct/fuselage.py b/src/hyperstruct/fuselage.py index 3df23f5..159197b 100644 --- a/src/hyperstruct/fuselage.py +++ b/src/hyperstruct/fuselage.py @@ -255,22 +255,28 @@ def panel_flutter(self, mach: float, altitude: float) -> float: To find the final required thickness, this method must be looped over for all Mach and altitudes corresponding to the flight envelope conditions of the aircraft. + + Inputs: Mach Number, and Altitude (in thousands of feet) + Returns: Field Thickness """ # Dynamic pressures based on standard day atmosphere. # Dynamic Pressure, q, in [psf] # Altitudes must be measured in [ft] - if altitude <= 20000: + if altitude <= 20: q = mach**2 * (1479.757 - 52.187 * altitude + 0.619868 * altitude**2) - elif 20000 < altitude <= 70000: + elif 20 < altitude <= 70: q = mach**2 * ( 1465.175 - 50.76695 * altitude + 0.6434412 * altitude**2 - 0.002907194 * altitude**3 ) - elif altitude > 70000: + elif altitude > 70: q = mach**2 * (199.659 / altitude) ** 4 + # Dynamic pressure is in [psf] convert to [psi] + q = q / 144 + # Calculate the Mach Number Effect # The Mach Number Effect, FM, is a 3 piece function from SWEEP. # This function is more conservative than the AFRL baseline curve, From 45b4554307b6295069403233c4f64527792d3a98 Mon Sep 17 00:00:00 2001 From: Benjamin Crews Date: Wed, 15 May 2024 21:02:25 -0400 Subject: [PATCH 2/6] Initial creation of fuselage.MinorFrame. MinorFrame class created and outlined with the basic analysis methods. --- src/hyperstruct/fuselage.py | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/hyperstruct/fuselage.py b/src/hyperstruct/fuselage.py index 159197b..3d0c29c 100644 --- a/src/hyperstruct/fuselage.py +++ b/src/hyperstruct/fuselage.py @@ -349,3 +349,37 @@ def acoustic_fatigue(self) -> Tuple[float, float]: f_c = 1.0794 + 0.000143 * x_c - 0.076475 * (1 / x_c) - 0.29969 * np.log(x_c) return (float(f_l * t_l), float(f_c * t_c)) + + +@dataclass +class MinorFrame(Component): + """Fuselage Minor Frame Component. + + Minor Frames form part of the basic structural grid work that resists + vehicle shear and bending loads. As opposed to Major Frames, Minor + Frames do not redistribute concentrated loads. Minor Frames are sized + for general shell stability, acoustic fatigue, and forced crippling + due to postbuckled cover (skin) design. + + Minor Frames have an assumed construction, shown here? The geometry + variables, frame depth (c), and cap flange width (b), are user input + parameters. Thickness is the only parameter determined by sizing. + + For simplicity, the flanges are assumed to have a thickness twice + that of the web. + """ + + c: float + b: float + + def general_stability(self) -> float: + """Thickness to avoid general instability.""" + return 0.0 + + def acoustic_fatigue(self) -> float: + """Thickness from acoustic fatigue.""" + return 0.0 + + def forced_crippling(self) -> float: + """Thickness from force crippling.""" + return 0.0 From ded89f82d6cad7dbfb1f0698b72553e5613f034b Mon Sep 17 00:00:00 2001 From: Benjamin Crews Date: Thu, 16 May 2024 21:51:14 -0400 Subject: [PATCH 3/6] Work on MinorFrames. Moved the material attribute to the base Component. Copied/modified the acoustic fatigue method from Cover for the MinorFrame. Added MinorFrame attributes and docstrings. Modified some docstrings to match Google styling. Will need to be more diligent with this in the future. --- src/hyperstruct/__init__.py | 3 + src/hyperstruct/fuselage.py | 127 ++++++++++++++++++++++++++++++++---- 2 files changed, 117 insertions(+), 13 deletions(-) diff --git a/src/hyperstruct/__init__.py b/src/hyperstruct/__init__.py index 378c206..a297012 100644 --- a/src/hyperstruct/__init__.py +++ b/src/hyperstruct/__init__.py @@ -58,6 +58,9 @@ class Component: weights estimation. """ + material: Material + """material the cover is made of.""" + def synthesis(self) -> None: """The sizing method. diff --git a/src/hyperstruct/fuselage.py b/src/hyperstruct/fuselage.py index 3d0c29c..fe42e3f 100644 --- a/src/hyperstruct/fuselage.py +++ b/src/hyperstruct/fuselage.py @@ -12,7 +12,6 @@ import numpy as np from hyperstruct import Component -from hyperstruct import Material @dataclass @@ -27,9 +26,6 @@ class Cover(Component): through a systematic check for the different criteria. """ - material: Material - """material the cover is made of.""" - milled: bool """if panel is to be milled for different field vs land thicknesses.""" @@ -130,6 +126,12 @@ def field_thickness_postbuckled(self, alpha: float = 45) -> float: Postbuckled strength assumes sizing covers with diagonal tension. The diagonal tension angle is unknown because intermediate frame and stringer sizing is not known. An initial estimate of 45 degrees is used. + + Arguments: + alpha = Diagonal tension angle (assumed 45 degrees) + + Returns: + t_c = field thickness """ F_scr = ( self.k_s @@ -195,7 +197,11 @@ def thickness_pressure(self, F_allow: Any = None) -> Tuple[float, float]: from zero to peak pressure 20,000 times during the vehicle's useful life, with a stress concentration factor of 4.0. - Returns: (t_l, t_c) + Arguments: + F_allow = Allowable stress (25% yield, if not provided) + + Returns: + (t_l, t_c) = Land thickness, Field thickness """ b = min(self.D, self.L) if not F_allow: @@ -256,8 +262,12 @@ def panel_flutter(self, mach: float, altitude: float) -> float: over for all Mach and altitudes corresponding to the flight envelope conditions of the aircraft. - Inputs: Mach Number, and Altitude (in thousands of feet) - Returns: Field Thickness + Arguments: + mach = Mach Number + altitude = Altitude (in thousands of feet) + + Returns: + Field Thickness """ # Dynamic pressures based on standard day atmosphere. # Dynamic Pressure, q, in [psf] @@ -370,15 +380,106 @@ class MinorFrame(Component): """ c: float + """frame depth.""" + b: float + """cap flange width.""" - def general_stability(self) -> float: - """Thickness to avoid general instability.""" - return 0.0 + t_r: float = 0.0 + """cap flange thickness.""" - def acoustic_fatigue(self) -> float: - """Thickness from acoustic fatigue.""" - return 0.0 + @property + def t_w(self) -> float: + """Web thickness.""" + return self.t_r / 2 + + @property + def area(self) -> float: + """Cross-sectional area.""" + return 4 * self.b * self.t_r + self.c * self.t_w + + @property + def i_xx(self) -> float: + """Second moment of area (bending moment of inertia). + + Assumes the simplified case that t_r = 2*t_w. + """ + return self.t_r * ( + self.b * self.c**2 + 2 * self.b**3 / 3 - self.b**2 * self.c + self.c**3 / 24 + ) + + @property + def rho(self) -> float: + """Radius of gyration.""" + return ( + ( + self.b * self.c**2 + + 2 * self.b**3 / 3 + - self.b**2 * self.c + + self.c**3 / 24 + ) + / (4 * self.b + self.c / 2) + ) ** 0.5 + + def general_stability(self, L: float, D: float, M: float) -> float: + """Thickness to avoid general instability. + + The thickness that provides frame stiffness sufficient to prevent + general instability failure is solved via the Shanley equation. + + Arguments: + L = Frame Spacing + D = Fuselage Diameter + M = Bending moment at the cut + + Returns: + t_r = Flange thickness + """ + c_f = 1 / 16000 + numerator = c_f * M * D**2 + denominator = ( + self.material.E_c + * L + * (self.b * self.c**2 + 2 * self.b**3 * self.c / 3 + self.c**3 / 24) + ) + + return float(numerator / denominator) + + def acoustic_fatigue(self, b: float) -> float: + """Thickness requirements based on acoustic fatigue. + + Assumptions are: + 1.) The overall pressure level from jet engine noise is approximately + 30db higher than the random spectrum pressure level. + 2.) The accuracy of noise intensity prediction is on the order of +/-3db. + 3.) Beam theory is sufficient for modelign the shell elements. + 4.) The design to repetitive pressure intensity from jet engine noise + can be correlated tot he material endurance limit. Polished specimen + s-n data, K_t=1, is a sufficiently representative strength index. + 5.) The method is based on a limited amount of testing. + 6.) The method does not consider panel aspect ratio, and is, therefore, + conservative when aspect ratios approach 1. + + The sound pressure level used is based on a material property. This value + provides the fatigue life of 10^9 cycles for the material. The overall + decibel level is then increased by 30, which represents jet noise instead + of a purely random spectrum. + + Arguments: + b = Support spacing (frame spacing) + + Returns: + t_r = Flange thickness + """ + # Random distribution acoustic level, that provides a material fatigue + # life of 10^9 cycles. + db_r = self.material.db_r + db_oa = db_r + 30 + P = 2.9e-9 * 10 ** (db_oa / 20) + # Note: K_c is directly hardcoded to 7.025, per Fig. 20 of ADA002867 + t_r = 7.025 * b**0.5 * P**2 / self.material.F_en + + return t_r def forced_crippling(self) -> float: """Thickness from force crippling.""" From 7ad3836e3bc9123bd71f03ca3d8ecaf260e58e5a Mon Sep 17 00:00:00 2001 From: Benjamin Crews Date: Thu, 16 May 2024 21:51:33 -0400 Subject: [PATCH 4/6] Version bump. --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index b874c69..dd26255 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "hyperstruct" -version = "0.0.3" +version = "0.0.4" description = "Hyperstruct" authors = ["Benjamin Crews "] license = "MIT" From 9ad93f6145ae657524cae65cd750def60bb06021 Mon Sep 17 00:00:00 2001 From: Benjamin Crews Date: Thu, 16 May 2024 21:57:48 -0400 Subject: [PATCH 5/6] Fix mypy. --- src/hyperstruct/fuselage.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/hyperstruct/fuselage.py b/src/hyperstruct/fuselage.py index fe42e3f..fff4275 100644 --- a/src/hyperstruct/fuselage.py +++ b/src/hyperstruct/fuselage.py @@ -411,15 +411,18 @@ def i_xx(self) -> float: @property def rho(self) -> float: """Radius of gyration.""" - return ( + return float( ( - self.b * self.c**2 - + 2 * self.b**3 / 3 - - self.b**2 * self.c - + self.c**3 / 24 + ( + self.b * self.c**2 + + 2 * self.b**3 / 3 + - self.b**2 * self.c + + self.c**3 / 24 + ) + / (4 * self.b + self.c / 2) ) - / (4 * self.b + self.c / 2) - ) ** 0.5 + ** 0.5 + ) def general_stability(self, L: float, D: float, M: float) -> float: """Thickness to avoid general instability. @@ -479,7 +482,7 @@ def acoustic_fatigue(self, b: float) -> float: # Note: K_c is directly hardcoded to 7.025, per Fig. 20 of ADA002867 t_r = 7.025 * b**0.5 * P**2 / self.material.F_en - return t_r + return float(t_r) def forced_crippling(self) -> float: """Thickness from force crippling.""" From 792d150fe05d8f96568f17b1ba1dd8d40be90816 Mon Sep 17 00:00:00 2001 From: Benjamin Crews Date: Thu, 16 May 2024 22:07:49 -0400 Subject: [PATCH 6/6] Fix darglint. We go by Google standards now, boi! --- src/hyperstruct/fuselage.py | 39 +++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/src/hyperstruct/fuselage.py b/src/hyperstruct/fuselage.py index fff4275..339b88e 100644 --- a/src/hyperstruct/fuselage.py +++ b/src/hyperstruct/fuselage.py @@ -127,11 +127,11 @@ def field_thickness_postbuckled(self, alpha: float = 45) -> float: diagonal tension angle is unknown because intermediate frame and stringer sizing is not known. An initial estimate of 45 degrees is used. - Arguments: - alpha = Diagonal tension angle (assumed 45 degrees) + Args: + alpha: Diagonal tension angle (assumed 45 degrees) Returns: - t_c = field thickness + A float of field thickness. """ F_scr = ( self.k_s @@ -197,11 +197,11 @@ def thickness_pressure(self, F_allow: Any = None) -> Tuple[float, float]: from zero to peak pressure 20,000 times during the vehicle's useful life, with a stress concentration factor of 4.0. - Arguments: - F_allow = Allowable stress (25% yield, if not provided) + Args: + F_allow: Allowable stress (25% yield, if not provided) Returns: - (t_l, t_c) = Land thickness, Field thickness + A tuple of (Land thickness, Field thickness). """ b = min(self.D, self.L) if not F_allow: @@ -262,12 +262,12 @@ def panel_flutter(self, mach: float, altitude: float) -> float: over for all Mach and altitudes corresponding to the flight envelope conditions of the aircraft. - Arguments: - mach = Mach Number - altitude = Altitude (in thousands of feet) + Args: + mach: Mach Number + altitude: Altitude (in thousands of feet) Returns: - Field Thickness + A float of Field Thickness. """ # Dynamic pressures based on standard day atmosphere. # Dynamic Pressure, q, in [psf] @@ -336,7 +336,8 @@ def acoustic_fatigue(self) -> Tuple[float, float]: decibel level is then increased by 30, which represents jet noise instead of a purely random spectrum. - Returns: (t_l, t_c) + Returns: + A tuple of Land thickness, Field thickness (t_l, t_c). """ # Random distribution acoustic level, that provides a material fatigue # life of 10^9 cycles. @@ -430,13 +431,13 @@ def general_stability(self, L: float, D: float, M: float) -> float: The thickness that provides frame stiffness sufficient to prevent general instability failure is solved via the Shanley equation. - Arguments: - L = Frame Spacing - D = Fuselage Diameter - M = Bending moment at the cut + Args: + L: Frame Spacing + D: Fuselage Diameter + M: Bending moment at the cut Returns: - t_r = Flange thickness + A float of Flange thickness. """ c_f = 1 / 16000 numerator = c_f * M * D**2 @@ -468,11 +469,11 @@ def acoustic_fatigue(self, b: float) -> float: decibel level is then increased by 30, which represents jet noise instead of a purely random spectrum. - Arguments: - b = Support spacing (frame spacing) + Args: + b: Support spacing (frame spacing) Returns: - t_r = Flange thickness + A float of Flange thickness. """ # Random distribution acoustic level, that provides a material fatigue # life of 10^9 cycles.