Skip to content

Commit 0ed8189

Browse files
committed
ENH: address comments and change mixin classes to geometry classes
1 parent e3c85db commit 0ed8189

11 files changed

Lines changed: 862 additions & 115 deletions

rocketpy/prints/aero_surface_prints.py

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
from abc import ABC, abstractmethod
22

3-
43
class _AeroSurfacePrints(ABC):
54
def __init__(self, aero_surface):
65
self.aero_surface = aero_surface
@@ -77,10 +76,8 @@ def geometry(self):
7776
print("-------------------------------------")
7877
print(f"Number of fins: {self.aero_surface.n}")
7978
print(f"Reference rocket radius: {self.aero_surface.rocket_radius:.3f} m")
80-
try:
79+
if hasattr(self.aero_surface, "tip_chord"):
8180
print(f"Tip chord: {self.aero_surface.tip_chord:.3f} m")
82-
except AttributeError:
83-
pass # it isn't a trapezoidal fin, just don't worry about tip chord
8481
print(f"Root chord: {self.aero_surface.root_chord:.3f} m")
8582
print(f"Span: {self.aero_surface.span:.3f} m")
8683
print(
@@ -176,10 +173,8 @@ def geometry(self):
176173
print("Geometric information of the fin set:")
177174
print("-------------------------------------")
178175
print(f"Reference rocket radius: {self.aero_surface.rocket_radius:.3f} m")
179-
try:
176+
if hasattr(self.aero_surface, "tip_chord"):
180177
print(f"Tip chord: {self.aero_surface.tip_chord:.3f} m")
181-
except AttributeError:
182-
pass # it isn't a trapezoidal fin, just don't worry about tip chord
183178
print(f"Root chord: {self.aero_surface.root_chord:.3f} m")
184179
print(f"Span: {self.aero_surface.span:.3f} m")
185180
print(

rocketpy/rocket/aero_surface/fins/_base_fin.py

Lines changed: 133 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -44,82 +44,180 @@ def __init__(
4444
self._airfoil = airfoil
4545
self._cant_angle = cant_angle
4646
self._cant_angle_rad = math.radians(cant_angle)
47+
self.geometry = None
4748

4849
self.d = 2 * rocket_radius
4950
self.ref_area = np.pi * rocket_radius**2
5051

5152
super().__init__(name, self.ref_area, self.d)
5253

54+
def _run_geometry_update_chain(self):
55+
"""Recompute all dependent aerodynamic properties after a geometry change."""
56+
self.evaluate_geometrical_parameters()
57+
self.evaluate_center_of_pressure()
58+
self.evaluate_lift_coefficient()
59+
self.evaluate_roll_parameters()
60+
5361
@property
5462
def rocket_radius(self):
63+
"""Rocket radius in meters.
64+
65+
Returns
66+
-------
67+
float
68+
Rocket radius in meters.
69+
"""
5570
return self._rocket_radius
5671

5772
@rocket_radius.setter
5873
def rocket_radius(self, value):
74+
"""Set rocket radius and update dependent properties.
75+
76+
Parameters
77+
----------
78+
value : float
79+
Rocket radius in meters.
80+
"""
5981
self._rocket_radius = value
60-
self.evaluate_geometrical_parameters()
61-
self.evaluate_center_of_pressure()
62-
self.evaluate_lift_coefficient()
63-
self.evaluate_roll_parameters()
82+
self._run_geometry_update_chain()
6483

6584
@property
6685
def root_chord(self):
86+
"""Root chord length in meters.
87+
88+
Returns
89+
-------
90+
float
91+
Root chord length in meters.
92+
"""
6793
return self._root_chord
6894

6995
@root_chord.setter
7096
def root_chord(self, value):
97+
"""Set root chord and update dependent properties.
98+
99+
Parameters
100+
----------
101+
value : float
102+
Root chord length in meters.
103+
"""
71104
self._root_chord = value
72-
self.evaluate_geometrical_parameters()
73-
self.evaluate_center_of_pressure()
74-
self.evaluate_lift_coefficient()
75-
self.evaluate_roll_parameters()
105+
self._run_geometry_update_chain()
76106

77107
@property
78108
def span(self):
109+
"""Fin span in meters.
110+
111+
Returns
112+
-------
113+
float
114+
Fin span in meters.
115+
"""
79116
return self._span
80117

81118
@span.setter
82119
def span(self, value):
120+
"""Set fin span and update dependent properties.
121+
122+
Parameters
123+
----------
124+
value : float
125+
Fin span in meters.
126+
"""
83127
self._span = value
84-
self.evaluate_geometrical_parameters()
85-
self.evaluate_center_of_pressure()
86-
self.evaluate_lift_coefficient()
87-
self.evaluate_roll_parameters()
128+
self._run_geometry_update_chain()
88129

89130
@property
90131
def cant_angle(self):
132+
"""Cant angle in degrees.
133+
134+
Returns
135+
-------
136+
float
137+
Cant angle in degrees.
138+
"""
91139
return self._cant_angle
92140

93141
@cant_angle.setter
94142
def cant_angle(self, value):
143+
"""Set cant angle and update radian representation.
144+
145+
Parameters
146+
----------
147+
value : float
148+
Cant angle in degrees.
149+
"""
95150
self._cant_angle = value
96151
self.cant_angle_rad = math.radians(value)
97152

98153
@property
99154
def cant_angle_rad(self):
155+
"""Cant angle in radians.
156+
157+
Returns
158+
-------
159+
float
160+
Cant angle in radians.
161+
"""
100162
return self._cant_angle_rad
101163

102164
@cant_angle_rad.setter
103165
def cant_angle_rad(self, value):
166+
"""Set cant angle in radians and update dependent properties.
167+
168+
Parameters
169+
----------
170+
value : float
171+
Cant angle in radians.
172+
"""
104173
self._cant_angle_rad = value
105-
self.evaluate_geometrical_parameters()
106-
self.evaluate_center_of_pressure()
107-
self.evaluate_lift_coefficient()
108-
self.evaluate_roll_parameters()
174+
self._run_geometry_update_chain()
109175

110176
@property
111177
def airfoil(self):
178+
"""Airfoil data for the fin.
179+
180+
Returns
181+
-------
182+
tuple or None
183+
Tuple containing airfoil data and unit ('degrees' or 'radians'),
184+
or None if using planar fin.
185+
"""
112186
return self._airfoil
113187

114188
@airfoil.setter
115189
def airfoil(self, value):
190+
"""Set airfoil data and update dependent properties.
191+
192+
Parameters
193+
----------
194+
value : tuple or None
195+
Tuple containing airfoil data and unit ('degrees' or 'radians'),
196+
or None for planar fin.
197+
"""
116198
self._airfoil = value
117-
self.evaluate_geometrical_parameters()
118-
self.evaluate_center_of_pressure()
119-
self.evaluate_lift_coefficient()
120-
self.evaluate_roll_parameters()
199+
self._run_geometry_update_chain()
200+
201+
def info(self):
202+
"""Print fin geometry and lift information."""
203+
self.prints.geometry()
204+
self.prints.lift()
205+
206+
def all_info(self):
207+
"""Print all available fin information and show all fin plots."""
208+
self.prints.all()
209+
self.plots.all()
121210

122211
def evaluate_single_fin_lift_coefficient(self):
212+
"""Evaluate the lift coefficient derivative for a single fin.
213+
214+
Computes the lift coefficient derivative (clalpha) considering the
215+
fin's geometry, airfoil characteristics (if provided), and Mach number
216+
effects using Prandtl-Glauert compressibility correction and
217+
Diederich's planform correlation.
218+
219+
Sets the `clalpha_single_fin` attribute as a Function of Mach number.
220+
"""
123221
if not self.airfoil:
124222
# Defines clalpha2D as 2*pi for planar fins
125223
clalpha2D_incompressible = 2 * np.pi
@@ -133,9 +231,7 @@ def evaluate_single_fin_lift_coefficient(self):
133231
)
134232

135233
# Differentiating at alpha = 0 to get cl_alpha
136-
clalpha2D_incompressible = self.airfoil_cl.differentiate_complex_step(
137-
x=1e-3, dx=1e-3
138-
)
234+
clalpha2D_incompressible = self.airfoil_cl.differentiate(x=1e-3, dx=1e-3)
139235

140236
# Convert to radians if needed
141237
if self.airfoil[1] == "degrees":
@@ -170,24 +266,30 @@ def lift_source(mach):
170266

171267
@abstractmethod
172268
def evaluate_lift_coefficient(self):
173-
pass
269+
"""Evaluate the lift coefficient for the fin."""
174270

175271
@abstractmethod
176272
def evaluate_roll_parameters(self):
177-
pass
273+
"""Evaluate roll-related parameters for the fin."""
178274

179275
@abstractmethod
180276
def evaluate_center_of_pressure(self):
181-
pass
277+
"""Evaluate the center of pressure for the fin."""
182278

183-
@abstractmethod
184279
def evaluate_geometrical_parameters(self):
185-
pass
280+
"""Evaluate geometric parameters of the fin.
281+
282+
This method delegates to the configured geometry strategy.
283+
"""
284+
self.geometry.evaluate_geometrical_parameters()
186285

187-
@abstractmethod
188286
def evaluate_shape(self):
189-
pass
287+
"""Evaluate the shape representation of the fin.
288+
289+
This method delegates to the configured geometry strategy.
290+
"""
291+
self.geometry.evaluate_shape()
190292

191293
@abstractmethod
192294
def draw(self):
193-
pass
295+
"""Draw or render the fin."""

rocketpy/rocket/aero_surface/fins/_free_form_mixin.py

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -114,19 +114,17 @@ def evaluate_geometrical_parameters(self): # pylint: disable=too-many-statement
114114
chord_length[i] += x
115115

116116
# Replace infinities and handle invalid values in chord_lead and chord_trail
117-
for i in range(points_per_line):
118-
if (
119-
np.isinf(chord_lead[i])
120-
or np.isinf(chord_trail[i])
121-
or np.isnan(chord_lead[i])
122-
or np.isnan(chord_trail[i])
123-
):
124-
chord_lead[i] = 0
125-
chord_trail[i] = 0
126-
if chord_length[i] < 0 or np.isnan(chord_length[i]):
127-
chord_length[i] = 0
128-
if chord_length[i] > chord_trail[i] - chord_lead[i]:
129-
chord_length[i] = chord_trail[i] - chord_lead[i]
117+
# Use vectorized operations for better performance
118+
invalid_lead = np.isnan(chord_lead) | np.isinf(chord_lead)
119+
invalid_trail = np.isnan(chord_trail) | np.isinf(chord_trail)
120+
chord_lead[invalid_lead | invalid_trail] = 0
121+
chord_trail[invalid_lead | invalid_trail] = 0
122+
123+
# Clamp chord_length to valid range
124+
chord_length[chord_length < 0] = 0
125+
chord_length[np.isnan(chord_length)] = 0
126+
max_chord = chord_trail - chord_lead
127+
chord_length = np.minimum(chord_length, max_chord)
130128

131129
# Initialize integration variables for various aerodynamic and roll properties
132130
radius = self.rocket_radius

0 commit comments

Comments
 (0)