Skip to content

Commit 4145feb

Browse files
caioessouzaGui-FernandesBR
authored andcommitted
ENH: Enable only radial burning
1 parent 170e89c commit 4145feb

File tree

7 files changed

+233
-57
lines changed

7 files changed

+233
-57
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ Attention: The newest changes should be on top -->
3232

3333
### Added
3434

35+
- ENH: Enable only radial burning [#815](https://github.com/RocketPy-Team/RocketPy/pull/815)
36+
3537
### Changed
3638

3739
### Fixed
@@ -71,6 +73,7 @@ Attention: The newest changes should be on top -->
7173
## [v1.10.0] - 2025-05-16
7274

7375
### Added
76+
7477
- ENH: Support for ND arithmetic in Function class. [#810] (https://github.com/RocketPy-Team/RocketPy/pull/810)
7578
- ENH: allow users to provide custom samplers [#803](https://github.com/RocketPy-Team/RocketPy/pull/803)
7679
- ENH: Implement Multivariate Rejection Sampling (MRS) [#738] (https://github.com/RocketPy-Team/RocketPy/pull/738)

rocketpy/motors/hybrid_motor.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,8 +193,12 @@ class HybridMotor(Motor):
193193
HybridMotor.reference_pressure : int, float
194194
Atmospheric pressure in Pa at which the thrust data was recorded.
195195
It will allow to obtain the net thrust in the Flight class.
196+
SolidMotor.only_radial_burn : bool
197+
If True, grain regression is restricted to radial burn only (inner radius growth).
198+
Grain length remains constant throughout the burn. Default is False.
196199
"""
197200

201+
# pylint: disable=too-many-arguments
198202
def __init__( # pylint: disable=too-many-arguments
199203
self,
200204
thrust_source,
@@ -216,6 +220,7 @@ def __init__( # pylint: disable=too-many-arguments
216220
interpolation_method="linear",
217221
coordinate_system_orientation="nozzle_to_combustion_chamber",
218222
reference_pressure=None,
223+
only_radial_burn=True,
219224
):
220225
"""Initialize Motor class, process thrust curve and geometrical
221226
parameters and store results.
@@ -313,6 +318,11 @@ class Function. Thrust units are Newtons.
313318
"nozzle_to_combustion_chamber".
314319
reference_pressure : int, float, optional
315320
Atmospheric pressure in Pa at which the thrust data was recorded.
321+
only_radial_burn : boolean, optional
322+
If True, inhibits the grain from burning axially, only computing
323+
radial burn. If False, allows the grain to also burn
324+
axially. May be useful for axially inhibited grains or hybrid motors.
325+
Default is False.
316326
317327
Returns
318328
-------
@@ -364,6 +374,7 @@ class Function. Thrust units are Newtons.
364374
interpolation_method,
365375
coordinate_system_orientation,
366376
reference_pressure,
377+
only_radial_burn,
367378
)
368379

369380
self.positioned_tanks = self.liquid.positioned_tanks

rocketpy/motors/solid_motor.py

Lines changed: 96 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,9 @@ class SolidMotor(Motor):
193193
SolidMotor.reference_pressure : int, float
194194
Atmospheric pressure in Pa at which the thrust data was recorded.
195195
It will allow to obtain the net thrust in the Flight class.
196+
SolidMotor.only_radial_burn : bool
197+
If True, grain regression is restricted to radial burn only (inner radius growth).
198+
Grain length remains constant throughout the burn. Default is False.
196199
"""
197200

198201
# pylint: disable=too-many-arguments
@@ -217,6 +220,7 @@ def __init__(
217220
interpolation_method="linear",
218221
coordinate_system_orientation="nozzle_to_combustion_chamber",
219222
reference_pressure=None,
223+
only_radial_burn=False,
220224
):
221225
"""Initialize Motor class, process thrust curve and geometrical
222226
parameters and store results.
@@ -314,11 +318,19 @@ class Function. Thrust units are Newtons.
314318
"nozzle_to_combustion_chamber".
315319
reference_pressure : int, float, optional
316320
Atmospheric pressure in Pa at which the thrust data was recorded.
321+
only_radial_burn : boolean, optional
322+
If True, inhibits the grain from burning axially, only computing
323+
radial burn. If False, allows the grain to also burn
324+
axially. May be useful for axially inhibited grains or hybrid motors.
325+
Default is False.
317326
318327
Returns
319328
-------
320329
None
321330
"""
331+
# Store before calling super().__init__() since it calls evaluate_geometry()
332+
self.only_radial_burn = only_radial_burn
333+
322334
super().__init__(
323335
thrust_source=thrust_source,
324336
dry_inertia=dry_inertia,
@@ -500,17 +512,25 @@ def geometry_dot(t, y):
500512

501513
# Compute state vector derivative
502514
grain_inner_radius, grain_height = y
503-
burn_area = (
504-
2
505-
* np.pi
506-
* (
507-
grain_outer_radius**2
508-
- grain_inner_radius**2
509-
+ grain_inner_radius * grain_height
515+
if self.only_radial_burn:
516+
burn_area = 2 * np.pi * (grain_inner_radius * grain_height)
517+
518+
grain_inner_radius_derivative = -volume_diff / burn_area
519+
grain_height_derivative = 0 # Set to zero to disable axial burning
520+
521+
else:
522+
burn_area = (
523+
2
524+
* np.pi
525+
* (
526+
grain_outer_radius**2
527+
- grain_inner_radius**2
528+
+ grain_inner_radius * grain_height
529+
)
510530
)
511-
)
512-
grain_inner_radius_derivative = -volume_diff / burn_area
513-
grain_height_derivative = -2 * grain_inner_radius_derivative
531+
532+
grain_inner_radius_derivative = -volume_diff / burn_area
533+
grain_height_derivative = -2 * grain_inner_radius_derivative
514534

515535
return [grain_inner_radius_derivative, grain_height_derivative]
516536

@@ -521,32 +541,55 @@ def geometry_jacobian(t, y):
521541

522542
# Compute jacobian
523543
grain_inner_radius, grain_height = y
524-
factor = volume_diff / (
525-
2
526-
* np.pi
527-
* (
528-
grain_outer_radius**2
529-
- grain_inner_radius**2
530-
+ grain_inner_radius * grain_height
544+
if self.only_radial_burn:
545+
factor = volume_diff / (
546+
2 * np.pi * (grain_inner_radius * grain_height) ** 2
531547
)
532-
** 2
533-
)
534-
inner_radius_derivative_wrt_inner_radius = factor * (
535-
grain_height - 2 * grain_inner_radius
536-
)
537-
inner_radius_derivative_wrt_height = factor * grain_inner_radius
538-
height_derivative_wrt_inner_radius = (
539-
-2 * inner_radius_derivative_wrt_inner_radius
540-
)
541-
height_derivative_wrt_height = -2 * inner_radius_derivative_wrt_height
542548

543-
return [
544-
[
545-
inner_radius_derivative_wrt_inner_radius,
546-
inner_radius_derivative_wrt_height,
547-
],
548-
[height_derivative_wrt_inner_radius, height_derivative_wrt_height],
549-
]
549+
inner_radius_derivative_wrt_inner_radius = factor * (
550+
grain_height - 2 * grain_inner_radius
551+
)
552+
inner_radius_derivative_wrt_height = 0
553+
height_derivative_wrt_inner_radius = 0
554+
height_derivative_wrt_height = 0
555+
# Height is a constant, so all the derivatives with respect to it are set to zero
556+
557+
return [
558+
[
559+
inner_radius_derivative_wrt_inner_radius,
560+
inner_radius_derivative_wrt_height,
561+
],
562+
[height_derivative_wrt_inner_radius, height_derivative_wrt_height],
563+
]
564+
565+
else:
566+
factor = volume_diff / (
567+
2
568+
* np.pi
569+
* (
570+
grain_outer_radius**2
571+
- grain_inner_radius**2
572+
+ grain_inner_radius * grain_height
573+
)
574+
** 2
575+
)
576+
577+
inner_radius_derivative_wrt_inner_radius = factor * (
578+
grain_height - 2 * grain_inner_radius
579+
)
580+
inner_radius_derivative_wrt_height = factor * grain_inner_radius
581+
height_derivative_wrt_inner_radius = (
582+
-2 * inner_radius_derivative_wrt_inner_radius
583+
)
584+
height_derivative_wrt_height = -2 * inner_radius_derivative_wrt_height
585+
586+
return [
587+
[
588+
inner_radius_derivative_wrt_inner_radius,
589+
inner_radius_derivative_wrt_height,
590+
],
591+
[height_derivative_wrt_inner_radius, height_derivative_wrt_height],
592+
]
550593

551594
def terminate_burn(t, y): # pylint: disable=unused-argument
552595
end_function = (self.grain_outer_radius - y[0]) * y[1]
@@ -597,16 +640,24 @@ def burn_area(self):
597640
burn_area : Function
598641
Function representing the burn area progression with the time.
599642
"""
600-
burn_area = (
601-
2
602-
* np.pi
603-
* (
604-
self.grain_outer_radius**2
605-
- self.grain_inner_radius**2
606-
+ self.grain_inner_radius * self.grain_height
643+
if self.only_radial_burn:
644+
burn_area = (
645+
2
646+
* np.pi
647+
* (self.grain_inner_radius * self.grain_height)
648+
* self.grain_number
649+
)
650+
else:
651+
burn_area = (
652+
2
653+
* np.pi
654+
* (
655+
self.grain_outer_radius**2
656+
- self.grain_inner_radius**2
657+
+ self.grain_inner_radius * self.grain_height
658+
)
659+
* self.grain_number
607660
)
608-
* self.grain_number
609-
)
610661
return burn_area
611662

612663
@funcify_method("Time (s)", "burn rate (m/s)")
@@ -778,6 +829,7 @@ def to_dict(self, **kwargs):
778829
"grain_initial_height": self.grain_initial_height,
779830
"grain_separation": self.grain_separation,
780831
"grains_center_of_mass_position": self.grains_center_of_mass_position,
832+
"only_radial_burn": self.only_radial_burn,
781833
}
782834
)
783835

@@ -827,4 +879,5 @@ def from_dict(cls, data):
827879
interpolation_method=data["interpolate"],
828880
coordinate_system_orientation=data["coordinate_system_orientation"],
829881
reference_pressure=data.get("reference_pressure"),
882+
only_radial_burn=data.get("only_radial_burn", False),
830883
)

tests/fixtures/motor/hybrid_fixtures.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55

66
@pytest.fixture
7-
def hybrid_motor(spherical_oxidizer_tank):
7+
def hybrid_motor(oxidizer_tank):
88
"""An example of a hybrid motor with spherical oxidizer
99
tank and fuel grains.
1010
@@ -35,6 +35,6 @@ def hybrid_motor(spherical_oxidizer_tank):
3535
grains_center_of_mass_position=-0.1,
3636
)
3737

38-
motor.add_tank(spherical_oxidizer_tank, position=0.3)
38+
motor.add_tank(oxidizer_tank, position=0.3)
3939

4040
return motor
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
from unittest.mock import patch
2+
3+
4+
@patch("matplotlib.pyplot.show")
5+
def test_solid_motor_info(mock_show, cesaroni_m1670): # pylint: disable=unused-argument
6+
"""Tests the SolidMotor.all_info() method.
7+
8+
Parameters
9+
----------
10+
mock_show : mock
11+
Mock of the matplotlib.pyplot.show function.
12+
cesaroni_m1670 : rocketpy.SolidMotor
13+
The SolidMotor object to be used in the tests.
14+
"""
15+
assert cesaroni_m1670.info() is None
16+
assert cesaroni_m1670.all_info() is None

0 commit comments

Comments
 (0)