diff --git a/rocketpy/rocket/rocket.py b/rocketpy/rocket/rocket.py index 609e1b76c..415f54790 100644 --- a/rocketpy/rocket/rocket.py +++ b/rocketpy/rocket/rocket.py @@ -1744,8 +1744,8 @@ def set_rail_buttons( 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. + position of the center of dry mass of the rocket relative to + its geometrical center line. Parameters ---------- @@ -1781,20 +1781,18 @@ def add_cm_eccentricity(self, x, y): def add_cp_eccentricity(self, x, y): """Moves line of action of aerodynamic forces to simulate an eccentricity in the position of the center of pressure relative - to the center of mass of the rocket. + to the center of dry mass of the rocket. Parameters ---------- x : float Distance in meters by which the CP is to be translated in - the x direction relative to the center of mass axial line. - The x axis is defined according to the body axes coordinate - system. + the x direction relative to the center of dry mass axial line. + The x axis is defined according to the body axes coordinate system. y : float Distance in meters by which the CP is to be translated in - the y direction relative to the center of mass axial line. - The y axis is defined according to the body axes coordinate - system. + the y direction relative to the center of dry mass axial line. + The y axis is defined according to the body axes coordinate system. Returns ------- @@ -1811,19 +1809,19 @@ def add_cp_eccentricity(self, x, y): def add_thrust_eccentricity(self, x, y): """Moves line of action of thrust forces to simulate a - misalignment of the thrust vector and the center of mass. + misalignment of the thrust vector and the center of dry mass. Parameters ---------- x : float Distance in meters by which the line of action of the thrust force is to be translated in the x direction - relative to the center of mass axial line. The x axis + relative to the center of dry mass axial line. The x axis is defined according to the body axes coordinate system. y : float Distance in meters by which the line of action of the - thrust force is to be translated in the x direction - relative to the center of mass axial line. The y axis + thrust force is to be translated in the y direction + relative to the center of dry mass axial line. The y axis is defined according to the body axes coordinate system. Returns @@ -1835,8 +1833,8 @@ def add_thrust_eccentricity(self, x, y): -------- :ref:`rocket_axes` """ - self.thrust_eccentricity_y = x - self.thrust_eccentricity_x = y + self.thrust_eccentricity_x = x + self.thrust_eccentricity_y = y return self def draw(self, vis_args=None, plane="xz", *, filename=None): diff --git a/rocketpy/simulation/flight.py b/rocketpy/simulation/flight.py index e2190bc40..9d53ae2d6 100644 --- a/rocketpy/simulation/flight.py +++ b/rocketpy/simulation/flight.py @@ -1469,8 +1469,8 @@ def u_dot(self, t, u, post_processing=False): # pylint: disable=too-many-locals # Thrust thrust = self.rocket.motor.thrust.get_value_opt(t) # Off center moment - M1 += self.rocket.thrust_eccentricity_x * thrust - M2 -= self.rocket.thrust_eccentricity_y * thrust + M1 += self.rocket.thrust_eccentricity_y * thrust + M2 -= self.rocket.thrust_eccentricity_x * thrust else: # Motor stopped # Inertias @@ -1856,11 +1856,11 @@ def u_dot_generalized(self, t, u, post_processing=False): # pylint: disable=too thrust = self.rocket.motor.thrust.get_value_opt(t) M1 += ( self.rocket.cp_eccentricity_y * R3 - + self.rocket.thrust_eccentricity_x * thrust + + self.rocket.thrust_eccentricity_y * thrust ) M2 -= ( self.rocket.cp_eccentricity_x * R3 - - self.rocket.thrust_eccentricity_y * thrust + + self.rocket.thrust_eccentricity_x * thrust ) M3 += self.rocket.cp_eccentricity_x * R2 - self.rocket.cp_eccentricity_y * R1 diff --git a/rocketpy/stochastic/stochastic_rocket.py b/rocketpy/stochastic/stochastic_rocket.py index 224262ff1..56a8a44d5 100644 --- a/rocketpy/stochastic/stochastic_rocket.py +++ b/rocketpy/stochastic/stochastic_rocket.py @@ -389,6 +389,105 @@ def set_rail_buttons( rail_buttons, self._validate_position(rail_buttons, lower_button_position) ) + def add_cp_eccentricity(self, x=None, y=None): + """Moves line of action of aerodynamic forces to simulate an + eccentricity in the position of the center of pressure relative + to the center of dry mass of the rocket. + + Parameters + ---------- + x : tuple, list, int, float, optional + Distance in meters by which the CP is to be translated in + the x direction relative to the center of dry mass axial line. + The x axis is defined according to the body axes coordinate system. + y : tuple, list, int, float, optional + Distance in meters by which the CP is to be translated in + the y direction relative to the center of dry mass axial line. + The y axis is defined according to the body axes coordinate system. + + Returns + ------- + self : StochasticRocket + Object of the StochasticRocket class. + """ + self.cp_eccentricity_x = self._validate_eccentricity("cp_eccentricity_x", x) + self.cp_eccentricity_y = self._validate_eccentricity("cp_eccentricity_y", y) + return self + + def add_thrust_eccentricity(self, x=None, y=None): + """Moves line of action of thrust forces to simulate a + misalignment of the thrust vector and the center of dry mass. + + Parameters + ---------- + x : tuple, list, int, float, optional + Distance in meters by which the line of action of the + thrust force is to be translated in the x direction + relative to the center of dry mass axial line. The x axis + is defined according to the body axes coordinate system. + y : tuple, list, int, float, optional + Distance in meters by which the line of action of the + thrust force is to be translated in the y direction + relative to the center of dry mass axial line. The y axis + is defined according to the body axes coordinate system. + + Returns + ------- + self : StochasticRocket + Object of the StochasticRocket class. + """ + self.thrust_eccentricity_x = self._validate_eccentricity( + "thrust_eccentricity_x", x + ) + self.thrust_eccentricity_y = self._validate_eccentricity( + "thrust_eccentricity_y", y + ) + return self + + def _validate_eccentricity(self, eccentricity, position): + """Validate the eccentricity argument. + + Parameters + ---------- + eccentricity : str + The eccentricity to which the position argument refers to. + position : tuple, list, int, float + The position argument to be validated. + + Returns + ------- + tuple or list + Validated position argument. + + Raises + ------ + ValueError + If the position argument does not conform to the specified formats. + """ + if isinstance(position, tuple): + return self._validate_tuple( + eccentricity, + position, + ) + elif isinstance(position, (int, float)): + return self._validate_scalar( + eccentricity, + position, + ) + elif isinstance(position, list): + return self._validate_list( + eccentricity, + position, + ) + elif position is None: + position = [] + return self._validate_list( + eccentricity, + position, + ) + else: + raise AssertionError("`position` must be a tuple, list, int, or float") + def _validate_position(self, validated_object, position): """Validate the position argument. @@ -578,6 +677,13 @@ def _create_parachute(self, stochastic_parachute): self.last_rnd_dict["parachutes"].append(stochastic_parachute.last_rnd_dict) return parachute + def _create_eccentricities(self, stochastic_x, stochastic_y, eccentricity): + x_rnd = self._randomize_position(stochastic_x) + self.last_rnd_dict[eccentricity + "_x"] = x_rnd + y_rnd = self._randomize_position(stochastic_y) + self.last_rnd_dict[eccentricity + "_y"] = y_rnd + return x_rnd, y_rnd + def create_object(self): """Creates and returns a Rocket object from the randomly generated input arguments. @@ -609,6 +715,23 @@ def create_object(self): rocket.power_off_drag *= generated_dict["power_off_drag_factor"] rocket.power_on_drag *= generated_dict["power_on_drag_factor"] + if hasattr(self, "cp_eccentricity_x") and hasattr(self, "cp_eccentricity_y"): + cp_ecc_x, cp_ecc_y = self._create_eccentricities( + self.cp_eccentricity_x, + self.cp_eccentricity_y, + "cp_eccentricity", + ) + rocket.add_cp_eccentricity(cp_ecc_x, cp_ecc_y) + if hasattr(self, "thrust_eccentricity_x") and hasattr( + self, "thrust_eccentricity_y" + ): + thrust_ecc_x, thrust_ecc_y = self._create_eccentricities( + self.thrust_eccentricity_x, + self.thrust_eccentricity_y, + "thrust_eccentricity", + ) + rocket.add_thrust_eccentricity(thrust_ecc_x, thrust_ecc_y) + for component_motor in self.motors: motor, position_rnd = self._create_motor(component_motor) rocket.add_motor(motor, position_rnd) diff --git a/tests/unit/test_rocket.py b/tests/unit/test_rocket.py index 47ea61eec..3c1e7168d 100644 --- a/tests/unit/test_rocket.py +++ b/tests/unit/test_rocket.py @@ -331,15 +331,15 @@ def test_add_cm_eccentricity_assert_properties_set(calisto): assert calisto.cp_eccentricity_x == -4 assert calisto.cp_eccentricity_y == -5 - assert calisto.thrust_eccentricity_y == -4 - assert calisto.thrust_eccentricity_x == -5 + assert calisto.thrust_eccentricity_x == -4 + assert calisto.thrust_eccentricity_y == -5 def test_add_thrust_eccentricity_assert_properties_set(calisto): calisto.add_thrust_eccentricity(x=4, y=5) - assert calisto.thrust_eccentricity_y == 4 - assert calisto.thrust_eccentricity_x == 5 + assert calisto.thrust_eccentricity_x == 4 + assert calisto.thrust_eccentricity_y == 5 def test_add_cp_eccentricity_assert_properties_set(calisto):