@@ -3424,6 +3424,151 @@ def max_rail_button2_shear_force(self):
34243424 """Maximum lower rail button shear force, in Newtons."""
34253425 return np .abs (self .rail_button2_shear_force .y_array ).max ()
34263426
3427+ @cached_property
3428+ def calculate_rail_button_bending_moments (self ):
3429+ """Calculate internal bending moments at rail button attachment points.
3430+
3431+ This method uses beam theory to determine the internal structural
3432+ moments for stress analysis of the rail button attachments (fasteners
3433+ and airframe).
3434+
3435+ The bending moment at each button attachment consists of:
3436+
3437+ 1. Bending from shear force at button contact point: $M = S \\ times h$,
3438+ where $S$ is the shear (tangential) force and $h$ is button height.
3439+ 2. Direct moment contribution from the button's reaction forces.
3440+
3441+ Returns
3442+ -------
3443+ tuple
3444+ rail_button1_bending_moment : Function
3445+ Bending moment at upper rail button as a function of time (N·m).
3446+ max_rail_button1_bending_moment : float
3447+ Maximum upper rail button bending moment (N·m).
3448+ rail_button2_bending_moment : Function
3449+ Bending moment at lower rail button as a function of time (N·m).
3450+ max_rail_button2_bending_moment : float
3451+ Maximum lower rail button bending moment (N·m).
3452+
3453+ Notes
3454+ -----
3455+ This calculation is meaningful only during the rail phase of flight.
3456+ Maximum values use absolute values for worst-case stress analysis.
3457+ The bending moments represent internal stresses in the rocket airframe
3458+ at the rail button attachment points.
3459+
3460+ **Assumptions:**
3461+
3462+ - Rail buttons act as simple supports: provide reaction forces (normal
3463+ and shear) but no moment reaction at the rail contact point.
3464+ - The rocket acts as a beam supported at two points (rail buttons).
3465+ - Bending moments arise from the lever arm effect of reaction forces
3466+ and the cantilever moment from button standoff height.
3467+ - Normal force moment: M = N x d, where N is normal reaction force
3468+ and d is distance from button to center of dry mass.
3469+ - Shear force cantilever moment: M = S x h, where S is shear force
3470+ and h is button standoff height.
3471+
3472+ Examples
3473+ --------
3474+ >>> moments = flight.calculate_rail_button_bending_moments
3475+ >>> print(moments[1]) # max rail button 1 bending moment
3476+ >>> print(moments[3]) # max rail button 2 bending moment
3477+ """
3478+ # Check if rail buttons exist
3479+ null_moment = Function (0 )
3480+ if len (self .rocket .rail_buttons ) == 0 :
3481+ warnings .warn (
3482+ "Trying to calculate rail button bending moments without "
3483+ "rail buttons defined. Setting moments to zero." ,
3484+ UserWarning ,
3485+ )
3486+ return (null_moment , 0.0 , null_moment , 0.0 )
3487+
3488+ # Get rail button geometry
3489+ rail_buttons_tuple = self .rocket .rail_buttons [0 ]
3490+ # Rail button standoff height
3491+ h_button = rail_buttons_tuple .component .button_height
3492+ if h_button is None :
3493+ warnings .warn (
3494+ "Rail button height not defined. Bending moments cannot be "
3495+ "calculated. Setting moments to zero." ,
3496+ UserWarning ,
3497+ )
3498+ return (null_moment , 0.0 , null_moment , 0.0 )
3499+ upper_button_position = (
3500+ rail_buttons_tuple .component .buttons_distance
3501+ + rail_buttons_tuple .position .z
3502+ )
3503+ lower_button_position = rail_buttons_tuple .position .z
3504+
3505+ # Get center of dry mass (handle both callable and property)
3506+ if callable (self .rocket .center_of_dry_mass_position ):
3507+ cdm = self .rocket .center_of_dry_mass_position (self .rocket ._csys )
3508+ else :
3509+ cdm = self .rocket .center_of_dry_mass_position
3510+
3511+ # Distances from buttons to center of dry mass
3512+ d1 = abs (upper_button_position - cdm )
3513+ d2 = abs (lower_button_position - cdm )
3514+
3515+ # forces
3516+ N1 = self .rail_button1_normal_force
3517+ N2 = self .rail_button2_normal_force
3518+ S1 = self .rail_button1_shear_force
3519+ S2 = self .rail_button2_shear_force
3520+ t = N1 .source [:, 0 ]
3521+
3522+ # Calculate bending moments at attachment points
3523+ # Primary contribution from shear force acting at button height
3524+ # Secondary contribution from normal force creating moment about attachment
3525+ m1_values = N2 .source [:, 1 ] * d2 + S1 .source [:, 1 ] * h_button
3526+ m2_values = N1 .source [:, 1 ] * d1 + S2 .source [:, 1 ] * h_button
3527+
3528+ rail_button1_bending_moment = Function (
3529+ np .column_stack ([t , m1_values ]),
3530+ inputs = "Time (s)" ,
3531+ outputs = "Bending Moment (N·m)" ,
3532+ interpolation = "linear" ,
3533+ )
3534+ rail_button2_bending_moment = Function (
3535+ np .column_stack ([t , m2_values ]),
3536+ inputs = "Time (s)" ,
3537+ outputs = "Bending Moment (N·m)" ,
3538+ interpolation = "linear" ,
3539+ )
3540+
3541+ # Maximum bending moments (absolute value for stress calculations)
3542+ max_rail_button1_bending_moment = float (np .max (np .abs (m1_values )))
3543+ max_rail_button2_bending_moment = float (np .max (np .abs (m2_values )))
3544+
3545+ return (
3546+ rail_button1_bending_moment ,
3547+ max_rail_button1_bending_moment ,
3548+ rail_button2_bending_moment ,
3549+ max_rail_button2_bending_moment ,
3550+ )
3551+
3552+ @property
3553+ def rail_button1_bending_moment (self ):
3554+ """Upper rail button bending moment as a Function of time."""
3555+ return self .calculate_rail_button_bending_moments [0 ]
3556+
3557+ @property
3558+ def max_rail_button1_bending_moment (self ):
3559+ """Maximum upper rail button bending moment, in N·m."""
3560+ return self .calculate_rail_button_bending_moments [1 ]
3561+
3562+ @property
3563+ def rail_button2_bending_moment (self ):
3564+ """Lower rail button bending moment as a Function of time."""
3565+ return self .calculate_rail_button_bending_moments [2 ]
3566+
3567+ @property
3568+ def max_rail_button2_bending_moment (self ):
3569+ """Maximum lower rail button bending moment, in N·m."""
3570+ return self .calculate_rail_button_bending_moments [3 ]
3571+
34273572 @funcify_method (
34283573 "Time (s)" , "Horizontal Distance to Launch Point (m)" , "spline" , "constant"
34293574 )
@@ -4220,142 +4365,3 @@ def __lt__(self, other):
42204365 otherwise.
42214366 """
42224367 return self .t < other .t
4223-
4224- @cached_property
4225- def calculate_rail_button_bending_moments (self ):
4226- """
4227- Calculate internal bending moments at rail button attachment points.
4228-
4229- Uses beam theory to determine internal structural moments for stress
4230- analysis of the rail button attachments (fasteners and airframe).
4231-
4232- The bending moment at each button attachment consists of:
4233- 1. Bending from shear force at button contact point: M = S × h
4234- where S is the shear (tangential) force and h is button height
4235- 2. Direct moment contribution from the button's reaction forces
4236-
4237- Assumptions
4238- -----------
4239- - Rail buttons act as simple supports: provide reaction forces (normal
4240- and shear) but no moment reaction at the rail contact point.
4241- - The rocket acts as a beam supported at two points (rail buttons).
4242- - Bending moments arise from the lever arm effect of reaction forces
4243- and the cantilever moment from button standoff height.
4244-
4245- The bending moment at each button attachment consists of:
4246- 1. Normal force moment: M = N x d, where N is normal reaction force
4247- and d is distance from button to center of dry mass
4248- 2. Shear force cantilever moment: M = S x h, where S is shear force
4249- and h is button standoff height
4250-
4251- Notes
4252- -----
4253- - Calculated only during the rail phase of flight
4254- - Maximum values use absolute values for worst-case stress analysis
4255- - The bending moments represent internal stresses in the rocket
4256- airframe at the rail button attachment points
4257-
4258- Returns
4259- -------
4260- tuple
4261- (rail_button1_bending_moment : Function,
4262- max_rail_button1_bending_moment : float,
4263- rail_button2_bending_moment : Function,
4264- max_rail_button2_bending_moment : float)
4265-
4266- Where rail_button1/2_bending_moment are Function objects of time
4267- in N·m, and max values are floats in N·m.
4268- """
4269- # Check if rail buttons exist
4270- null_moment = Function (0 )
4271- if len (self .rocket .rail_buttons ) == 0 :
4272- warnings .warn (
4273- "Trying to calculate rail button bending moments without "
4274- "rail buttons defined. Setting moments to zero." ,
4275- UserWarning ,
4276- )
4277- return (null_moment , 0.0 , null_moment , 0.0 )
4278-
4279- # Get rail button geometry
4280- rail_buttons_tuple = self .rocket .rail_buttons [0 ]
4281- # Rail button standoff height
4282- h_button = rail_buttons_tuple .component .button_height
4283- if h_button is None :
4284- warnings .warn (
4285- "Rail button height not defined. Bending moments cannot be "
4286- "calculated. Setting moments to zero." ,
4287- UserWarning ,
4288- )
4289- return (null_moment , 0.0 , null_moment , 0.0 )
4290- upper_button_position = (
4291- rail_buttons_tuple .component .buttons_distance
4292- + rail_buttons_tuple .position .z
4293- )
4294- lower_button_position = rail_buttons_tuple .position .z
4295-
4296- # Get center of dry mass (handle both callable and property)
4297- if callable (self .rocket .center_of_dry_mass_position ):
4298- cdm = self .rocket .center_of_dry_mass_position (self .rocket ._csys )
4299- else :
4300- cdm = self .rocket .center_of_dry_mass_position
4301-
4302- # Distances from buttons to center of dry mass
4303- d1 = abs (upper_button_position - cdm )
4304- d2 = abs (lower_button_position - cdm )
4305-
4306- # forces
4307- N1 = self .rail_button1_normal_force
4308- N2 = self .rail_button2_normal_force
4309- S1 = self .rail_button1_shear_force
4310- S2 = self .rail_button2_shear_force
4311- t = N1 .source [:, 0 ]
4312-
4313- # Calculate bending moments at attachment points
4314- # Primary contribution from shear force acting at button height
4315- # Secondary contribution from normal force creating moment about attachment
4316- m1_values = N2 .source [:, 1 ] * d2 + S1 .source [:, 1 ] * h_button
4317- m2_values = N1 .source [:, 1 ] * d1 + S2 .source [:, 1 ] * h_button
4318-
4319- rail_button1_bending_moment = Function (
4320- np .column_stack ([t , m1_values ]),
4321- inputs = "Time (s)" ,
4322- outputs = "Bending Moment (N·m)" ,
4323- interpolation = "linear" ,
4324- )
4325- rail_button2_bending_moment = Function (
4326- np .column_stack ([t , m2_values ]),
4327- inputs = "Time (s)" ,
4328- outputs = "Bending Moment (N·m)" ,
4329- interpolation = "linear" ,
4330- )
4331-
4332- # Maximum bending moments (absolute value for stress calculations)
4333- max_rail_button1_bending_moment = float (np .max (np .abs (m1_values )))
4334- max_rail_button2_bending_moment = float (np .max (np .abs (m2_values )))
4335-
4336- return (
4337- rail_button1_bending_moment ,
4338- max_rail_button1_bending_moment ,
4339- rail_button2_bending_moment ,
4340- max_rail_button2_bending_moment ,
4341- )
4342-
4343- @property
4344- def rail_button1_bending_moment (self ):
4345- """Upper rail button bending moment as a Function of time."""
4346- return self .calculate_rail_button_bending_moments [0 ]
4347-
4348- @property
4349- def max_rail_button1_bending_moment (self ):
4350- """Maximum upper rail button bending moment, in N·m."""
4351- return self .calculate_rail_button_bending_moments [1 ]
4352-
4353- @property
4354- def rail_button2_bending_moment (self ):
4355- """Lower rail button bending moment as a Function of time."""
4356- return self .calculate_rail_button_bending_moments [2 ]
4357-
4358- @property
4359- def max_rail_button2_bending_moment (self ):
4360- """Maximum lower rail button bending moment, in N·m."""
4361- return self .calculate_rail_button_bending_moments [3 ]
0 commit comments