@@ -10,16 +10,20 @@ class SeparableBody:
1010 name : str
1111 Unique body name within the vehicle; used to group Mission
1212 results (e.g. ``mission.flights["booster"]``).
13- separation : Event, optional
14- Event that releases this body. If None, never separates.
1513 separation_delta_v : float, optional
1614 Relative separation speed along the stack's longitudinal axis,
1715 in m/s, split by momentum conservation. Default 0.
16+
17+ Notes
18+ -----
19+ Subclasses each hold their own release-event attribute under its own
20+ name (``Stage.separation``, ``Deployable.ejection``) rather than a
21+ shared base attribute, since the two events are conceptually similar
22+ but not interchangeable.
1823 """
1924
20- def __init__ (self , name , separation = None , separation_delta_v = 0.0 ):
25+ def __init__ (self , name , separation_delta_v = 0.0 ):
2126 self .name = name
22- self .separation = separation
2327 self .separation_delta_v = separation_delta_v
2428
2529
@@ -56,10 +60,9 @@ def __init__(
5660 ignition = None ,
5761 ignition_delay = 0.0 ,
5862 ):
59- super ().__init__ (
60- name = name , separation = separation , separation_delta_v = separation_delta_v
61- )
63+ super ().__init__ (name = name , separation_delta_v = separation_delta_v )
6264 self .rocket = rocket
65+ self .separation = separation
6366 self .ignition = ignition
6467 self .ignition_delay = ignition_delay
6568
@@ -72,3 +75,82 @@ def burn_out_time(self):
7275 def dry_mass (self ):
7376 """Stage dry mass (structure + motor dry mass), in kg."""
7477 return self .rocket .dry_mass
78+
79+
80+ class Deployable (SeparableBody ):
81+ """An inert body carried by the vehicle and ejected in flight.
82+
83+ No aerodynamic identity while attached: contributes only mass and
84+ inertia at a position inside the carrying stage.
85+
86+ Free-flight aerodynamics come from one of two sources, mutually
87+ exclusive:
88+ - ``free_rocket``: a fully built Rocket or PointMassRocket (full
89+ control);
90+ - surfaces added with ``add_surface()``: Mission assembles the
91+ free-flight Rocket from the deployable's mass, inertia, radius and
92+ the added surfaces.
93+
94+ Parameters
95+ ----------
96+ name : str
97+ Unique body name; used to group Mission results.
98+ mass : float
99+ Carried mass in kg.
100+ inertia : tuple of float
101+ Inertia (I11, I22, I33) about the deployable's own center of
102+ mass, in kg*m^2.
103+ position : float
104+ Position of the deployable's center of mass in the carrying
105+ stage's rocket coordinate system, in meters.
106+ radius : float, optional
107+ The deployable's largest radius in meters. Required only when
108+ defining its free-flight aerodynamics via add_surface().
109+ free_rocket : Rocket, PointMassRocket, optional
110+ Free-flight configuration after ejection. Mutually exclusive
111+ with add_surface().
112+ ejection : Event, optional
113+ When the deployable is released, e.g. Event(trigger="apogee").
114+ separation_delta_v : float, optional
115+ See SeparableBody.
116+ """
117+
118+ def __init__ (
119+ self ,
120+ name ,
121+ mass ,
122+ inertia ,
123+ position ,
124+ radius = None ,
125+ free_rocket = None ,
126+ ejection = None ,
127+ separation_delta_v = 0.0 ,
128+ ):
129+ super ().__init__ (name = name , separation_delta_v = separation_delta_v )
130+ self .mass = mass
131+ self .inertia = inertia
132+ self .position = position
133+ self .radius = radius
134+ self .free_rocket = free_rocket
135+ self .ejection = ejection
136+ self .surfaces = []
137+
138+ def add_surface (self , surface , position ):
139+ """Add an aerodynamic surface to the deployable's free flight.
140+
141+ Takes effect only after ejection; while attached the deployable
142+ still contributes only mass and inertia. ``position`` is in the
143+ deployable's own coordinate system, in meters. Requires
144+ ``radius`` to be set and is mutually exclusive with
145+ ``free_rocket``.
146+ """
147+ if self .free_rocket is not None :
148+ raise ValueError (
149+ "add_surface is mutually exclusive with free_rocket; "
150+ "a free_rocket was already provided for this deployable."
151+ )
152+ if self .radius is None :
153+ raise ValueError (
154+ "add_surface requires radius to be set on the deployable."
155+ )
156+ self .surfaces .append ((surface , position ))
0 commit comments