From 6987481f56074dab32357bb41b3e71966078cdd4 Mon Sep 17 00:00:00 2001 From: giovaniceotto Date: Wed, 14 Sep 2022 08:15:17 -0300 Subject: [PATCH 1/5] ENH: allow Flight instance to be used as initialSolution --- rocketpy/Flight.py | 110 ++++++++++++++++++++++++++------------------- 1 file changed, 63 insertions(+), 47 deletions(-) diff --git a/rocketpy/Flight.py b/rocketpy/Flight.py index 49a473e33..45ffebfb6 100644 --- a/rocketpy/Flight.py +++ b/rocketpy/Flight.py @@ -539,15 +539,18 @@ def __init__( heading : int, float, optional Heading angle relative to north given in degrees. Default is 90, which points in the x direction. - initialSolution : array, optional + initialSolution : array, Flight, optional Initial solution array to be used. Format is - initialSolution = [self.tInitial, - xInit, yInit, zInit, - vxInit, vyInit, vzInit, - e0Init, e1Init, e2Init, e3Init, - w1Init, w2Init, w3Init]. - If None, the initial solution will start with all null values, - except for the euler parameters which will be calculated based + initialSolution = [] + self.tInitial, + xInit, yInit, zInit, + vxInit, vyInit, vzInit, + e0Init, e1Init, e2Init, e3Init, + w1Init, w2Init, w3Init + ]. + If a Flight object is used, the last state vector will be used as + initial solution. If None, the initial solution will start with + all null values, except for the euler parameters which will be calculated based on given values of inclination and heading. Default is None. terminateOnApogee : boolean, optional Whether to terminate simulation when rocket reaches apogee. @@ -636,45 +639,8 @@ def __init__( self.timeSteps = [] # Initialize solution state self.solution = [] - if self.initialSolution is None: - # Initialize time and state variables - self.tInitial = 0 - xInit, yInit, zInit = 0, 0, self.env.elevation - vxInit, vyInit, vzInit = 0, 0, 0 - w1Init, w2Init, w3Init = 0, 0, 0 - # Initialize attitude - psiInit = -heading * (np.pi / 180) # Precession / Heading Angle - thetaInit = (inclination - 90) * (np.pi / 180) # Nutation Angle - e0Init = np.cos(psiInit / 2) * np.cos(thetaInit / 2) - e1Init = np.cos(psiInit / 2) * np.sin(thetaInit / 2) - e2Init = np.sin(psiInit / 2) * np.sin(thetaInit / 2) - e3Init = np.sin(psiInit / 2) * np.cos(thetaInit / 2) - # Store initial conditions - self.initialSolution = [ - self.tInitial, - xInit, - yInit, - zInit, - vxInit, - vyInit, - vzInit, - e0Init, - e1Init, - e2Init, - e3Init, - w1Init, - w2Init, - w3Init, - ] - # Set initial derivative for rail phase - self.initialDerivative = self.uDotRail1 - else: - # Initial solution given, ignore rail phase - # TODO: Check if rocket is actually out of rail. Otherwise, start at rail - self.outOfRailState = self.initialSolution[1:] - self.outOfRailTime = self.initialSolution[0] - self.initialDerivative = self.uDot - + self.__init_flight_state() + self.tInitial = self.initialSolution[0] self.solution.append(self.initialSolution) self.t = self.solution[-1][0] @@ -1191,6 +1157,56 @@ def __init_post_process_variables(self): self.difference = Function(0) self.safetyFactor = Function(0) + def __init_flight_state(self): + """Initialize flight state.""" + if self.initialSolution is None: + # Initialize time and state variables based on heading and inclination + self.tInitial = 0 + xInit, yInit, zInit = 0, 0, self.env.elevation + vxInit, vyInit, vzInit = 0, 0, 0 + w1Init, w2Init, w3Init = 0, 0, 0 + # Initialize attitude + psiInit = -self.heading * (np.pi / 180) # Precession / self. Angle + thetaInit = (self.inclination - 90) * (np.pi / 180) # Nutation Angle + e0Init = np.cos(psiInit / 2) * np.cos(thetaInit / 2) + e1Init = np.cos(psiInit / 2) * np.sin(thetaInit / 2) + e2Init = np.sin(psiInit / 2) * np.sin(thetaInit / 2) + e3Init = np.sin(psiInit / 2) * np.cos(thetaInit / 2) + # Store initial conditions + self.initialSolution = [ + self.tInitial, + xInit, + yInit, + zInit, + vxInit, + vyInit, + vzInit, + e0Init, + e1Init, + e2Init, + e3Init, + w1Init, + w2Init, + w3Init, + ] + # Set initial derivative for rail phase + self.initialDerivative = self.uDotRail1 + elif isinstance(self.initialSolution, Flight): + # Initialize time and state variables based on last solution of + # previous flight + self.initialSolution = self.initialSolution.solution[-1] + # Set unused monitors + self.outOfRailState = self.initialSolution[1:] + self.outOfRailTime = self.initialSolution[0] + # Set initial derivative for 6-DOF flight phase + self.initialDerivative = self.uDot + else: + # Initial solution given, ignore rail phase + # TODO: Check if rocket is actually out of rail. Otherwise, start at rail + self.outOfRailState = self.initialSolution[1:] + self.outOfRailTime = self.initialSolution[0] + self.initialDerivative = self.uDot + def uDotRail1(self, t, u, postProcessing=False): """Calculates derivative of u state vector with respect to time when rocket is flying in 1 DOF motion in the rail. From ea8e8e885a56e85e563a5cb5f03503bd10071edd Mon Sep 17 00:00:00 2001 From: giovaniceotto Date: Wed, 14 Sep 2022 08:19:17 -0300 Subject: [PATCH 2/5] MAINT: refactor solution monitors initialization --- rocketpy/Flight.py | 50 +++++++++++++++++++++++++--------------------- 1 file changed, 27 insertions(+), 23 deletions(-) diff --git a/rocketpy/Flight.py b/rocketpy/Flight.py index 45ffebfb6..667a87e9a 100644 --- a/rocketpy/Flight.py +++ b/rocketpy/Flight.py @@ -616,29 +616,7 @@ def __init__( # Flight initialization self.__init_post_process_variables() - # Initialize solution monitors - self.outOfRailTime = 0 - self.outOfRailState = np.array([0]) - self.outOfRailVelocity = 0 - self.apogeeState = np.array([0]) - self.apogeeTime = 0 - self.apogeeX = 0 - self.apogeeY = 0 - self.apogee = 0 - self.xImpact = 0 - self.yImpact = 0 - self.impactVelocity = 0 - self.impactState = np.array([0]) - self.parachuteEvents = [] - self.postProcessed = False - self.latitude = 0 # Function(0) - self.longitude = 0 # Function(0) - # Initialize solver monitors - self.functionEvaluations = [] - self.functionEvaluationsPerTimeStep = [] - self.timeSteps = [] - # Initialize solution state - self.solution = [] + self.__init_solution_monitors() self.__init_flight_state() self.tInitial = self.initialSolution[0] @@ -1157,6 +1135,32 @@ def __init_post_process_variables(self): self.difference = Function(0) self.safetyFactor = Function(0) + def __init_solution_monitors(self): + """Initialize all variables related to solution monitoring.""" + # Initialize trajectory monitors + self.outOfRailTime = 0 + self.outOfRailState = np.array([0]) + self.outOfRailVelocity = 0 + self.apogeeState = np.array([0]) + self.apogeeTime = 0 + self.apogeeX = 0 + self.apogeeY = 0 + self.apogee = 0 + self.xImpact = 0 + self.yImpact = 0 + self.impactVelocity = 0 + self.impactState = np.array([0]) + self.parachuteEvents = [] + self.postProcessed = False + self.latitude = 0 # Function(0) + self.longitude = 0 # Function(0) + # Initialize solver monitors + self.functionEvaluations = [] + self.functionEvaluationsPerTimeStep = [] + self.timeSteps = [] + # Initialize solution state + self.solution = [] + def __init_flight_state(self): """Initialize flight state.""" if self.initialSolution is None: From 171ffbbe55d1eb4ec6056bf370a2a3ee03c287e5 Mon Sep 17 00:00:00 2001 From: Lint Action Date: Wed, 14 Sep 2022 11:25:14 +0000 Subject: [PATCH 3/5] Fix code style issues with Black --- rocketpy/Flight.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rocketpy/Flight.py b/rocketpy/Flight.py index 667a87e9a..4b7fa87c1 100644 --- a/rocketpy/Flight.py +++ b/rocketpy/Flight.py @@ -618,7 +618,7 @@ def __init__( self.__init_post_process_variables() self.__init_solution_monitors() self.__init_flight_state() - + self.tInitial = self.initialSolution[0] self.solution.append(self.initialSolution) self.t = self.solution[-1][0] From 19dc471dc59aa24dfc4d9c40aa567d4a96a89736 Mon Sep 17 00:00:00 2001 From: Guilherme Fernandes Alves Date: Sat, 24 Sep 2022 13:52:37 +0200 Subject: [PATCH 4/5] FIX: refactoring initialSolution array Co-authored-by: MateusStano <69485049+MateusStano@users.noreply.github.com> --- rocketpy/Flight.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rocketpy/Flight.py b/rocketpy/Flight.py index 4b7fa87c1..386af6c0c 100644 --- a/rocketpy/Flight.py +++ b/rocketpy/Flight.py @@ -541,7 +541,7 @@ def __init__( Default is 90, which points in the x direction. initialSolution : array, Flight, optional Initial solution array to be used. Format is - initialSolution = [] + initialSolution = [ self.tInitial, xInit, yInit, zInit, vxInit, vyInit, vzInit, From 48490639d1cdaaee18661f8641380c565ebfa788 Mon Sep 17 00:00:00 2001 From: Gui-FernandesBR Date: Tue, 27 Sep 2022 16:42:20 +0200 Subject: [PATCH 5/5] MAINT: add tests TODO so we don't forget --- rocketpy/Flight.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/rocketpy/Flight.py b/rocketpy/Flight.py index 386af6c0c..8040e7304 100644 --- a/rocketpy/Flight.py +++ b/rocketpy/Flight.py @@ -1161,6 +1161,7 @@ def __init_solution_monitors(self): # Initialize solution state self.solution = [] + # TODO: Need unit tests def __init_flight_state(self): """Initialize flight state.""" if self.initialSolution is None: @@ -1195,7 +1196,9 @@ def __init_flight_state(self): ] # Set initial derivative for rail phase self.initialDerivative = self.uDotRail1 + # TODO: Need unit tests elif isinstance(self.initialSolution, Flight): + # TODO: Add optional argument to specify wether to merge/concatenate flight or not # Initialize time and state variables based on last solution of # previous flight self.initialSolution = self.initialSolution.solution[-1]