@@ -544,15 +544,18 @@ def __init__(
544
544
heading : int, float, optional
545
545
Heading angle relative to north given in degrees.
546
546
Default is 90, which points in the x direction.
547
- initialSolution : array, optional
547
+ initialSolution : array, Flight, optional
548
548
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
556
559
on given values of inclination and heading. Default is None.
557
560
terminateOnApogee : boolean, optional
558
561
Whether to terminate simulation when rocket reaches apogee.
@@ -609,76 +612,11 @@ def __init__(
609
612
self .timeOvershoot = timeOvershoot
610
613
self .terminateOnApogee = terminateOnApogee
611
614
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
-
619
615
# Flight initialization
620
616
self .__init_post_process_variables ()
621
617
# 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 ()
682
620
683
621
self .tInitial = self .initialSolution [0 ]
684
622
self .solution .append (self .initialSolution )
@@ -1118,6 +1056,81 @@ def __init__(
1118
1056
if verbose :
1119
1057
print ("Simulation Completed at Time: {:3.4f} s" .format (self .t ))
1120
1058
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
+
1121
1134
def __init_post_process_variables (self ):
1122
1135
"""Initialize post-process variables."""
1123
1136
# Initialize all variables calculated after initialization.
@@ -1126,6 +1139,29 @@ def __init_post_process_variables(self):
1126
1139
self .difference = Function (0 )
1127
1140
self .safetyFactor = Function (0 )
1128
1141
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
+
1129
1165
def uDotRail1 (self , t , u , postProcessing = False ):
1130
1166
"""Calculates derivative of u state vector with respect to time
1131
1167
when rocket is flying in 1 DOF motion in the rail.
@@ -2309,7 +2345,9 @@ def retrieve_acceleration_arrays(self):
2309
2345
callback (self )
2310
2346
# Loop through time steps in flight phase
2311
2347
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
+ ):
2313
2351
# Get derivatives
2314
2352
uDot = currentDerivative (step [0 ], step [1 :])
2315
2353
# Get accelerations
@@ -2397,7 +2435,9 @@ def retrieve_temporary_values_arrays(self):
2397
2435
callback (self )
2398
2436
# Loop through time steps in flight phase
2399
2437
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
+ ):
2401
2441
# Call derivatives in post processing mode
2402
2442
uDot = currentDerivative (step [0 ], step [1 :], postProcessing = True )
2403
2443
@@ -3171,7 +3211,8 @@ def plotTrajectoryForceData(self):
3171
3211
eventTimeIndex = - 1
3172
3212
3173
3213
# 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 ))
3175
3216
3176
3217
ax1 = plt .subplot (211 )
3177
3218
ax1 .plot (
0 commit comments