Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ENH: solid motors draw #435

Merged
merged 4 commits into from
Oct 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions rocketpy/motors/solid_motor.py
Original file line number Diff line number Diff line change
Expand Up @@ -701,6 +701,10 @@ def propellant_I_13(self):
def propellant_I_23(self):
return 0

def draw(self):
self.plots.draw()
return None

def info(self):
"""Prints out basic data about the SolidMotor."""
self.prints.all()
Expand Down
36 changes: 31 additions & 5 deletions rocketpy/plots/rocket_plots.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import numpy as np
import matplotlib.pyplot as plt
import numpy as np

from rocketpy.motors import HybridMotor, LiquidMotor, SolidMotor
from rocketpy.plots import _generate_nozzle
from rocketpy.rocket.aero_surface import Fins, NoseCone, Tail


Expand Down Expand Up @@ -159,7 +161,7 @@ def draw(self, vis_args=None):
}

# Create the figure and axis
_, ax = plt.subplots(figsize=(8, 5))
_, ax = plt.subplots(figsize=(8, 6))
ax.set_aspect("equal")
ax.set_facecolor(vis_args["background"])
ax.grid(True, linestyle="--", linewidth=0.5)
Expand Down Expand Up @@ -335,6 +337,30 @@ def draw(self, vis_args=None):
ax.scatter(
nozzle_position, 0, label="Nozzle Outlet", color="brown", s=10, zorder=10
)

if isinstance(self.rocket.motor, (SolidMotor, HybridMotor)):
chamber = self.rocket.motor.plots._generate_combustion_chamber(
translate=(nozzle_position, 0), csys=self.rocket._csys
)
ax.add_patch(chamber)
grains = self.rocket.motor.plots._generate_grains(
translate=(nozzle_position, 0), csys=self.rocket._csys
)
for grain in grains:
ax.add_patch(grain)

elif isinstance(self.rocket.motor, LiquidMotor):
patches = self.rocket.motor.plots._generate_positioned_tanks(
translate=(self.rocket.motor_position, 0), csys=self.rocket._csys
)
for patch in patches:
ax.add_patch(patch)

nozzle = _generate_nozzle(
self.rocket.motor, translate=(nozzle_position, 0), csys=self.rocket._csys
)
ax.add_patch(nozzle)

# Check if nozzle is beyond the last surface, if so draw a tube
# to it, with the radius of the last surface
if self.rocket._csys == 1:
Expand Down Expand Up @@ -393,16 +419,16 @@ def draw(self, vis_args=None):
ax.scatter(cm, 0, color="black", label="Center of Mass", s=30)
ax.scatter(cm, 0, facecolors="none", edgecolors="black", s=100)

cp = self.rocket.cp_position
ax.scatter(cp, 0, label="Center Of Pressure", color="red", s=30, zorder=10)
cp = self.rocket.cp_position(0)
ax.scatter(cp, 0, label="CP (M=0)", color="red", s=30, zorder=10)
ax.scatter(cp, 0, facecolors="none", edgecolors="red", s=100, zorder=10)

# Set plot attributes
plt.title(f"Rocket Geometry")
plt.ylim([-self.rocket.radius * 4, self.rocket.radius * 6])
plt.xlabel("Position (m)")
plt.ylabel("Radius (m)")
plt.legend(loc="best")
plt.legend(bbox_to_anchor=(1.05, 1), loc="upper left")
plt.tight_layout()
plt.show()

Expand Down
145 changes: 145 additions & 0 deletions rocketpy/plots/solid_motor_plots.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.patches import Polygon

from rocketpy.plots import _generate_nozzle


