diff --git a/rocketpy/simulation/flight.py b/rocketpy/simulation/flight.py index 999de8a3c..97ffbc88c 100644 --- a/rocketpy/simulation/flight.py +++ b/rocketpy/simulation/flight.py @@ -3276,7 +3276,19 @@ def time_iterator(self, node_list): i += 1 class FlightPhases: - def __init__(self, init_list=[]): + """Class to handle flight phases. It is used to store the derivatives + and callbacks for each flight phase. It is also used to handle the + insertion of flight phases in the correct order, according to their + initial time. + + Attributes + ---------- + list : list + A list of FlightPhase objects. See FlightPhase subclass. + """ + + def __init__(self, init_list=None): + init_list = [] if init_list is None else init_list self.list = init_list[:] def __getitem__(self, index): @@ -3288,102 +3300,169 @@ def __len__(self): def __repr__(self): return str(self.list) + def display_warning(self, *messages): + """A simple function to print a warning message.""" + print("WARNING:", *messages) + def add(self, flight_phase, index=None): + """Add a flight phase to the list. It will be inserted in the + correct position, according to its initial time. If no index is + provided, it will be appended to the end of the list. If by any + reason the flight phase cannot be inserted in the correct position, + a warning will be displayed and the flight phase will be inserted + in the closest position possible. + + Parameters + ---------- + flight_phase : FlightPhase + The flight phase object to be added. See FlightPhase class. + index : int, optional + The index of the flight phase to be added. If no index is + provided, the flight phase will be appended to the end of the + list. Default is None. + + Returns + ------- + None + """ # Handle first phase if len(self.list) == 0: self.list.append(flight_phase) + return None + # Handle appending to last position - elif index is None: - # Check if new flight phase respects time + if index is None: previous_phase = self.list[-1] if flight_phase.t > previous_phase.t: - # All good! Add phase. self.list.append(flight_phase) - elif flight_phase.t == previous_phase.t: - print( - "WARNING: Trying to add a flight phase starting " - + "together with the one preceding it." - ) - print( - "This may be caused by more than when parachute being " - + "triggered simultaneously." - ) - flight_phase.t += 1e-7 - self.add(flight_phase) - elif flight_phase.t < previous_phase.t: - print( - "WARNING: Trying to add a flight phase starting before " - + "the one preceding it." - ) - print( - "This may be caused by more than when parachute being " - + "triggered simultaneously." - ) - print("Or by having a negative parachute lag.") - self.add(flight_phase, -2) - # Handle inserting into intermediary position - else: - # Check if new flight phase respects time - next_phase = self.list[index] - previous_phase = self.list[index - 1] - if previous_phase.t < flight_phase.t < next_phase.t: - # All good! Add phase. - self.list.insert(index, flight_phase) - elif flight_phase.t < previous_phase.t: - print( - "WARNING: Trying to add a flight phase starting before " - + "the one preceding it." - ) - print( - "This may be caused by more than when parachute being " - + "triggered simultaneously." - ) - print("Or by having a negative parachute lag.") - self.add(flight_phase, index - 1) - elif flight_phase.t == previous_phase.t: - print( - "WARNING: Trying to add a flight phase starting " - + "together with the one preceding it." - ) - print( - "This may be caused by more than when parachute being " - + "triggered simultaneously." - ) - flight_phase.t += 1e-7 - self.add(flight_phase, index) - elif flight_phase.t == next_phase.t: - print( - "WARNING: Trying to add a flight phase starting " - + "together with the one proceeding it." - ) - print( - "This may be caused by more than when parachute being " - + "triggered simultaneously." - ) - flight_phase.t += 1e-7 - self.add(flight_phase, index + 1) - elif flight_phase.t > next_phase.t: - print( - "WARNING: Trying to add a flight phase starting after " - + "the one proceeding it." + return None + warning_msg = ( + ( + "Trying to add flight phase starting together with the one preceding it. ", + "This may be caused by multiple parachutes being triggered simultaneously.", ) - print( - "This may be caused by more than when parachute being " - + "triggered simultaneously." + if flight_phase.t == previous_phase.t + else ( + "Trying to add flight phase starting *before* the one *preceding* it. ", + "This may be caused by multiple parachutes being triggered simultaneously", + " or by having a negative parachute lag.", ) - self.add(flight_phase, index + 1) - - def add_phase(self, t, derivatives=None, callback=[], clear=True, index=None): + ) + self.display_warning(*warning_msg) + flight_phase.t += 1e-7 if flight_phase.t == previous_phase.t else 0 + self.add( + flight_phase, -2 if flight_phase.t < previous_phase.t else None + ) + return None + + # Handle inserting into intermediary position. + # Check if new flight phase respects time + next_phase = self.list[index] + previous_phase = self.list[index - 1] + if previous_phase.t < flight_phase.t < next_phase.t: + self.list.insert(index, flight_phase) + return None + warning_msg = ( + ( + "Trying to add flight phase starting *together* with the one *preceding* it. ", + "This may be caused by multiple parachutes being triggered simultaneously.", + ) + if flight_phase.t == previous_phase.t + else ( + "Trying to add flight phase starting *together* with the one *proceeding* it. ", + "This may be caused by multiple parachutes being triggered simultaneously.", + ) + if flight_phase.t == next_phase.t + else ( + "Trying to add flight phase starting *before* the one *preceding* it. ", + "This may be caused by multiple parachutes being triggered simultaneously", + " or by having a negative parachute lag.", + ) + if flight_phase.t < previous_phase.t + else ( + "Trying to add flight phase starting *after* the one *proceeding* it.", + "This may be caused by multiple parachutes being triggered simultaneously.", + ) + ) + self.display_warning(*warning_msg) + adjust = 1e-7 if flight_phase.t in {previous_phase.t, next_phase.t} else 0 + new_index = ( + index - 1 + if flight_phase.t < previous_phase.t + else index + 1 + if flight_phase.t > next_phase.t + else index + ) + flight_phase.t += adjust + self.add(flight_phase, new_index) + + def add_phase(self, t, derivatives=None, callback=None, clear=True, index=None): + """Add a new flight phase to the list, with the specified + characteristics. This method creates a new FlightPhase instance and + adds it to the flight phases list, either at the specified index + position or appended to the end. See FlightPhases.add() for more + information. + + Parameters + ---------- + t : float + The initial time of the new flight phase. + derivatives : function, optional + A function representing the derivatives of the flight phase. + Default is None. + callback : list of functions, optional + A list of callback functions to be executed during the flight + phase. Default is None. You can also pass an empty list. + clear : bool, optional + A flag indicating whether to clear the solution after the phase. + Default is True. + index : int, optional + The index at which the new flight phase should be inserted. + If not provided, the flight phase will be appended + to the end of the list. Default is None. + + Returns + ------- + None + """ self.add(self.FlightPhase(t, derivatives, callback, clear), index) def flush_after(self, index): + """This function deletes all flight phases after a given index. + + Parameters + ---------- + index : int + The index of the last flight phase to be kept. + + Returns + ------- + None + """ del self.list[index + 1 :] class FlightPhase: - def __init__(self, t, derivative=None, callbacks=[], clear=True): + """Class to store a flight phase. It stores the initial time, the + derivative function, the callback functions and a flag to clear + the solution after the phase. + + Attributes + ---------- + t : float + The initial time of the flight phase. + derivative : function + A function representing the derivatives of the flight phase. + callbacks : list of functions + A list of callback functions to be executed during the flight + phase. + clear : bool + A flag indicating whether to clear the solution after the phase. + """ + + def __init__(self, t, derivative=None, callbacks=None, clear=True): self.t = t self.derivative = derivative - self.callbacks = callbacks[:] + self.callbacks = callbacks[:] if callbacks is not None else [] self.clear = clear def __repr__(self):