diff --git a/rocketpy/motors/hybrid_motor.py b/rocketpy/motors/hybrid_motor.py index a4468bc26..1ef07859a 100644 --- a/rocketpy/motors/hybrid_motor.py +++ b/rocketpy/motors/hybrid_motor.py @@ -42,15 +42,17 @@ class HybridMotor(Motor): HybridMotor.liquid : LiquidMotor Liquid motor object that composes the hybrid motor. HybridMotor.dry_mass : float - The total mass of the motor structure, including chambers - and tanks, when it is empty and does not contain any propellant. + Same as in Motor class. See the :class:`Motor ` docs. HybridMotor.propellant_initial_mass : float - Total propellant initial mass in kg. + Total propellant initial mass in kg. This is the sum of the initial + mass of fluids in each tank and the initial mass of the solid grains. HybridMotor.total_mass : Function Total motor mass in kg as a function of time, defined as the sum - of propellant and dry mass. + of the dry mass (motor's structure mass) and the propellant mass, which + varies with time. HybridMotor.propellant_mass : Function - Total propellant mass in kg as a function of time. + Total propellant mass in kg as a function of time, this includes the + mass of fluids in each tank and the mass of the solid grains. HybridMotor.total_mass_flow_rate : Function Time derivative of propellant total mass in kg/s as a function of time as obtained by the thrust source. @@ -199,8 +201,7 @@ def __init__( .. seealso:: :doc:`Thrust Source Details ` dry_mass : int, float - The total mass of the motor structure, including chambers - and tanks, when it is empty and does not contain any propellant. + Same as in Motor class. See the :class:`Motor ` docs dry_inertia : tuple, list Tuple or list containing the motor's dry mass inertia tensor components, in kg*m^2. This inertia is defined with respect to the @@ -345,18 +346,19 @@ def exhaust_velocity(self): @funcify_method("Time (s)", "Mass (kg)") def propellant_mass(self): """Evaluates the total propellant mass of the motor as the sum - of each tank mass and the grains mass. + of fluids mass in each tank and the grains mass. Returns ------- Function - Total propellant mass of the motor, in kg. + Total propellant mass of the motor as a function of time, in kg. """ return self.solid.propellant_mass + self.liquid.propellant_mass @cached_property def propellant_initial_mass(self): - """Returns the initial propellant mass of the motor. + """Returns the initial propellant mass of the motor. See the docs of the + HybridMotor.propellant_mass property for more information. Returns ------- @@ -367,8 +369,8 @@ def propellant_initial_mass(self): @funcify_method("Time (s)", "mass flow rate (kg/s)", extrapolation="zero") def mass_flow_rate(self): - """Evaluates the mass flow rate of the motor as the sum of each tank - mass flow rate and the grains mass flow rate. + """Evaluates the mass flow rate of the motor as the sum of mass flow + rates from all tanks and the solid grains mass flow rate. Returns ------- @@ -486,14 +488,59 @@ def propellant_I_33(self): @funcify_method("Time (s)", "Inertia I_12 (kg m²)") def propellant_I_12(self): + """Inertia tensor 12 component of the propellant, the inertia is + relative to the e_1 and e_2 axes, centered at the instantaneous + propellant center of mass. + + Returns + ------- + Function + Propellant inertia tensor 12 component at time t. + + Notes + ----- + This is assumed to be zero due to axial symmetry of the motor. This + could be improved in the future to account for the fact that the + motor is not perfectly symmetric. + """ return 0 @funcify_method("Time (s)", "Inertia I_13 (kg m²)") def propellant_I_13(self): + """Inertia tensor 13 component of the propellant, the inertia is + relative to the e_1 and e_3 axes, centered at the instantaneous + propellant center of mass. + + Returns + ------- + Function + Propellant inertia tensor 13 component at time t. + + Notes + ----- + This is assumed to be zero due to axial symmetry of the motor. This + could be improved in the future to account for the fact that the + motor is not perfectly symmetric. + """ return 0 @funcify_method("Time (s)", "Inertia I_23 (kg m²)") def propellant_I_23(self): + """Inertia tensor 23 component of the propellant, the inertia is + relative to the e_2 and e_3 axes, centered at the instantaneous + propellant center of mass. + + Returns + ------- + Function + Propellant inertia tensor 23 component at time t. + + Notes + ----- + This is assumed to be zero due to axial symmetry of the motor. This + could be improved in the future to account for the fact that the + motor is not perfectly symmetric. + """ return 0 def add_tank(self, tank, position): diff --git a/rocketpy/motors/liquid_motor.py b/rocketpy/motors/liquid_motor.py index d89157e72..8fc3c4fce 100644 --- a/rocketpy/motors/liquid_motor.py +++ b/rocketpy/motors/liquid_motor.py @@ -37,14 +37,12 @@ class LiquidMotor(Motor): List containing the motor's added tanks and their respective positions. LiquidMotor.dry_mass : float - The total mass of the motor structure, including chambers - and tanks, when it is empty and does not contain any propellant. + Same as in Motor class. See the :class:`Motor ` docs. LiquidMotor.propellant_initial_mass : float - Total propellant initial mass in kg, includes - fuel and oxidizer. + Total propellant initial mass in kg, includes fuel and oxidizer. LiquidMotor.total_mass : Function Total motor mass in kg as a function of time, defined as the sum - of propellant and dry mass. + of propellant mass and the motor's dry mass (i.e. structure mass). LiquidMotor.propellant_mass : Function Total propellant mass in kg as a function of time, includes fuel and oxidizer. @@ -175,8 +173,7 @@ def __init__( .. seealso:: :doc:`Thrust Source Details ` dry_mass : int, float - The total mass of the motor structure, including chambers - and tanks, when it is empty and does not contain any propellant. + Same as in Motor class. See the :class:`Motor ` docs. dry_inertia : tuple, list Tuple or list containing the motor's dry mass inertia tensor components, in kg*m^2. This inertia is defined with respect to the @@ -260,12 +257,20 @@ def exhaust_velocity(self): ------- self.exhaust_velocity : Function Gas exhaust velocity of the motor. + + Notes + ----- + The exhaust velocity is computed as the ratio of the thrust and the + mass flow rate. Therefore, this will vary with time if the mass flow + rate varies with time. """ return self.thrust / (-1 * self.mass_flow_rate) @funcify_method("Time (s)", "Propellant Mass (kg)") def propellant_mass(self): - """Evaluates the mass of the motor as the sum of each tank mass. + """Evaluates the total propellant mass of the motor as the sum of fluids + mass in each tank, which may include fuel and oxidizer and usually vary + with time. Returns ------- @@ -281,7 +286,8 @@ def propellant_mass(self): @cached_property def propellant_initial_mass(self): - """Property to store the initial mass of the propellant. + """Property to store the initial mass of the propellant, this includes + fuel and oxidizer. Returns ------- @@ -292,8 +298,9 @@ def propellant_initial_mass(self): @funcify_method("Time (s)", "Mass flow rate (kg/s)", extrapolation="zero") def mass_flow_rate(self): - """Evaluates the mass flow rate of the motor as the sum of each tank - mass flow rate. + """Evaluates the mass flow rate of the motor as the sum of mass flow + rate from each tank, which may include fuel and oxidizer and usually + vary with time. Returns ------- @@ -317,12 +324,12 @@ def mass_flow_rate(self): def center_of_propellant_mass(self): """Evaluates the center of mass of the motor from each tank center of mass and positioning. The center of mass height is measured relative to - the motor nozzle. + the origin of the motor's coordinate system. Returns ------- Function - Center of mass of the motor, in meters. + Position of the propellant center of mass, in meters. """ total_mass = 0 mass_balance = 0 diff --git a/rocketpy/motors/motor.py b/rocketpy/motors/motor.py index 2fda0c267..1a8d75f1c 100644 --- a/rocketpy/motors/motor.py +++ b/rocketpy/motors/motor.py @@ -36,15 +36,23 @@ class Motor(ABC): :doc:`Positions and Coordinate Systems ` for more information. Motor.dry_mass : float - The total mass of the motor structure, including chambers - and tanks, when it is empty and does not contain any propellant. + The mass of the motor when devoid of any propellants, measured in + kilograms (kg). It encompasses the structural weight of the motor, + including the combustion chamber, nozzles, tanks, and fasteners. + Excluded from this measure are the propellants and any other elements + that are dynamically accounted for in the `mass` parameter of the rocket + class. Ensure that mass contributions from components shared with the + rocket structure are not recounted here. This parameter does not vary + with time. Motor.propellant_initial_mass : float - Total propellant initial mass in kg. + Total propellant initial mass in kg, including solid, liquid and gas + phases. Motor.total_mass : Function Total motor mass in kg as a function of time, defined as the sum - of propellant and dry mass. + of propellant mass and the motor's dry mass (i.e. structure mass). Motor.propellant_mass : Function - Total propellant mass in kg as a function of time. + Total propellant mass in kg as a function of time, including solid, + liquid and gas phases. Motor.total_mass_flow_rate : Function Time derivative of propellant total mass in kg/s as a function of time as obtained by the thrust source. @@ -174,8 +182,7 @@ def __init__( .. seealso:: :doc:`Thrust Source Details ` dry_mass : int, float - The total mass of the motor structure, including chambers - and tanks, when it is empty and does not contain any propellant. + Same as in Motor class. See the :class:`Motor ` docs center_of_dry_mass_position : int, float The position, in meters, of the motor's center of mass with respect to the motor's coordinate system when it is devoid of propellant. @@ -378,7 +385,7 @@ def exhaust_velocity(self): """ pass - @funcify_method("Time (s)", "total mass (kg)") + @funcify_method("Time (s)", "Total mass (kg)") def total_mass(self): """Total mass of the motor as a function of time. It is defined as the propellant mass plus the dry mass. @@ -386,11 +393,11 @@ def total_mass(self): Returns ------- Function - Total mass as a function of time. + Motor total mass as a function of time. """ return self.propellant_mass + self.dry_mass - @funcify_method("Time (s)", "propellant mass (kg)") + @funcify_method("Time (s)", "Propellant mass (kg)") def propellant_mass(self): """Total propellant mass as a Function of time. @@ -403,11 +410,10 @@ def propellant_mass(self): self.total_mass_flow_rate.integral_function() + self.propellant_initial_mass ) - @funcify_method("Time (s)", "mass dot (kg/s)", extrapolation="zero") + @funcify_method("Time (s)", "Mass flow rate (kg/s)", extrapolation="zero") def total_mass_flow_rate(self): - """Time derivative of propellant mass. Assumes constant exhaust - velocity. The formula used is the opposite of thrust divided by - exhaust velocity. + """Time derivative of the propellant mass as a function of time. The + formula used is the opposite of thrust divided by exhaust velocity. Returns ------- @@ -427,10 +433,8 @@ def total_mass_flow_rate(self): Notes ----- This function computes the total mass flow rate of the motor by - dividing the thrust data by a constant approximation of the exhaust - velocity. - This approximation of the total mass flow rate is used in the - following manner by the child Motor classes: + dividing the thrust data by the exhaust velocity. This is an + approximation, and it is used by the child Motor classes as follows: - The ``SolidMotor`` class uses this approximation to compute the grain's mass flow rate; @@ -449,7 +453,7 @@ def total_mass_flow_rate(self): @property @abstractmethod def propellant_initial_mass(self): - """Propellant initial mass in kg. + """Propellant initial mass in kg, including solid, liquid and gas phases Returns ------- @@ -478,8 +482,8 @@ def center_of_mass(self): @abstractmethod def center_of_propellant_mass(self): """Position of the propellant center of mass as a function of time. - The position is specified as a scalar, relative to the motor's - coordinate system. + The position is specified as a scalar, relative to the origin of the + motor's coordinate system. Returns ------- @@ -501,7 +505,7 @@ def I_11(self): Notes ----- The e_1 direction is assumed to be the direction perpendicular to the - motor body axis. + motor body axis. Also, due to symmetry, I_11 = I_22. References ---------- @@ -540,7 +544,8 @@ def I_22(self): Notes ----- The e_2 direction is assumed to be the direction perpendicular to the - motor body axis, and perpendicular to e_1. + motor body axis, and perpendicular to e_1. Also, due to symmetry, + I_22 = I_11. References ---------- @@ -667,6 +672,7 @@ def I_23(self): ---------- https://en.wikipedia.org/wiki/Moment_of_inertia """ + # wrt = with respect to # Propellant inertia tensor 23 component wrt propellant center of mass propellant_I_23 = self.propellant_I_23 @@ -1103,8 +1109,7 @@ def __init__( coordinate system. See :doc:`Positions and Coordinate Systems ` dry_mass : int, float - The total mass of the motor structure, including chambers - and tanks, when it is empty and does not contain any propellant. + Same as in Motor class. See the :class:`Motor ` docs propellant_initial_mass : int, float The initial mass of the propellant in the motor. center_of_dry_mass_position : int, float, optional diff --git a/rocketpy/motors/solid_motor.py b/rocketpy/motors/solid_motor.py index c92f95007..f809777b3 100644 --- a/rocketpy/motors/solid_motor.py +++ b/rocketpy/motors/solid_motor.py @@ -65,8 +65,7 @@ class SolidMotor(Motor): SolidMotor.grain_initial_mass : float Initial mass of each grain in kg. SolidMotor.dry_mass : float - The total mass of the motor structure, including chambers - and tanks, when it is empty and does not contain any propellant. + Same as in Motor class. See the :class:`Motor ` docs. SolidMotor.propellant_initial_mass : float Total propellant initial mass in kg. SolidMotor.total_mass : Function @@ -78,8 +77,8 @@ class SolidMotor(Motor): Time derivative of propellant total mass in kg/s as a function of time as obtained by the thrust source. SolidMotor.center_of_mass : Function - Position of the motor center of mass in - meters as a function of time. + Position of the motor center of mass in meters as a function of time, + with respect to the motor's coordinate system. See :doc:`Positions and Coordinate Systems ` for more information regarding the motor's coordinate system. @@ -226,8 +225,7 @@ def __init__( nozzle_radius : int, float Motor's nozzle outlet radius in meters. dry_mass : int, float - The total mass of the motor structure, including chambers - and tanks, when it is empty and does not contain any propellant. + Same as in Motor class. See the :class:`Motor ` docs dry_inertia : tuple, list Tuple or list containing the motor's dry mass inertia tensor components, in kg*m^2. This inertia is defined with respect to the @@ -421,7 +419,8 @@ def mass_flow_rate(self): @mass_flow_rate.setter def mass_flow_rate(self, value): - """Sets the mass flow rate of the motor. + """Sets the mass flow rate of the motor. This includes all the grains + burning all at once. Parameters ---------- @@ -432,10 +431,10 @@ def mass_flow_rate(self, value): ------- None """ - self._mass_flow_rate = value.reset("Time (s)", "grain mass flow rate (kg/s)") + self._mass_flow_rate = value.reset("Time (s)", "Grain mass flow rate (kg/s)") self.evaluate_geometry() - @funcify_method("Time (s)", "center of mass (m)", "linear") + @funcify_method("Time (s)", "Center of Propellant Mass (m)", "linear") def center_of_propellant_mass(self): """Position of the propellant center of mass as a function of time. The position is specified as a scalar, relative to the motor's diff --git a/rocketpy/motors/tank.py b/rocketpy/motors/tank.py index 0b7069141..022afed0e 100644 --- a/rocketpy/motors/tank.py +++ b/rocketpy/motors/tank.py @@ -36,7 +36,7 @@ class Tank(ABC): of time. Tank.net_mass_flow_rate : Function Net mass flow rate of the tank in kg/s as a function of time, also - understood as time derivative of the tank mass. + understood as time derivative of the fluids mass. Tank.liquid_volume : Function Volume of the liquid inside the Tank in m^3 as a function of time. Tank.gas_volume : Function diff --git a/rocketpy/prints/rocket_prints.py b/rocketpy/prints/rocket_prints.py index a3150ac02..603e66a9a 100644 --- a/rocketpy/prints/rocket_prints.py +++ b/rocketpy/prints/rocket_prints.py @@ -22,8 +22,6 @@ def __init__(self, rocket): """ self.rocket = rocket - pass - def inertia_details(self): """Print inertia details. @@ -32,44 +30,30 @@ def inertia_details(self): None """ print("\nInertia Details\n") - print("Rocket Mass: {:.3f} kg".format(self.rocket.mass)) - print("Rocket Dry Mass: {:.3f} kg (With Motor)".format(self.rocket.dry_mass)) + print(f"Rocket Mass: {self.rocket.mass:.3f} kg (without motor)") + print(f"Rocket Dry Mass: {self.rocket.dry_mass:.3f} kg (with unloaded motor)") print( - "Rocket Mass: {:.3f} kg (With Propellant)".format(self.rocket.total_mass(0)) + f"Rocket Loaded Mass: {self.rocket.total_mass(0):.3f} kg (with loaded motor)" ) print( - "Rocket Inertia (with motor, but without propellant) 11: {:.3f} kg*m2".format( - self.rocket.dry_I_11 - ) + f"Rocket Inertia (with unloaded motor) 11: {self.rocket.dry_I_11:.3f} kg*m2" ) print( - "Rocket Inertia (with motor, but without propellant) 22: {:.3f} kg*m2".format( - self.rocket.dry_I_22 - ) + f"Rocket Inertia (with unloaded motor) 22: {self.rocket.dry_I_22:.3f} kg*m2" ) print( - "Rocket Inertia (with motor, but without propellant) 33: {:.3f} kg*m2".format( - self.rocket.dry_I_33 - ) + f"Rocket Inertia (with unloaded motor) 33: {self.rocket.dry_I_33:.3f} kg*m2" ) print( - "Rocket Inertia (with motor, but without propellant) 12: {:.3f} kg*m2".format( - self.rocket.dry_I_12 - ) + f"Rocket Inertia (with unloaded motor) 12: {self.rocket.dry_I_12:.3f} kg*m2" ) print( - "Rocket Inertia (with motor, but without propellant) 13: {:.3f} kg*m2".format( - self.rocket.dry_I_13 - ) + f"Rocket Inertia (with unloaded motor) 13: {self.rocket.dry_I_13:.3f} kg*m2" ) print( - "Rocket Inertia (with motor, but without propellant) 23: {:.3f} kg*m2".format( - self.rocket.dry_I_23 - ) + f"Rocket Inertia (with unloaded motor) 23: {self.rocket.dry_I_23:.3f} kg*m2" ) - return None - def rocket_geometrical_parameters(self): """Print rocket geometrical parameters. diff --git a/rocketpy/rocket/rocket.py b/rocketpy/rocket/rocket.py index f491d27c1..029f90c24 100644 --- a/rocketpy/rocket/rocket.py +++ b/rocketpy/rocket/rocket.py @@ -19,7 +19,6 @@ class Rocket: - """Keeps rocket information. Attributes @@ -35,6 +34,19 @@ class Rocket: See :doc:`Positions and Coordinate Systems ` for more information regarding the rocket's coordinate system. + Rocket.center_of_mass_without_motor : int, float + Position, in m, of the rocket's center of mass without motor + relative to the rocket's coordinate system. This does not include + the motor or propellant mass. + Rocket.motor_center_of_mass_position : Function + Position, in meters, of the motor's center of mass relative to the user + defined rocket coordinate system. This is a function of time since the + propellant mass decreases with time. For more information, see the + :doc:`Positions and Coordinate Systems `. + Rocket.motor_center_of_dry_mass_position : float + Position, in meters, of the motor's center of dry mass (i.e. center of + mass without propellant) relative to the user defined rocket coordinate + system. This is constant since the motor dry mass is constant. Rocket.coordinate_system_orientation : string String defining the orientation of the rocket's coordinate system. The coordinate system is defined by the rocket's axis of symmetry. @@ -46,7 +58,7 @@ class Rocket: coordinate system is defined with the rocket's axis of symmetry pointing from the rocket's nose cone to the rocket's tail. Rocket.mass : float - Rocket's mass without propellant in kg. + Rocket's mass without motor and propellant, measured in kg. Rocket.center_of_mass : Function Position of the rocket's center of mass, including propellant, relative to the user defined rocket reference system. @@ -116,6 +128,36 @@ class Rocket: :doc:`Positions and Coordinate Systems ` for more information regarding the rocket's coordinate system. Expressed in meters as a function of time. + Rocket.I_11_without_motor : float + Rocket's inertia tensor 11 component without any motors, in kg*m^2. This + is the same value that is passed in the Rocket.__init__() method. + Rocket.I_22_without_motor : float + Rocket's inertia tensor 22 component without any motors, in kg*m^2. This + is the same value that is passed in the Rocket.__init__() method. + Rocket.I_33_without_motor : float + Rocket's inertia tensor 33 component without any motors, in kg*m^2. This + is the same value that is passed in the Rocket.__init__() method. + Rocket.I_12_without_motor : float + Rocket's inertia tensor 12 component without any motors, in kg*m^2. This + is the same value that is passed in the Rocket.__init__() method. + Rocket.I_13_without_motor : float + Rocket's inertia tensor 13 component without any motors, in kg*m^2. This + is the same value that is passed in the Rocket.__init__() method. + Rocket.I_23_without_motor : float + Rocket's inertia tensor 23 component without any motors, in kg*m^2. This + is the same value that is passed in the Rocket.__init__() method. + Rocket.dry_I_11 : float + Rocket's inertia tensor 11 component with unloaded motor,in kg*m^2. + Rocket.dry_I_22 : float + Rocket's inertia tensor 22 component with unloaded motor,in kg*m^2. + Rocket.dry_I_33 : float + Rocket's inertia tensor 33 component with unloaded motor,in kg*m^2. + Rocket.dry_I_12 : float + Rocket's inertia tensor 12 component with unloaded motor,in kg*m^2. + Rocket.dry_I_13 : float + Rocket's inertia tensor 13 component with unloaded motor,in kg*m^2. + Rocket.dry_I_23 : float + Rocket's inertia tensor 23 component with unloaded motor,in kg*m^2. """ def __init__( @@ -138,15 +180,17 @@ def __init__( mass : int, float Rocket total mass without motor in kg. inertia : tuple, list - Tuple or list containing the rocket's dry mass inertia tensor - components, in kg*m^2. + Tuple or list containing the rocket's inertia tensor components, + in kg*m^2. This should be measured without motor and propellant so + that the inertia reference point is the + `center_of_mass_without_motor`. Assuming e_3 is the rocket's axis of symmetry, e_1 and e_2 are - orthogonal and form a plane perpendicular to e_3, the dry mass - inertia tensor components must be given in the following order: - (I_11, I_22, I_33, I_12, I_13, I_23), where I_ij is the - component of the inertia tensor in the direction of e_i x e_j. - Alternatively, the inertia tensor can be given as - (I_11, I_22, I_33), where I_12 = I_13 = I_23 = 0. + orthogonal and form a plane perpendicular to e_3, the inertia tensor + components must be given in the following order: (I_11, I_22, I_33, + I_12, I_13, I_23), where I_ij is the component of the inertia tensor + in the direction of e_i x e_j. Alternatively, the inertia tensor can + be given as (I_11, I_22, I_33), where I_12 = I_13 = I_23 = 0. This + can also be called as "rocket dry inertia tensor". power_off_drag : int, float, callable, string, array Rocket's drag coefficient when the motor is off. Can be given as an entry to the Function class. See help(Function) for more @@ -217,13 +261,9 @@ def __init__( self.thrust_eccentricity_y = 0 self.thrust_eccentricity_x = 0 - # Parachute data initialization + # Parachute, Aerodynamic and Rail buttons data initialization self.parachutes = [] - - # Aerodynamic data initialization self.aerodynamic_surfaces = Components() - - # Rail buttons data initialization self.rail_buttons = Components() self.cp_position = 0 @@ -249,7 +289,7 @@ def __init__( self.cp_position = 0 # Set by self.evaluate_static_margin() # Create a, possibly, temporary empty motor - # self.motors = Components() # currently unused since only one motor is supported + # self.motors = Components() # currently unused, only 1 motor is supported self.add_motor(motor=EmptyMotor(), position=0) # Important dynamic inertial quantities @@ -273,18 +313,19 @@ def __init__( self.prints = _RocketPrints(self) self.plots = _RocketPlots(self) - return None - @property def nosecones(self): + """A list containing all the nose cones currently added to the rocket.""" return self.aerodynamic_surfaces.get_by_type(NoseCone) @property def fins(self): + """A list containing all the fins currently added to the rocket.""" return self.aerodynamic_surfaces.get_by_type(Fins) @property def tails(self): + """A list with all the tails currently added to the rocket""" return self.aerodynamic_surfaces.get_by_type(Tail) def evaluate_total_mass(self): @@ -305,11 +346,9 @@ def evaluate_total_mass(self): print("Please associate this rocket with a motor!") return False - # Calculate total mass by summing up propellant and dry mass self.total_mass = self.mass + self.motor.total_mass - self.total_mass.set_outputs("Total Mass (Rocket + Propellant) (kg)") - - # Return total mass + self.total_mass.set_outputs("Total Mass (Rocket + Motor + Propellant) (kg)") + self.total_mass.set_title("Total Mass (Rocket + Motor + Propellant)") return self.total_mass def evaluate_dry_mass(self): @@ -330,10 +369,9 @@ def evaluate_dry_mass(self): print("Please associate this rocket with a motor!") return False - # Calculate total dry mass: motor (without propellant) + rocket + # Calculate total dry mass: motor (without propellant) + rocket mass self.dry_mass = self.mass + self.motor.dry_mass - # Return total mass return self.dry_mass def evaluate_center_of_mass(self): @@ -348,74 +386,64 @@ def evaluate_center_of_mass(self): See :doc:`Positions and Coordinate Systems ` for more information. """ - # Compute center of mass position self.center_of_mass = ( self.center_of_mass_without_motor * self.mass + self.motor_center_of_mass_position * self.motor.total_mass ) / self.total_mass self.center_of_mass.set_inputs("Time (s)") self.center_of_mass.set_outputs("Center of Mass Position (m)") - + self.center_of_mass.set_title( + "Center of Mass Position (Rocket + Motor + Propellant)" + ) return self.center_of_mass def evaluate_center_of_dry_mass(self): - """Evaluates rocket center dry of mass (i.e. without propellant) - position relative to user defined rocket reference system. + """Evaluates the rocket's center of dry mass (i.e. rocket with motor but + without propellant) position relative to user defined rocket reference + system. Returns ------- self.center_of_dry_mass_position : int, float - Rocket's center of dry mass position relative to user defined rocket - reference system. See - :doc:`Positions and Coordinate Systems ` for - more information. + Rocket's center of dry mass position (with unloaded motor) """ - # Compute center of mass position self.center_of_dry_mass_position = ( self.center_of_mass_without_motor * self.mass + self.motor_center_of_dry_mass_position * self.motor.dry_mass ) / self.dry_mass - return self.center_of_dry_mass_position def evaluate_reduced_mass(self): - """Calculates and returns the rocket's total reduced mass. The - reduced mass is defined as the product of the propellant mass - and the mass of the rocket without propellant, divided by the - sum of the propellant mass and the rocket mass. The function - returns an object of the Function class and is defined as a + """Calculates and returns the rocket's total reduced mass. The reduced + mass is defined as the product of the propellant mass and the rocket dry + mass (i.e. with unloaded motor), divided by the loaded rocket mass. + The function returns an object of the Function class and is defined as a function of time. Returns ------- self.reduced_mass : Function - Function of time expressing the reduced mass of the rocket, - defined as the product of the propellant mass and the mass - of the rocket without propellant, divided by the sum of the - propellant mass and the rocket mass. + Function of time expressing the reduced mass of the rocket. """ + # TODO: add tests for reduced_mass values # Make sure there is a motor associated with the rocket if self.motor is None: print("Please associate this rocket with a motor!") return False - # Retrieve propellant mass as a function of time - motor_mass = self.motor.propellant_mass - - # retrieve constant rocket mass without propellant - mass = self.dry_mass - - # calculate reduced mass - self.reduced_mass = motor_mass * mass / (motor_mass + mass) + # Get nicknames + prop_mass = self.motor.propellant_mass + dry_mass = self.dry_mass + # calculate reduced mass and return it + self.reduced_mass = prop_mass * dry_mass / (prop_mass + dry_mass) self.reduced_mass.set_outputs("Reduced Mass (kg)") - - # Return reduced mass + self.reduced_mass.set_title("Reduced Mass") return self.reduced_mass def evaluate_thrust_to_weight(self): - """Evaluates thrust to weight as a Function of time. - - Uses g = 9.80665 m/s² as nominal gravity for weight calculation. + """Evaluates thrust to weight as a Function of time. This is defined as + the motor thrust force divided by rocket weight. The gravitational + acceleration is assumed constant and equals to 9.80665 m/s^2. Returns ------- @@ -424,6 +452,7 @@ def evaluate_thrust_to_weight(self): self.thrust_to_weight = self.motor.thrust / (9.80665 * self.total_mass) self.thrust_to_weight.set_inputs("Time (s)") self.thrust_to_weight.set_outputs("Thrust/Weight") + self.thrust_to_weight.set_title("Thrust to Weight ratio") def evaluate_static_margin(self): """Calculates and returns the rocket's static margin when @@ -455,21 +484,20 @@ def evaluate_static_margin(self): self.static_margin = (self.center_of_mass - self.cp_position) / ( 2 * self.radius ) - self.static_margin *= ( - self._csys - ) # Change sign if coordinate system is upside down + # Change sign if coordinate system is upside down + self.static_margin *= self._csys self.static_margin.set_inputs("Time (s)") self.static_margin.set_outputs("Static Margin (c)") self.static_margin.set_title("Static Margin") self.static_margin.set_discrete( lower=0, upper=self.motor.burn_out_time, samples=200 ) - return None def evaluate_dry_inertias(self): """Calculates and returns the rocket's dry inertias relative to the rocket's center of mass. The inertias are saved and returned - in units of kg*m². + in units of kg*m². This does not consider propellant mass but does take + into account the motor dry mass. Returns ------- @@ -537,7 +565,6 @@ def evaluate_dry_inertias(self): self.dry_I_13 = self.I_13_without_motor + self.motor.dry_I_13 self.dry_I_23 = self.I_23_without_motor + self.motor.dry_I_23 - # Return inertias return ( self.dry_I_11, self.dry_I_22, @@ -582,7 +609,7 @@ def evaluate_inertias(self): """ # Get masses prop_mass = self.motor.propellant_mass # Propellant mass as a function of time - dry_mass = self.dry_mass # Constant rocket dry mass without propellant + dry_mass = self.dry_mass # Constant rocket mass with motor, without propellant # Compute axes distances CM_to_CDM = self.center_of_mass - self.center_of_dry_mass_position @@ -665,7 +692,6 @@ def add_motor(self, motor, position): self.evaluate_reduced_mass() self.evaluate_thrust_to_weight() self.evaluate_static_margin() - return None def add_surfaces(self, surfaces, positions): """Adds one or more aerodynamic surfaces to the rocket. The aerodynamic @@ -703,7 +729,6 @@ def add_surfaces(self, surfaces, positions): self.aerodynamic_surfaces.add(surfaces, positions) self.evaluate_static_margin() - return None def add_tail( self, top_radius, bottom_radius, length, position, radius=None, name="Tail" @@ -739,17 +764,11 @@ def add_tail( tail : Tail Tail object created. """ - # Modify reference radius if not provided radius = self.radius if radius is None else radius - - # Create new tail as an object of the Tail class + # Create tail, adds it to the rocket and returns it tail = Tail(top_radius, bottom_radius, length, radius, name) - - # Add tail to aerodynamic surfaces self.add_surfaces(tail, position) - - # Return self return tail def add_nose(self, length, kind, position, bluffness=0, name="Nose Cone"): @@ -785,7 +804,6 @@ def add_nose(self, length, kind, position, bluffness=0, name="Nose Cone"): nose : Nose Nose cone object created. """ - # Create a nose as an object of NoseCone class nose = NoseCone( length=length, kind=kind, @@ -794,11 +812,7 @@ def add_nose(self, length, kind, position, bluffness=0, name="Nose Cone"): bluffness=bluffness, name=name, ) - - # Add nose to the list of aerodynamic surfaces self.add_surfaces(nose, position) - - # Return self return nose def add_fins(self, *args, **kwargs): @@ -911,8 +925,6 @@ def add_trapezoidal_fins( # Add fin set to the list of aerodynamic surfaces self.add_surfaces(fin_set, position) - - # Return the created aerodynamic surface return fin_set def add_elliptical_fins( @@ -974,17 +986,9 @@ def add_elliptical_fins( fin_set : EllipticalFins Fin set object created. """ - - # Modify radius if not given, use rocket radius, otherwise use given. radius = radius if radius is not None else self.radius - - # Create a fin set as an object of EllipticalFins class fin_set = EllipticalFins(n, root_chord, span, radius, cant_angle, airfoil, name) - - # Add fin set to the list of aerodynamic surfaces self.add_surfaces(fin_set, position) - - # Return self return fin_set def add_parachute( @@ -1051,13 +1055,8 @@ def add_parachute( noise_signal and noisyPressureSignal which are filled in during Flight simulation. """ - # Create a parachute parachute = Parachute(name, cd_s, trigger, sampling_rate, lag, noise) - - # Add parachute to list of parachutes self.parachutes.append(parachute) - - # Return self return self.parachutes[-1] def set_rail_buttons( @@ -1096,7 +1095,6 @@ def set_rail_buttons( rail_buttons : RailButtons RailButtons object created """ - # Create a rail buttons object buttons_distance = abs(upper_button_position - lower_button_position) rail_buttons = RailButtons( buttons_distance=buttons_distance, angular_position=angular_position @@ -1108,8 +1106,7 @@ def add_cm_eccentricity(self, x, y): """Moves line of action of aerodynamic and thrust forces by equal translation amount to simulate an eccentricity in the position of the center of mass of the rocket relative to its - geometrical center line. Should not be used together with - add_cp_eccentricity and add_thrust_eccentricity. + geometrical center line. Parameters ---------- @@ -1124,16 +1121,16 @@ def add_cm_eccentricity(self, x, y): ------- self : Rocket Object of the Rocket class. + + Notes + ----- + Should not be used together with add_cp_eccentricity and + add_thrust_eccentricity. """ - # Move center of pressure to -x and -y self.cp_eccentricity_x = -x self.cp_eccentricity_y = -y - - # Move thrust center by -x and -y self.thrust_eccentricity_y = -x self.thrust_eccentricity_x = -y - - # Return self return self def add_cp_eccentricity(self, x, y): @@ -1155,11 +1152,8 @@ def add_cp_eccentricity(self, x, y): self : Rocket Object of the Rocket class. """ - # Move center of pressure by x and y self.cp_eccentricity_x = x self.cp_eccentricity_y = y - - # Return self return self def add_thrust_eccentricity(self, x, y): @@ -1182,11 +1176,8 @@ def add_thrust_eccentricity(self, x, y): self : Rocket Object of the Rocket class. """ - # Move thrust line by x and y self.thrust_eccentricity_y = x self.thrust_eccentricity_x = y - - # Return self return self def info(self): @@ -1197,11 +1188,8 @@ def info(self): ------- None """ - # All prints self.prints.all() - return None - def all_info(self): """Prints out all data and graphs available about the Rocket. @@ -1209,9 +1197,5 @@ def all_info(self): ------- None """ - - # All prints and plots self.info() self.plots.all() - - return None diff --git a/rocketpy/utilities.py b/rocketpy/utilities.py index 78992ffb6..9f7cf3265 100644 --- a/rocketpy/utilities.py +++ b/rocketpy/utilities.py @@ -1,8 +1,8 @@ import traceback import warnings -import numpy as np import matplotlib.pyplot as plt +import numpy as np from scipy.integrate import solve_ivp from .environment.environment import Environment @@ -517,22 +517,26 @@ def create_dispersion_dictionary(filename): def apogee_by_mass(flight, min_mass, max_mass, points=10, plot=True): """Returns a Function object that estimates the apogee of a rocket given - its dry mass. The function will use the rocket's mass as the independent - variable and the estimated apogee as the dependent variable. The function - will use the rocket's environment and inclination to estimate the apogee. - This is useful when you want to adjust the rocket's mass to reach a - specific apogee. + its mass (no motor). The function will use the rocket's mass as the + independent variable and the estimated apogee as the dependent variable. + The function will use the rocket's environment and inclination to estimate + the apogee. This is useful when you want to adjust the rocket's mass to + reach a specific apogee. Parameters ---------- flight : rocketpy.Flight Flight object containing the rocket's flight data - min_mass : int - The minimum value of mass to calculate the apogee, by default 3. This - value should be the minimum dry mass of the rocket, therefore, a - positive value is expected. - max_mass : int - The maximum value of mass to calculate the apogee, by default 30. + min_mass : float + The minimum value for the rocket's mass to calculate the apogee, given + in kilograms (kg). This value should be the minimum rocket's mass, + therefore, a positive value is expected. See the Rocket.mass attribute + for more details. + max_mass : float + The maximum value for the rocket's mass to calculate the apogee, given + in kilograms (kg). This value should be the maximum rocket's mass, + therefore, a positive value is expected and it should be higher than the + min_mass attribute. See the Rocket.mass attribute for more details. points : int, optional The number of points to calculate the apogee between the mass boundaries, by default 10. Increasing this value will refine the @@ -544,7 +548,7 @@ def apogee_by_mass(flight, min_mass, max_mass, points=10, plot=True): ------- rocketpy.Function Function object containing the estimated apogee as a function of the - rocket's dry mass. + rocket's mass (without motor nor propellant). """ rocket = flight.rocket @@ -581,8 +585,8 @@ def apogee(mass): def liftoff_speed_by_mass(flight, min_mass, max_mass, points=10, plot=True): """Returns a Function object that estimates the liftoff speed of a rocket - given its dry mass. The function will use the rocket's mass as the - independent variable and the estimated liftoff speed as the dependent + given its mass (without motor). The function will use the rocket's mass as + the independent variable and the estimated liftoff speed as the dependent variable. The function will use the rocket's environment and inclination to estimate the liftoff speed. This is useful when you want to adjust the rocket's mass to reach a specific liftoff speed. @@ -591,16 +595,21 @@ def liftoff_speed_by_mass(flight, min_mass, max_mass, points=10, plot=True): ---------- flight : rocketpy.Flight Flight object containing the rocket's flight data - min_mass : int - The minimum value of mass to calculate the liftoff speed, by default 3. - This value should be the minimum dry mass of the rocket, therefore, a - positive value is expected. - max_mass : int - The maximum value of mass to calculate the liftoff speed, by default 30. + min_mass : float + The minimum value for the rocket's mass to calculate the out of rail + speed, given in kilograms (kg). This value should be the minimum + rocket's mass, therefore, a positive value is expected. See the + Rocket.mass attribute for more details. + max_mass : float + The maximum value for the rocket's mass to calculate the out of rail + speed, given in kilograms (kg). This value should be the maximum + rocket's mass, therefore, a positive value is expected and it should be + higher than the min_mass attribute. See the Rocket.mass attribute for + more details. points : int, optional The number of points to calculate the liftoff speed between the mass - boundaries, by default 10. Increasing this value will refine the results, - but will also increase the computational time. + boundaries, by default 10. Increasing this value will refine the + results, but will also increase the computational time. plot : bool, optional If True, the function will plot the results, by default True. @@ -608,7 +617,7 @@ def liftoff_speed_by_mass(flight, min_mass, max_mass, points=10, plot=True): ------- rocketpy.Function Function object containing the estimated liftoff speed as a function of - the rocket's dry mass. + the rocket's mass (without motor nor propellant). """ rocket = flight.rocket