Skip to content

Commit 1f0535f

Browse files
Merge pull request #321 from RocketPy-Team/enh/improve_initial_sol
MAINT: refactor flight class __init__ method
2 parents b311fed + ac34d57 commit 1f0535f

File tree

1 file changed

+119
-78
lines changed

1 file changed

+119
-78
lines changed

rocketpy/Flight.py

Lines changed: 119 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -544,15 +544,18 @@ def __init__(
544544
heading : int, float, optional
545545
Heading angle relative to north given in degrees.
546546
Default is 90, which points in the x direction.
547-
initialSolution : array, optional
547+
initialSolution : array, Flight, optional
548548
Initial solution array to be used. Format is
549-
initialSolution = [self.tInitial,
550-
xInit, yInit, zInit,
551-
vxInit, vyInit, vzInit,
552-
e0Init, e1Init, e2Init, e3Init,
553-
w1Init, w2Init, w3Init].
554-
If None, the initial solution will start with all null values,
555-
except for the euler parameters which will be calculated based
549+
initialSolution = []
550+
self.tInitial,
551+
xInit, yInit, zInit,
552+
vxInit, vyInit, vzInit,
553+
e0Init, e1Init, e2Init, e3Init,
554+
w1Init, w2Init, w3Init
555+
].
556+
If a Flight object is used, the last state vector will be used as
557+
initial solution. If None, the initial solution will start with
558+
all null values, except for the euler parameters which will be calculated based
556559
on given values of inclination and heading. Default is None.
557560
terminateOnApogee : boolean, optional
558561
Whether to terminate simulation when rocket reaches apogee.
@@ -609,76 +612,11 @@ def __init__(
609612
self.timeOvershoot = timeOvershoot
610613
self.terminateOnApogee = terminateOnApogee
611614

612-
# Modifying Rail Length for a better out of rail condition
613-
upperRButton = max(self.rocket.railButtons[0])
614-
lowerRButton = min(self.rocket.railButtons[0])
615-
nozzle = self.rocket.distanceRocketNozzle
616-
self.effective1RL = self.env.rL - abs(nozzle - upperRButton)
617-
self.effective2RL = self.env.rL - abs(nozzle - lowerRButton)
618-
619615
# Flight initialization
620616
self.__init_post_process_variables()
621617
# Initialize solution monitors
622-
self.outOfRailTime = 0
623-
self.outOfRailTimeIndex = 0
624-
self.outOfRailState = np.array([0])
625-
self.outOfRailVelocity = 0
626-
self.apogeeState = np.array([0])
627-
self.apogeeTime = 0
628-
self.apogeeX = 0
629-
self.apogeeY = 0
630-
self.apogee = 0
631-
self.xImpact = 0
632-
self.yImpact = 0
633-
self.impactVelocity = 0
634-
self.impactState = np.array([0])
635-
self.parachuteEvents = []
636-
self.postProcessed = False
637-
# Initialize solver monitors
638-
self.functionEvaluations = []
639-
self.functionEvaluationsPerTimeStep = []
640-
self.timeSteps = []
641-
# Initialize solution state
642-
self.solution = []
643-
if self.initialSolution is None:
644-
# Initialize time and state variables
645-
self.tInitial = 0
646-
xInit, yInit, zInit = 0, 0, self.env.elevation
647-
vxInit, vyInit, vzInit = 0, 0, 0
648-
w1Init, w2Init, w3Init = 0, 0, 0
649-
# Initialize attitude
650-
psiInit = -heading * (np.pi / 180) # Precession / Heading Angle
651-
thetaInit = (inclination - 90) * (np.pi / 180) # Nutation Angle
652-
e0Init = np.cos(psiInit / 2) * np.cos(thetaInit / 2)
653-
e1Init = np.cos(psiInit / 2) * np.sin(thetaInit / 2)
654-
e2Init = np.sin(psiInit / 2) * np.sin(thetaInit / 2)
655-
e3Init = np.sin(psiInit / 2) * np.cos(thetaInit / 2)
656-
# Store initial conditions
657-
self.initialSolution = [
658-
self.tInitial,
659-
xInit,
660-
yInit,
661-
zInit,
662-
vxInit,
663-
vyInit,
664-
vzInit,
665-
e0Init,
666-
e1Init,
667-
e2Init,
668-
e3Init,
669-
w1Init,
670-
w2Init,
671-
w3Init,
672-
]
673-
# Set initial derivative for rail phase
674-
self.initialDerivative = self.uDotRail1
675-
else:
676-
# Initial solution given, ignore rail phase
677-
# TODO: Check if rocket is actually out of rail. Otherwise, start at rail
678-
self.outOfRailState = self.initialSolution[1:]
679-
self.outOfRailTime = self.initialSolution[0]
680-
self.outOfRailTimeIndex = 0
681-
self.initialDerivative = self.uDot
618+
self.__init_solution_monitors()
619+
self.__init_flight_state()
682620

683621
self.tInitial = self.initialSolution[0]
684622
self.solution.append(self.initialSolution)
@@ -1118,6 +1056,81 @@ def __init__(
11181056
if verbose:
11191057
print("Simulation Completed at Time: {:3.4f} s".format(self.t))
11201058

1059+
def __init_flight_state(self):
1060+
"""Initialize flight state variables."""
1061+
if self.initialSolution is None:
1062+
# Initialize time and state variables
1063+
self.tInitial = 0
1064+
xInit, yInit, zInit = 0, 0, self.env.elevation
1065+
vxInit, vyInit, vzInit = 0, 0, 0
1066+
w1Init, w2Init, w3Init = 0, 0, 0
1067+
# Initialize attitude
1068+
psiInit = -self.heading * (np.pi / 180) # Precession / Heading Angle
1069+
thetaInit = (self.inclination - 90) * (np.pi / 180) # Nutation Angle
1070+
e0Init = np.cos(psiInit / 2) * np.cos(thetaInit / 2)
1071+
e1Init = np.cos(psiInit / 2) * np.sin(thetaInit / 2)
1072+
e2Init = np.sin(psiInit / 2) * np.sin(thetaInit / 2)
1073+
e3Init = np.sin(psiInit / 2) * np.cos(thetaInit / 2)
1074+
# Store initial conditions
1075+
self.initialSolution = [
1076+
self.tInitial,
1077+
xInit,
1078+
yInit,
1079+
zInit,
1080+
vxInit,
1081+
vyInit,
1082+
vzInit,
1083+
e0Init,
1084+
e1Init,
1085+
e2Init,
1086+
e3Init,
1087+
w1Init,
1088+
w2Init,
1089+
w3Init,
1090+
]
1091+
# Set initial derivative for rail phase
1092+
self.initialDerivative = self.uDotRail1
1093+
elif isinstance(self.initialSolution, Flight):
1094+
# Initialize time and state variables based on last solution of
1095+
# previous flight
1096+
self.initialSolution = self.initialSolution.solution[-1]
1097+
# Set unused monitors
1098+
self.outOfRailState = self.initialSolution[1:]
1099+
self.outOfRailTime = self.initialSolution[0]
1100+
# Set initial derivative for 6-DOF flight phase
1101+
self.initialDerivative = self.uDot
1102+
else:
1103+
# Initial solution given, ignore rail phase
1104+
# TODO: Check if rocket is actually out of rail. Otherwise, start at rail
1105+
self.outOfRailState = self.initialSolution[1:]
1106+
self.outOfRailTime = self.initialSolution[0]
1107+
self.outOfRailTimeIndex = 0
1108+
self.initialDerivative = self.uDot
1109+
1110+
def __init_solution_monitors(self):
1111+
"""Initialize solution monitors."""
1112+
self.outOfRailTime = 0
1113+
self.outOfRailTimeIndex = 0
1114+
self.outOfRailState = np.array([0])
1115+
self.outOfRailVelocity = 0
1116+
self.apogeeState = np.array([0])
1117+
self.apogeeTime = 0
1118+
self.apogeeX = 0
1119+
self.apogeeY = 0
1120+
self.apogee = 0
1121+
self.xImpact = 0
1122+
self.yImpact = 0
1123+
self.impactVelocity = 0
1124+
self.impactState = np.array([0])
1125+
self.parachuteEvents = []
1126+
self.postProcessed = False
1127+
# Initialize solver monitors
1128+
self.functionEvaluations = []
1129+
self.functionEvaluationsPerTimeStep = []
1130+
self.timeSteps = []
1131+
# Initialize solution state
1132+
self.solution = []
1133+
11211134
def __init_post_process_variables(self):
11221135
"""Initialize post-process variables."""
11231136
# Initialize all variables calculated after initialization.
@@ -1126,6 +1139,29 @@ def __init_post_process_variables(self):
11261139
self.difference = Function(0)
11271140
self.safetyFactor = Function(0)
11281141

1142+
@cached_property
1143+
def effective1RL(self):
1144+
# Modifying Rail Length for a better out of rail condition
1145+
nozzle = self.rocket.distanceRocketNozzle # Kinda works for single nozzle
1146+
try:
1147+
upperRButton = max(self.rocket.railButtons[0])
1148+
except AttributeError: # If there is no rail button
1149+
upperRButton = nozzle
1150+
effective1RL = self.env.rL - abs(nozzle - upperRButton)
1151+
1152+
return effective1RL
1153+
1154+
@cached_property
1155+
def effective2RL(self):
1156+
# Modifying Rail Length for a better out of rail condition
1157+
nozzle = self.rocket.distanceRocketNozzle
1158+
try:
1159+
lowerRButton = min(self.rocket.railButtons[0])
1160+
except AttributeError:
1161+
lowerRButton = nozzle
1162+
effective2RL = self.env.rL - abs(nozzle - lowerRButton)
1163+
return effective2RL
1164+
11291165
def uDotRail1(self, t, u, postProcessing=False):
11301166
"""Calculates derivative of u state vector with respect to time
11311167
when rocket is flying in 1 DOF motion in the rail.
@@ -2309,7 +2345,9 @@ def retrieve_acceleration_arrays(self):
23092345
callback(self)
23102346
# Loop through time steps in flight phase
23112347
for step in self.solution: # Can be optimized
2312-
if initTime < step[0] <= finalTime:
2348+
if initTime < step[0] <= finalTime or (
2349+
initTime == self.tInitial and step[0] == self.tInitial
2350+
):
23132351
# Get derivatives
23142352
uDot = currentDerivative(step[0], step[1:])
23152353
# Get accelerations
@@ -2397,7 +2435,9 @@ def retrieve_temporary_values_arrays(self):
23972435
callback(self)
23982436
# Loop through time steps in flight phase
23992437
for step in self.solution: # Can be optimized
2400-
if initTime < step[0] <= finalTime or (initTime == 0 and step[0] == 0):
2438+
if initTime < step[0] <= finalTime or (
2439+
initTime == self.tInitial and step[0] == self.tInitial
2440+
):
24012441
# Call derivatives in post processing mode
24022442
uDot = currentDerivative(step[0], step[1:], postProcessing=True)
24032443

@@ -3171,7 +3211,8 @@ def plotTrajectoryForceData(self):
31713211
eventTimeIndex = -1
31723212

31733213
# Rail Button Forces
3174-
fig6 = plt.figure(figsize=(9, 6))
3214+
if self.rocket.railButtons is not None:
3215+
fig6 = plt.figure(figsize=(9, 6))
31753216

31763217
ax1 = plt.subplot(211)
31773218
ax1.plot(

0 commit comments

Comments
 (0)