Skip to content

Commit e431490

Browse files
committed
ENH: move weathercocking_coeff to PointMassRockt
1 parent d9c79ba commit e431490

6 files changed

Lines changed: 35 additions & 42 deletions

File tree

docs/user/three_dof_simulation.rst

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -381,7 +381,7 @@ The ``weathercock_coeff`` Parameter
381381
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
382382

383383
The weathercocking behavior is controlled by the ``weathercock_coeff`` parameter
384-
in the :class:`rocketpy.Flight` class:
384+
in the :class:`rocketpy.PointMassRocket` class:
385385

386386
.. jupyter-execute::
387387

@@ -407,18 +407,18 @@ in the :class:`rocketpy.Flight` class:
407407
center_of_mass_without_motor=0.0,
408408
power_off_drag=0.43,
409409
power_on_drag=0.43,
410+
weathercock_coeff=1.0, # Example with weathercocking enabled
410411
)
411412
rocket.add_motor(motor, position=0)
412413

413-
# Flight with weathercocking enabled
414+
# Flight uses the weathercocking configured on the point-mass rocket
414415
flight = Flight(
415416
rocket=rocket,
416417
environment=env,
417418
rail_length=4.2,
418419
inclination=85,
419420
heading=45,
420421
simulation_mode="3 DOF",
421-
weathercock_coeff=1.0, # Example with weathercocking enabled
422422
)
423423

424424
print(f"Apogee: {flight.apogee - env.elevation:.2f} m")
@@ -540,6 +540,7 @@ accuracy.
540540
center_of_mass_without_motor=0,
541541
power_off_drag=0.43,
542542
power_on_drag=0.43,
543+
weathercock_coeff=0.0,
543544
)
544545
rocket_3dof.add_motor(motor_3dof, -1.1356)
545546

@@ -561,6 +562,7 @@ accuracy.
561562

562563
# 3-DOF with no weathercocking
563564
start = time.time()
565+
rocket_3dof.weathercock_coeff = 0.0
564566
flight_3dof_0 = Flight(
565567
rocket=rocket_3dof,
566568
environment=env,
@@ -569,12 +571,12 @@ accuracy.
569571
heading=45,
570572
terminate_on_apogee=True,
571573
simulation_mode="3 DOF",
572-
weathercock_coeff=0.0,
573574
)
574575
time_3dof_0 = time.time() - start
575576

576577
# 3-DOF with default weathercocking
577578
start = time.time()
579+
rocket_3dof.weathercock_coeff = 1.0
578580
flight_3dof_1 = Flight(
579581
rocket=rocket_3dof,
580582
environment=env,
@@ -583,12 +585,12 @@ accuracy.
583585
heading=45,
584586
terminate_on_apogee=True,
585587
simulation_mode="3 DOF",
586-
weathercock_coeff=1.0,
587588
)
588589
time_3dof_1 = time.time() - start
589590

590591
# 3-DOF with high weathercocking
591592
start = time.time()
593+
rocket_3dof.weathercock_coeff = 5.0
592594
flight_3dof_5 = Flight(
593595
rocket=rocket_3dof,
594596
environment=env,
@@ -597,7 +599,6 @@ accuracy.
597599
heading=45,
598600
terminate_on_apogee=True,
599601
simulation_mode="3 DOF",
600-
weathercock_coeff=5.0,
601602
)
602603
time_3dof_5 = time.time() - start
603604