class _SolidMotorPlots:
"""Class that holds plot methods for SolidMotor class.

Expand Down Expand Up @@ -361,6 +368,144 @@ def I_23(self, lower_limit=None, upper_limit=None):

return None

def draw(self):
"""Draws a simple 2D representation of the SolidMotor."""
fig, ax = plt.subplots(figsize=(10, 6))
ax.set_aspect("equal")

_csys = self.solid_motor._csys

chamber = self._generate_combustion_chamber(_csys=_csys)
nozzle = _generate_nozzle(self.solid_motor)
grains = self._generate_grains(_csys=_csys, translate=(0, 0))

ax.add_patch(chamber)
for grain in grains:
ax.add_patch(grain)
ax.add_patch(nozzle)

# self._draw_nozzle(ax, nozzle_height, csys)
# self._draw_combustion_chamber(ax, csys)
# self._draw_grains(ax, csys)
self._draw_center_of_mass(ax)

self._set_plot_properties(ax)
plt.show()
return None

def _generate_combustion_chamber(self, translate=(0, 0), csys=1):
# csys = self.solid_motor.csys
chamber_length = (
abs(
self.solid_motor.center_of_dry_mass_position
- self.solid_motor.nozzle_position
)
* 2
) * csys
x = np.array(
[
self.solid_motor.nozzle_position,
self.solid_motor.nozzle_position,
self.solid_motor.nozzle_position + chamber_length,
self.solid_motor.nozzle_position + chamber_length,
]
)
y = np.array(
[
self.solid_motor.nozzle_radius,
self.solid_motor.grain_outer_radius * 1.4,
self.solid_motor.grain_outer_radius * 1.4,
0,
]
)
# we need to draw the other half of the nozzle
x = np.concatenate([x, x[::-1]])
y = np.concatenate([y, -y[::-1]])
# now we need to sum the the translate
x = x + translate[0]
y = y + translate[1]

patch = Polygon(
np.column_stack([x, y]),
label="Combustion Chamber",
facecolor="lightslategray",
edgecolor="black",
)
return patch

def _generate_grains(self, translate=(0, 0), csys=1):
patches = []
n_total = self.solid_motor.grain_number
separation = self.solid_motor.grain_separation
height = self.solid_motor.grain_initial_height
outer_radius = self.solid_motor.grain_outer_radius
inner_radius = self.solid_motor.grain_initial_inner_radius

cm_teo = (
csys * ((n_total / 2) * (height + separation))
+ self.solid_motor.nozzle_position
)
cm_real = self.solid_motor.center_of_propellant_mass(0)

init = abs(cm_teo - cm_real) * csys

inner_y = np.array([0, inner_radius, inner_radius, 0])
outer_y = np.array([inner_radius, outer_radius, outer_radius, inner_radius])
inner_y = np.concatenate([inner_y, -inner_y[::-1]])
outer_y = np.concatenate([outer_y, -outer_y[::-1]])
inner_y = inner_y + translate[1]
outer_y = outer_y + translate[1]
for n in range(n_total):
grain_start = init + csys * (separation / 2 + n * (height + separation))
grain_end = grain_start + height * csys
x = np.array([grain_start, grain_start, grain_end, grain_end])
# draw the other half of the nozzle
x = np.concatenate([x, x[::-1]])
# sum the translate
x = x + translate[0]
patch = Polygon(
np.column_stack([x, outer_y]),
facecolor="olive",
edgecolor="khaki",
)
patches.append(patch)

patch = Polygon(
np.column_stack([x, inner_y]),
facecolor="khaki",
edgecolor="olive",
)
if n == 0:
patch.set_label("Grains")
patches.append(patch)
return patches

def _draw_center_of_mass(self, ax):
ax.axhline(0, color="k", linestyle="--", alpha=0.5) # symmetry line
ax.plot(
[self.solid_motor.grains_center_of_mass_position],
[0],
"ro",
label="Grains Center of Mass",
)
ax.plot(
[self.solid_motor.center_of_dry_mass_position],
[0],
"bo",
label="Center of Dry Mass",
)

def _set_plot_properties(self, ax):
ax.set_title("Solid Motor Representation")
ax.set_ylabel("Radius (m)")
ax.set_xlabel("Position (m)")
# ax.grid(True)
plt.ylim(
-self.solid_motor.grain_outer_radius * 1.2 * 1.7,
self.solid_motor.grain_outer_radius * 1.2 * 1.7,
)
plt.legend(loc="upper left", bbox_to_anchor=(1, 1))

def all(self):
"""Prints out all graphs available about the SolidMotor. It simply calls
all the other plotter methods in this class.
Expand Down
Loading