Skip to content
Merged
27 changes: 27 additions & 0 deletions data/motors/Hypertek_835CC125J-K240.eng
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
K240-835CC125J 54 819 0 0.570 1.859 HyperTek
0.131 278.007
0.396 329.552
0.66 338.024
0.925 334.092
1.191 326.199
1.456 319.745
1.721 315.195
1.985 311.182
2.25 302.916
2.516 305.943
2.781 289.975
3.046 281.781
3.31 273.33
3.575 268.852
3.841 255.702
4.106 251.068
4.371 234.82
4.635 159.972
4.9 96.543
5.166 73.367
5.431 55.477
5.696 40.928
5.96 29.542
6.225 21.25
6.491 14.787
6.756 0
945 changes: 945 additions & 0 deletions docs/notebooks/HybridMotor_class_usage.ipynb

Large diffs are not rendered by default.

144 changes: 124 additions & 20 deletions rocketpy/Motor.py
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ def __init__(
# mass = float(desc[4])
# nozzleRadius = diameter/4
# throatRadius = diameter/8
# grainNumber = grainnumber
# grainNumber = grainNumber
# grainVolume = height*np.pi*((diameter/2)**2 -(diameter/4)**2)
# grainDensity = mass/grainVolume
# grainOuterRadius = diameter/2
Expand Down Expand Up @@ -355,15 +355,15 @@ def evaluateMassDot(self):
@abstractmethod
def evaluateCenterOfMass(self):
"""Calculates and returns the time derivative of motor center of mass.
The result is a function of time, object of the Function class, which is stored in self.yCM.
The result is a function of time, object of the Function class, which is stored in self.zCM.

Parameters
----------
None

Returns
-------
yCM : Function
zCM : Function
Position of the center of mass as a function
of time.
"""
Expand Down Expand Up @@ -829,7 +829,7 @@ def __init__(
# mass = float(desc[4])
# nozzleRadius = diameter/4
# throatRadius = diameter/8
# grainNumber = grainnumber
# grainNumber = grainNumber
# grainVolume = height*np.pi*((diameter/2)**2 -(diameter/4)**2)
# grainDensity = mass/grainVolume
# grainOuterRadius = diameter/2
Expand Down Expand Up @@ -943,22 +943,22 @@ def evaluateMassDot(self):

def evaluateCenterOfMass(self):
"""Calculates and returns the time derivative of motor center of mass.
The result is a function of time, object of the Function class, which is stored in self.yCM.
The result is a function of time, object of the Function class, which is stored in self.zCM.

Parameters
----------
None

Returns
-------
yCM : Function
zCM : Function
Position of the center of mass as a function
of time.
"""

self.yCM = 0
self.zCM = 0

return self.yCM
return self.zCM

def evaluateGeometry(self):
"""Calculates grain inner radius and grain height as a
Expand Down Expand Up @@ -1531,7 +1531,7 @@ def __init__(
# mass = float(desc[4])
# nozzleRadius = diameter/4
# throatRadius = diameter/8
# grainNumber = grainnumber
# grainNumber = grainNumber
# grainVolume = height*np.pi*((diameter/2)**2 -(diameter/4)**2)
# grainDensity = mass/grainVolume
# grainOuterRadius = diameter/2
Expand Down Expand Up @@ -1574,8 +1574,8 @@ def __init__(
self.injectorArea = injectorArea
# Other quantities that will be computed
self.massDot = None
self.yCM = None
self.oxidizerInitialMass = None
self.zCM = None
self.liquidInitialMass = None
self.mass = None
self.grainInnerRadius = None
self.grainHeight = None
Expand Down Expand Up @@ -1659,23 +1659,42 @@ def evaluateMassDot(self):

def evaluateCenterOfMass(self):
"""Calculates and returns the time derivative of motor center of mass.
The formulas used are the Bernoulli equation, law of the ideal gases and Boyle's law.
The result is a function of time, object of the Function class, which is stored in self.yCM.
The final mass of the propellant is assumed to be zero,
so a linear extrapolation is used to calculate the position of the center of mass.
The result is a function of time, object of the Function class, which is stored in self.zCM.

Parameters
----------
None

Returns
-------
yCM : Function
zCM : Function
Position of the center of mass as a function
of time.
"""
self.solidInitialMass = self.grainInitialMass * self.grainNumber
self.liquidInitialMass = self.oxidizerInitialVolume * self.oxidizerDensity

self.yCM = 0
self.solidPropellantInitialCM = 0

return self.yCM
self.liquidPropellantInitialCM = (
self.oxidizerInitialVolume / (np.pi * (self.oxidizerTankRadius**2))
) / 2 + self.distanceGrainToTank

zCM0 = (
self.solidInitialMass * self.solidPropellantInitialCM
+ self.liquidInitialMass * self.liquidPropellantInitialCM
) / (self.solidInitialMass + self.liquidInitialMass)

self.zCM = Function(
[(0, zCM0), (self.burnOutTime, 0)],
interpolation="linear",
inputs="Time (s)",
outputs="Propellant center of mass position (m)",
)

return self.zCM

def evaluateGeometry(self):
"""Calculates grain inner radius and grain height as a
Expand Down Expand Up @@ -1971,11 +1990,96 @@ def info(self):

# Show plots
print("\nPlots")
# self.thrust()

self.yCM
self.thrust()
self.zCM()

return None

def allInfo(self):
pass
"""Prints out all data and graphs available about the Motor.

Parameters
----------
None

Return
------
None
"""
# Print nozzle details
print("Nozzle Details")
print("Nozzle Radius: " + str(self.nozzleRadius) + " m")
print("Nozzle Throat Radius: " + str(self.throatRadius) + " m")
print(
"Distance Nozzle - Motor reference point: "
+ str(self.distanceNozzleMotorReference)
+ " m"
)

# Print grain details
print("\nGrain Details")
print("Number of Grains: " + str(self.grainNumber))
print("Grain Spacing: " + str(self.grainSeparation) + " m")
print("Grain Density: " + str(self.grainDensity) + " kg/m3")
print("Grain Outer Radius: " + str(self.grainOuterRadius) + " m")
print("Grain Inner Radius: " + str(self.grainInitialInnerRadius) + " m")
print("Grain Height: " + str(self.grainInitialHeight) + " m")
print("Grain Volume: " + "{:.3f}".format(self.grainInitialVolume) + " m3")
print("Grain Mass: " + "{:.3f}".format(self.grainInitialMass) + " kg")

# Print oxidizer details
print("\nOxidizer Details")
print("Oxidizer Tank Radius: " + str(self.oxidizerTankRadius) + " m")
print("Oxidizer Tank Height: " + str(self.oxidizerTankHeight) + " m")
print(
"Oxidizer Initial Pressure: " + str(self.oxidizerInitialPressure) + " atm"
)
print("Oxidizer Initial Mass: " + str(self.liquidInitialMass) + " kg")
print("Oxidizer Density: " + str(self.oxidizerDensity) + " kg/m3")
print("Oxidizer Molar Mass: " + str(self.oxidizerMolarMass) + " g/mol")
print(
"Oxidizer Initial Volume: "
+ "{:.3f}".format(self.oxidizerInitialVolume)
+ " m3"
)

# Print motor details
print("\nMotor Details")
print("Total Burning Time: " + str(self.burnOutTime) + " s")
print(
"Total Propellant Mass: "
+ "{:.3f}".format(self.propellantInitialMass)
+ " kg"
)
print(
"Propellant Exhaust Velocity: "
+ "{:.3f}".format(self.exhaustVelocity)
+ " m/s"
)
print("Average Thrust: " + "{:.3f}".format(self.averageThrust) + " N")
print(
"Maximum Thrust: "
+ str(self.maxThrust)
+ " N at "
+ str(self.maxThrustTime)
+ " s after ignition."
)
print("Total Impulse: " + "{:.3f}".format(self.totalImpulse) + " Ns")

# Show plots
print("\nPlots")
self.thrust()
self.mass()
self.massDot()
self.zCM()
self.grainInnerRadius()
self.grainHeight()
self.burnRate()
self.burnArea()
self.Kn()
self.inertiaI()
self.inertiaIDot()
self.inertiaZ()
self.inertiaZDot()

return None
2 changes: 1 addition & 1 deletion rocketpy/Rocket.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ def __init__(
self.inertiaZ = inertiaZ

self.centerOfMass = (
(self.distanceRocketMotorReference - self.motor.yCM)
(self.distanceRocketMotorReference - self.motor.zCM)
* motor.mass
/ (mass + motor.mass)
)
Expand Down
30 changes: 30 additions & 0 deletions tests/test_hybridmotor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
from unittest.mock import patch
import os

import numpy as np
import pytest

from rocketpy import HybridMotor


def test_Initial_Center_Of_Mass_Position_correct():
example_motor = HybridMotor(
thrustSource="data/motors/Hypertek_835CC125J-K240.eng",
burnOut=5.4,
distanceNozzleMotorReference=1,
grainNumber=6,
grainDensity=1707,
grainOuterRadius=21.40 / 1000,
grainInitialInnerRadius=9.65 / 1000,
grainInitialHeight=120 / 1000,
oxidizerTankRadius=62.5 / 1000,
oxidizerTankHeight=600 / 1000,
oxidizerInitialPressure=51.03,
oxidizerDensity=1.98,
oxidizerMolarMass=44.01,
oxidizerInitialVolume=62.5 / 1000 * 62.5 / 1000 * np.pi * 600 / 1000,
distanceGrainToTank=200 / 1000,
injectorArea=3e-05,
)

assert abs(example_motor.zCM(0)) - abs(0.005121644685784456) < 1e-6