rocketpy/rocket/point_mass_rocket.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ class PointMassRocket(Rocket):
3131
as :class:`rocketpy.Rocket`, including 1D (Mach-only) and 7D
3232
(alpha, beta, mach, reynolds, pitch_rate, yaw_rate, roll_rate)
3333
definitions.
34+
weathercock_coeff : float, optional
35+
Proportionality coefficient for the alignment rate of the point-mass
36+
rocket body axis with the relative wind direction in 3-DOF
37+
simulations. Must be non-negative. Default is 0.0.
3438
3539
Attributes
3640
----------
@@ -63,6 +67,9 @@ class PointMassRocket(Rocket):
6367
Convenience wrapper for power-off drag as a Mach-only function.
6468
power_on_drag_by_mach : Function
6569
Convenience wrapper for power-on drag as a Mach-only function.
70+
weathercock_coeff : float
71+
Proportionality coefficient for weathercocking alignment in 3-DOF
72+
simulations.
6673
"""
6774

6875
def __init__(
@@ -72,6 +79,7 @@ def __init__(
7279
center_of_mass_without_motor: float,
7380
power_off_drag,
7481
power_on_drag,
82+
weathercock_coeff: float = 0.0,
7583
):
7684
self._center_of_mass_without_motor_pointmass = center_of_mass_without_motor
7785
self._center_of_dry_mass_position = center_of_mass_without_motor
@@ -84,6 +92,8 @@ def __init__(
8492
self.dry_I_13 = 0.0
8593
self.dry_I_23 = 0.0
8694

95+
self.weathercock_coeff = float(weathercock_coeff)
96+
8797
# Call base init with safe defaults
8898
super().__init__(
8999
radius=radius,

rocketpy/simulation/flight.py

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -504,7 +504,6 @@ def __init__( # pylint: disable=too-many-arguments,too-many-statements
504504
equations_of_motion="standard",
505505
ode_solver="LSODA",
506506
simulation_mode="6 DOF",
507-
weathercock_coeff=0.0,
508507
):
509508
"""Run a trajectory simulation.
510509
@@ -588,16 +587,6 @@ def __init__( # pylint: disable=too-many-arguments,too-many-statements
588587
A custom ``scipy.integrate.OdeSolver`` can be passed as well.
589588
For more information on the integration methods, see the scipy
590589
documentation [1]_.
591-
weathercock_coeff : float, optional
592-
Proportionality coefficient (rate coefficient) for the alignment rate of the rocket's body axis
593-
with the relative wind direction in 3-DOF simulations, in rad/s. The actual angular velocity
594-
applied to align the rocket is calculated as ``weathercock_coeff * sin(angle)``, where ``angle``
595-
is the angle between the rocket's axis and the wind direction. A higher value means faster alignment
596-
(quasi-static weathercocking). This parameter is only used when simulation_mode is '3 DOF'.
597-
Default is 0.0 to mimic a pure 3-DOF simulation without any weathercocking (fixed attitude).
598-
Set to a positive value to enable quasi-static weathercocking behaviour.
599-
600-
601590
Returns
602591
-------
603592
None
@@ -627,7 +616,6 @@ def __init__( # pylint: disable=too-many-arguments,too-many-statements
627616
self.equations_of_motion = equations_of_motion
628617
self.simulation_mode = simulation_mode
629618
self.ode_solver = ode_solver
630-
self.weathercock_coeff = weathercock_coeff
631619

632620
# Controller initialization
633621
self.__init_controllers()
@@ -2310,7 +2298,8 @@ def u_dot_generalized_3dof(self, t, u, post_processing=False):
23102298
r_dot = [vx, vy, vz]
23112299
# Weathercocking: evolve body axis direction toward relative wind
23122300
# The body z-axis (attitude vector) should align with -freestream_velocity
2313-
if self.weathercock_coeff > 0 and free_stream_speed > 1e-6:
2301+
weathercock_coeff = getattr(self.rocket, "weathercock_coeff", 0.0)
2302+
if weathercock_coeff > 0 and free_stream_speed > 1e-6:
23142303
# Current body z-axis in inertial frame (attitude vector)
23152304
# From rotation matrix: column 3 gives the body z-axis in inertial frame
23162305
body_z_inertial = Vector(
@@ -2342,7 +2331,7 @@ def u_dot_generalized_3dof(self, t, u, post_processing=False):
23422331
sin_angle = min(1.0, max(-1.0, rotation_axis_mag))
23432332

23442333
# Angular velocity magnitude proportional to misalignment angle
2345-
omega_mag = self.weathercock_coeff * sin_angle
2334+
omega_mag = weathercock_coeff * sin_angle
23462335

23472336
# Angular velocity in inertial frame, then transform to body frame
23482337
omega_body = Kt @ (rotation_axis * omega_mag)
@@ -2363,7 +2352,7 @@ def u_dot_generalized_3dof(self, t, u, post_processing=False):
23632352
)
23642353
rotation_axis = perp_axis.unit_vector
23652354
# 180 degree rotation: sin(angle) = 1
2366-
omega_mag = self.weathercock_coeff * 1.0
2355+
omega_mag = weathercock_coeff * 1.0
23672356
omega_body = Kt @ (rotation_axis * omega_mag)
23682357
# else: aligned (dot > 0.999) - no rotation needed, omega_body stays None
23692358

tests/acceptance/test_3dof_flight.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ def test_3dof_weathercocking_coefficient_stored(flight_3dof_with_weathercock):
202202
flight_3dof_with_weathercock : rocketpy.Flight
203203
A 3 DOF flight simulation with weathercocking enabled.
204204
"""
205-
assert flight_3dof_with_weathercock.weathercock_coeff == 1.0
205+
assert flight_3dof_with_weathercock.rocket.weathercock_coeff == 1.0
206206

207207

208208
def test_3dof_flight_post_processing_attributes(flight_3dof_no_weathercock):
@@ -399,6 +399,8 @@ def test_3dof_flight_reproducibility(
399399
acceptance_point_mass_rocket : rocketpy.PointMassRocket
400400
Rocket fixture for testing.
401401
"""
402+
acceptance_point_mass_rocket.weathercock_coeff = 0.5
403+
402404
# Run simulation twice with same parameters
403405
flight1 = Flight(
404406
rocket=acceptance_point_mass_rocket,
@@ -407,7 +409,6 @@ def test_3dof_flight_reproducibility(
407409
inclination=LAUNCH_INCLINATION,
408410
heading=LAUNCH_HEADING,
409411
simulation_mode="3 DOF",
410-
weathercock_coeff=0.5,
411412
)
412413

413414
flight2 = Flight(
@@ -417,7 +418,6 @@ def test_3dof_flight_reproducibility(
417418
inclination=LAUNCH_INCLINATION,
418419
heading=LAUNCH_HEADING,
419420
simulation_mode="3 DOF",
420-
weathercock_coeff=0.5,
421421
)
422422

423423
# Results should be identical
@@ -452,14 +452,14 @@ def test_3dof_flight_different_weathercock_coefficients(
452452
flights = []
453453

454454
for coeff in coefficients:
455+
acceptance_point_mass_rocket.weathercock_coeff = coeff
455456
flight = Flight(
456457
rocket=acceptance_point_mass_rocket,
457458
environment=example_spaceport_env,
458459
rail_length=5.0,
459460
inclination=LAUNCH_INCLINATION,
460461
heading=LAUNCH_HEADING,
461462
simulation_mode="3 DOF",
462-
weathercock_coeff=coeff,
463463
)
464464
flights.append(flight)
465465

tests/fixtures/flight/flight_fixtures.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,7 @@ def acceptance_point_mass_rocket(acceptance_point_mass_motor):
352352
center_of_mass_without_motor=0,
353353
power_off_drag=0.43,
354354
power_on_drag=0.43,
355+
weathercock_coeff=0.0,
355356
)
356357
rocket.add_motor(acceptance_point_mass_motor, position=0)
357358
return rocket
@@ -376,14 +377,14 @@ def flight_3dof_no_weathercock(example_spaceport_env, acceptance_point_mass_rock
376377
rocketpy.Flight
377378
A 3 DOF flight simulation with weathercock_coeff=0.0.
378379
"""
380+
acceptance_point_mass_rocket.weathercock_coeff = 0.0
379381
return Flight(
380382
rocket=acceptance_point_mass_rocket,
381383
environment=example_spaceport_env,
382384
rail_length=5.0,
383385
inclination=LAUNCH_INCLINATION,
384386
heading=LAUNCH_HEADING,
385387
simulation_mode="3 DOF",
386-
weathercock_coeff=0.0,
387388
)
388389

389390

@@ -406,12 +407,12 @@ def flight_3dof_with_weathercock(example_spaceport_env, acceptance_point_mass_ro
406407
rocketpy.Flight
407408
A 3 DOF flight simulation with weathercock_coeff=1.0.
408409
"""
410+
acceptance_point_mass_rocket.weathercock_coeff = 1.0
409411
return Flight(
410412
rocket=acceptance_point_mass_rocket,
411413
environment=example_spaceport_env,
412414
rail_length=5.0,
413415
inclination=LAUNCH_INCLINATION,
414416
heading=LAUNCH_HEADING,
415417
simulation_mode="3 DOF",
416-
weathercock_coeff=1.0,
417418
)

tests/integration/simulation/test_flight_3dof.py

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -59,12 +59,12 @@ def flight_weathercock_zero(example_plain_env, point_mass_rocket):
5959
rocketpy.simulation.flight.Flight
6060
A Flight object configured for 3-DOF with zero weathercock coefficient.
6161
"""
62+
point_mass_rocket.weathercock_coeff = 0.0
6263
return Flight(
6364
rocket=point_mass_rocket,
6465
environment=example_plain_env,
6566
rail_length=1,
6667
simulation_mode="3 DOF",
67-
weathercock_coeff=0.0,
6868
)
6969

7070

@@ -94,12 +94,12 @@ def flight_weathercock_pos(example_plain_env, point_mass_rocket):
9494
rocketpy.simulation.flight.Flight
9595
A Flight object configured for 3-DOF with weathercocking enabled.
9696
"""
97+
point_mass_rocket.weathercock_coeff = 1.0
9798
return Flight(
9899
rocket=point_mass_rocket,
99100
environment=example_plain_env,
100101
rail_length=1,
101102
simulation_mode="3 DOF",
102-
weathercock_coeff=1.0,
103103
)
104104

105105

@@ -169,24 +169,16 @@ def test_invalid_simulation_mode(example_plain_env, calisto):
169169
)
170170

171171

172-
def test_weathercock_coeff_stored(example_plain_env, point_mass_rocket):
173-
"""Tests that the weathercock_coeff parameter is correctly stored.
172+
def test_weathercock_coeff_stored(point_mass_rocket):
173+
"""Tests that weathercock coefficient is stored in PointMassRocket.
174174
175175
Parameters
176176
----------
177-
example_plain_env : rocketpy.Environment
178-
A basic environment fixture for flight simulation.
179177
point_mass_rocket : rocketpy.PointMassRocket
180178
A point mass rocket fixture for 3-DOF simulation.
181179
"""
182-
flight = Flight(
183-
rocket=point_mass_rocket,
184-
environment=example_plain_env,
185-
rail_length=1,
186-
simulation_mode="3 DOF",
187-
weathercock_coeff=2.5,
188-
)
189-
assert flight.weathercock_coeff == 2.5
180+
point_mass_rocket.weathercock_coeff = 2.5
181+
assert point_mass_rocket.weathercock_coeff == 2.5
190182

191183

192184
def test_weathercock_coeff_default(flight_3dof):
@@ -197,7 +189,7 @@ def test_weathercock_coeff_default(flight_3dof):
197189
flight_3dof : rocketpy.Flight
198190
A Flight object for a 3-DOF simulation, provided by the flight_3dof fixture.
199191
"""
200-
assert flight_3dof.weathercock_coeff == 0.0
192+
assert flight_3dof.rocket.weathercock_coeff == 0.0
201193

202194

203195
def test_point_mass_rocket_3dof_uses_7d_drag_inputs(

0 commit comments

Comments
 (0)