@@ -3720,6 +3720,151 @@ def max_rail_button2_shear_force(self):
37203720 """Maximum lower rail button shear force, in Newtons."""
37213721 return np .abs (self .rail_button2_shear_force .y_array ).max ()
37223722
3723+ @cached_property
3724+ def calculate_rail_button_bending_moments (self ):
3725+ """Calculate internal bending moments at rail button attachment points.
3726+
3727+ This method uses beam theory to determine the internal structural
3728+ moments for stress analysis of the rail button attachments (fasteners
3729+ and airframe).
3730+
3731+ The bending moment at each button attachment consists of:
3732+
3733+ 1. Bending from shear force at button contact point: $M = S \\ times h$,
3734+ where $S$ is the shear (tangential) force and $h$ is button height.
3735+ 2. Direct moment contribution from the button's reaction forces.
3736+
3737+ Returns
3738+ -------
3739+ tuple
3740+ rail_button1_bending_moment : Function
3741+ Bending moment at upper rail button as a function of time (N·m).
3742+ max_rail_button1_bending_moment : float
3743+ Maximum upper rail button bending moment (N·m).
3744+ rail_button2_bending_moment : Function
3745+ Bending moment at lower rail button as a function of time (N·m).
3746+ max_rail_button2_bending_moment : float
3747+ Maximum lower rail button bending moment (N·m).
3748+
3749+ Notes
3750+ -----
3751+ This calculation is meaningful only during the rail phase of flight.
3752+ Maximum values use absolute values for worst-case stress analysis.
3753+ The bending moments represent internal stresses in the rocket airframe
3754+ at the rail button attachment points.
3755+
3756+ **Assumptions:**
3757+
3758+ - Rail buttons act as simple supports: provide reaction forces (normal
3759+ and shear) but no moment reaction at the rail contact point.
3760+ - The rocket acts as a beam supported at two points (rail buttons).
3761+ - Bending moments arise from the lever arm effect of reaction forces
3762+ and the cantilever moment from button standoff height.
3763+ - Normal force moment: M = N x d, where N is normal reaction force
3764+ and d is distance from button to center of dry mass.
3765+ - Shear force cantilever moment: M = S x h, where S is shear force
3766+ and h is button standoff height.
3767+
3768+ Examples
3769+ --------
3770+ >>> moments = flight.calculate_rail_button_bending_moments
3771+ >>> print(moments[1]) # max rail button 1 bending moment
3772+ >>> print(moments[3]) # max rail button 2 bending moment
3773+ """
3774+ # Check if rail buttons exist
3775+ null_moment = Function (0 )
3776+ if len (self .rocket .rail_buttons ) == 0 :
3777+ warnings .warn (
3778+ "Trying to calculate rail button bending moments without "
3779+ "rail buttons defined. Setting moments to zero." ,
3780+ UserWarning ,
3781+ )
3782+ return (null_moment , 0.0 , null_moment , 0.0 )
3783+
3784+ # Get rail button geometry
3785+ rail_buttons_tuple = self .rocket .rail_buttons [0 ]
3786+ # Rail button standoff height
3787+ h_button = rail_buttons_tuple .component .button_height
3788+ if h_button is None :
3789+ warnings .warn (
3790+ "Rail button height not defined. Bending moments cannot be "
3791+ "calculated. Setting moments to zero." ,
3792+ UserWarning ,
3793+ )
3794+ return (null_moment , 0.0 , null_moment , 0.0 )
3795+ upper_button_position = (
3796+ rail_buttons_tuple .component .buttons_distance
3797+ + rail_buttons_tuple .position .z
3798+ )
3799+ lower_button_position = rail_buttons_tuple .position .z
3800+
3801+ # Get center of dry mass (handle both callable and property)
3802+ if callable (self .rocket .center_of_dry_mass_position ):
3803+ cdm = self .rocket .center_of_dry_mass_position (self .rocket ._csys )
3804+ else :
3805+ cdm = self .rocket .center_of_dry_mass_position
3806+
3807+ # Distances from buttons to center of dry mass
3808+ d1 = abs (upper_button_position - cdm )
3809+ d2 = abs (lower_button_position - cdm )
3810+
3811+ # forces
3812+ N1 = self .rail_button1_normal_force
3813+ N2 = self .rail_button2_normal_force
3814+ S1 = self .rail_button1_shear_force
3815+ S2 = self .rail_button2_shear_force
3816+ t = N1 .source [:, 0 ]
3817+
3818+ # Calculate bending moments at attachment points
3819+ # Primary contribution from shear force acting at button height
3820+ # Secondary contribution from normal force creating moment about attachment
3821+ m1_values = N2 .source [:, 1 ] * d2 + S1 .source [:, 1 ] * h_button
3822+ m2_values = N1 .source [:, 1 ] * d1 + S2 .source [:, 1 ] * h_button
3823+
3824+ rail_button1_bending_moment = Function (
3825+ np .column_stack ([t , m1_values ]),
3826+ inputs = "Time (s)" ,
3827+ outputs = "Bending Moment (N·m)" ,
3828+ interpolation = "linear" ,
3829+ )
3830+ rail_button2_bending_moment = Function (
3831+ np .column_stack ([t , m2_values ]),
3832+ inputs = "Time (s)" ,
3833+ outputs = "Bending Moment (N·m)" ,
3834+ interpolation = "linear" ,
3835+ )
3836+
3837+ # Maximum bending moments (absolute value for stress calculations)
3838+ max_rail_button1_bending_moment = float (np .max (np .abs (m1_values )))
3839+ max_rail_button2_bending_moment = float (np .max (np .abs (m2_values )))
3840+
3841+ return (
3842+ rail_button1_bending_moment ,
3843+ max_rail_button1_bending_moment ,
3844+ rail_button2_bending_moment ,
3845+ max_rail_button2_bending_moment ,
3846+ )
3847+
3848+ @property
3849+ def rail_button1_bending_moment (self ):
3850+ """Upper rail button bending moment as a Function of time."""
3851+ return self .calculate_rail_button_bending_moments [0 ]
3852+
3853+ @property
3854+ def max_rail_button1_bending_moment (self ):
3855+ """Maximum upper rail button bending moment, in N·m."""
3856+ return self .calculate_rail_button_bending_moments [1 ]
3857+
3858+ @property
3859+ def rail_button2_bending_moment (self ):
3860+ """Lower rail button bending moment as a Function of time."""
3861+ return self .calculate_rail_button_bending_moments [2 ]
3862+
3863+ @property
3864+ def max_rail_button2_bending_moment (self ):
3865+ """Maximum lower rail button bending moment, in N·m."""
3866+ return self .calculate_rail_button_bending_moments [3 ]
3867+
37233868 @funcify_method (
37243869 "Time (s)" , "Horizontal Distance to Launch Point (m)" , "spline" , "constant"
37253870 )
@@ -4516,142 +4661,3 @@ def __lt__(self, other):
45164661 otherwise.
45174662 """
45184663 return self .t < other .t
4519-
4520- @cached_property
4521- def calculate_rail_button_bending_moments (self ):
4522- """
4523- Calculate internal bending moments at rail button attachment points.
4524-
4525- Uses beam theory to determine internal structural moments for stress
4526- analysis of the rail button attachments (fasteners and airframe).
4527-
4528- The bending moment at each button attachment consists of:
4529- 1. Bending from shear force at button contact point: M = S × h
4530- where S is the shear (tangential) force and h is button height
4531- 2. Direct moment contribution from the button's reaction forces
4532-
4533- Assumptions
4534- -----------
4535- - Rail buttons act as simple supports: provide reaction forces (normal
4536- and shear) but no moment reaction at the rail contact point.
4537- - The rocket acts as a beam supported at two points (rail buttons).
4538- - Bending moments arise from the lever arm effect of reaction forces
4539- and the cantilever moment from button standoff height.
4540-
4541- The bending moment at each button attachment consists of:
4542- 1. Normal force moment: M = N x d, where N is normal reaction force
4543- and d is distance from button to center of dry mass
4544- 2. Shear force cantilever moment: M = S x h, where S is shear force
4545- and h is button standoff height
4546-
4547- Notes
4548- -----
4549- - Calculated only during the rail phase of flight
4550- - Maximum values use absolute values for worst-case stress analysis
4551- - The bending moments represent internal stresses in the rocket
4552- airframe at the rail button attachment points
4553-
4554- Returns
4555- -------
4556- tuple
4557- (rail_button1_bending_moment : Function,
4558- max_rail_button1_bending_moment : float,
4559- rail_button2_bending_moment : Function,
4560- max_rail_button2_bending_moment : float)
4561-
4562- Where rail_button1/2_bending_moment are Function objects of time
4563- in N·m, and max values are floats in N·m.
4564- """
4565- # Check if rail buttons exist
4566- null_moment = Function (0 )
4567- if len (self .rocket .rail_buttons ) == 0 :
4568- warnings .warn (
4569- "Trying to calculate rail button bending moments without "
4570- "rail buttons defined. Setting moments to zero." ,
4571- UserWarning ,
4572- )
4573- return (null_moment , 0.0 , null_moment , 0.0 )
4574-
4575- # Get rail button geometry
4576- rail_buttons_tuple = self .rocket .rail_buttons [0 ]
4577- # Rail button standoff height
4578- h_button = rail_buttons_tuple .component .button_height
4579- if h_button is None :
4580- warnings .warn (
4581- "Rail button height not defined. Bending moments cannot be "
4582- "calculated. Setting moments to zero." ,
4583- UserWarning ,
4584- )
4585- return (null_moment , 0.0 , null_moment , 0.0 )
4586- upper_button_position = (
4587- rail_buttons_tuple .component .buttons_distance
4588- + rail_buttons_tuple .position .z
4589- )
4590- lower_button_position = rail_buttons_tuple .position .z
4591-
4592- # Get center of dry mass (handle both callable and property)
4593- if callable (self .rocket .center_of_dry_mass_position ):
4594- cdm = self .rocket .center_of_dry_mass_position (self .rocket ._csys )
4595- else :
4596- cdm = self .rocket .center_of_dry_mass_position
4597-
4598- # Distances from buttons to center of dry mass
4599- d1 = abs (upper_button_position - cdm )
4600- d2 = abs (lower_button_position - cdm )
4601-
4602- # forces
4603- N1 = self .rail_button1_normal_force
4604- N2 = self .rail_button2_normal_force
4605- S1 = self .rail_button1_shear_force
4606- S2 = self .rail_button2_shear_force
4607- t = N1 .source [:, 0 ]
4608-
4609- # Calculate bending moments at attachment points
4610- # Primary contribution from shear force acting at button height
4611- # Secondary contribution from normal force creating moment about attachment
4612- m1_values = N2 .source [:, 1 ] * d2 + S1 .source [:, 1 ] * h_button
4613- m2_values = N1 .source [:, 1 ] * d1 + S2 .source [:, 1 ] * h_button
4614-
4615- rail_button1_bending_moment = Function (
4616- np .column_stack ([t , m1_values ]),
4617- inputs = "Time (s)" ,
4618- outputs = "Bending Moment (N·m)" ,
4619- interpolation = "linear" ,
4620- )
4621- rail_button2_bending_moment = Function (
4622- np .column_stack ([t , m2_values ]),
4623- inputs = "Time (s)" ,
4624- outputs = "Bending Moment (N·m)" ,
4625- interpolation = "linear" ,
4626- )
4627-
4628- # Maximum bending moments (absolute value for stress calculations)
4629- max_rail_button1_bending_moment = float (np .max (np .abs (m1_values )))
4630- max_rail_button2_bending_moment = float (np .max (np .abs (m2_values )))
4631-
4632- return (
4633- rail_button1_bending_moment ,
4634- max_rail_button1_bending_moment ,
4635- rail_button2_bending_moment ,
4636- max_rail_button2_bending_moment ,
4637- )
4638-
4639- @property
4640- def rail_button1_bending_moment (self ):
4641- """Upper rail button bending moment as a Function of time."""
4642- return self .calculate_rail_button_bending_moments [0 ]
4643-
4644- @property
4645- def max_rail_button1_bending_moment (self ):
4646- """Maximum upper rail button bending moment, in N·m."""
4647- return self .calculate_rail_button_bending_moments [1 ]
4648-
4649- @property
4650- def rail_button2_bending_moment (self ):
4651- """Lower rail button bending moment as a Function of time."""
4652- return self .calculate_rail_button_bending_moments [2 ]
4653-
4654- @property
4655- def max_rail_button2_bending_moment (self ):
4656- """Maximum lower rail button bending moment, in N·m."""
4657- return self .calculate_rail_button_bending_moments [3 ]
0 commit comments