Skip to content

Commit

Permalink
initiate motor/rocket modulization
Browse files Browse the repository at this point in the history
implements motor tanks
implements guard on motor to cover hybrid and liquid inputs
improves motors model readability
improves motors model readability
improves models visibility
implements MotorTankGeometry
implements API logger
implements hashing for rocket model and udpates api docs
adapts flight controller to hybrid
adapt updates to hybrid
updates flight controller to support hybrid updates
start saving rocket option and motor kind
adapts flight to save rocket options and motor kind state
  • Loading branch information
GabrielBarberini committed Nov 3, 2023
1 parent bd59b9f commit 509887c
Show file tree
Hide file tree
Showing 12 changed files with 563 additions and 274 deletions.
482 changes: 298 additions & 184 deletions lib/api.py

Large diffs are not rendered by default.

50 changes: 35 additions & 15 deletions lib/controllers/flight.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@

import jsonpickle

from lib.models.rocket import Rocket
from lib.models.rocket import Rocket, RocketOptions
from lib.models.motor import Motor, MotorEngines, MotorKinds
from lib.models.flight import Flight
from lib.models.environment import Env
from lib.views.flight import FlightSummary, SurfaceWindConditions, OutOfRailConditions, BurnoutConditions, ApogeeConditions, MaximumValues, InitialConditions, NumericalIntegrationSettings, ImpactConditions, EventsRegistered, LaunchRailConditions, FlightData, FlightCreated, FlightUpdated, FlightDeleted, FlightPickle
Expand All @@ -31,17 +32,20 @@ class FlightController():
- Read a RocketpyFlight object from the database.
"""
def __init__(self, flight: Flight):
rocketpy_rocket = RocketController(flight.rocket).rocketpy_rocket
def __init__(self, flight: Flight, rocket_option: RocketOptions, motor_kind: MotorKinds):
rocketpy_rocket = RocketController(flight.rocket, rocket_option=rocket_option, motor_kind=motor_kind).rocketpy_rocket
rocketpy_env = EnvController(flight.environment).rocketpy_env

rocketpy_flight=RocketpyFlight(
rocket=rocketpy_rocket,
inclination=flight.inclination,
heading=flight.heading,
environment=rocketpy_env,
rail_length=flight.rail_length,
rocketpy_flight = RocketpyFlight(
rocket=rocketpy_rocket,
inclination=flight.inclination,
heading=flight.heading,
environment=rocketpy_env,
rail_length=flight.rail_length
)

self.rocket_option = rocket_option
self.motor_kind = motor_kind
self.rocketpy_flight = rocketpy_flight
self.flight = flight

Expand All @@ -53,7 +57,12 @@ async def create_flight(self) -> "Union[FlightCreated, Response]":
Dict[str, str]: Flight id.
"""
flight = FlightRepository(flight=self.flight)
successfully_created_flight = await flight.create_flight()
successfully_created_flight = \
await flight.create_flight(
motor_kind = self.motor_kind,
rocket_option = self.rocket_option
)

if successfully_created_flight:
return FlightCreated(flight_id=str(flight.flight_id))
return Response(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR)
Expand Down Expand Up @@ -98,7 +107,9 @@ async def get_rocketpy_flight(flight_id: int) -> "Union[FlightPickle, Response]"
return Response(status_code=status.HTTP_404_NOT_FOUND)

successfully_read_rocketpy_flight = \
FlightController(successfully_read_flight).rocketpy_flight
FlightController(flight = successfully_read_flight,
rocket_option = RocketOptions(successfully_read_flight.rocket._rocket_option),
motor_kind = MotorKinds(successfully_read_flight.rocket.motor._motor_kind)).rocketpy_flight

return FlightPickle(jsonpickle_rocketpy_flight=jsonpickle.encode(successfully_read_rocketpy_flight))

Expand All @@ -121,7 +132,10 @@ async def update_flight(self, flight_id: int) -> "Union[FlightUpdated, Response]
return Response(status_code=status.HTTP_404_NOT_FOUND)

successfully_updated_flight = \
await FlightRepository(flight=self.flight, flight_id=flight_id).update_flight()
await FlightRepository(flight=self.flight, flight_id=flight_id).update_flight(
rocket_option = self.rocket_option,
motor_kind = self.motor_kind
)

if successfully_updated_flight:
return FlightUpdated(new_flight_id=str(successfully_updated_flight))
Expand Down Expand Up @@ -158,7 +172,7 @@ async def update_env(flight_id: int, env: Env) -> "Union[FlightUpdated, Response
return Response(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR)

@staticmethod
async def update_rocket(flight_id: int, rocket: Rocket) -> "Union[FlightUpdated, Response]":
async def update_rocket(flight_id: int, rocket: Rocket, rocket_option, motor_kind) -> "Union[FlightUpdated, Response]":
"""
Update the rocket of a flight in the database.
Expand All @@ -178,7 +192,11 @@ async def update_rocket(flight_id: int, rocket: Rocket) -> "Union[FlightUpdated,
return Response(status_code=status.HTTP_404_NOT_FOUND)
flight = successfully_read_flight.dict()

flight["rocket"] = rocket
updated_rocket = rocket.dict()
updated_rocket["rocket_option"] = rocket_option
updated_rocket["motor"]["motor_kind"] = motor_kind
flight["rocket"] = Rocket(**updated_rocket)

flight = Flight(**flight)
successfully_updated_flight = \
await FlightRepository(flight=flight, flight_id=flight_id).update_flight()
Expand Down Expand Up @@ -231,7 +249,9 @@ async def simulate(flight_id: int) -> "Union[FlightSummary, Response]":
if not successfully_read_flight:
return Response(status_code=status.HTTP_404_NOT_FOUND)

flight = FlightController(successfully_read_flight).rocketpy_flight
flight = FlightController(flight = successfully_read_flight,
rocket_option = RocketOptions(successfully_read_flight.rocket._rocket_option),
motor_kind = MotorKinds(successfully_read_flight.rocket.motor._motor_kind)).rocketpy_flight

_initial_conditions = InitialConditions(
initial_altitude = "Attitude - e0: {:.3f} | e1: {:.3f} | e2: {:.3f} | e3: {:.3f}".format(flight.e0(0), flight.e1(0), flight.e2(0), flight.e3(0)),
Expand Down
80 changes: 57 additions & 23 deletions lib/controllers/motor.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
from fastapi import Response, status
from typing import Any, Dict, Union
from rocketpy.motors.solid_motor import SolidMotor
from rocketpy.motors.liquid_motor import LiquidMotor
from rocketpy.motors.hybrid_motor import HybridMotor
from rocketpy import TankGeometry, Fluid
import jsonpickle

from lib.models.motor import Motor
from lib.models.motor import Motor, MotorKinds
from lib.repositories.motor import MotorRepository
from lib.views.motor import MotorSummary, MotorData, MotorCreated, MotorUpdated, MotorDeleted, MotorPickle

Expand All @@ -17,26 +20,55 @@ class MotorController():
Enables:
- Create a rocketpy.Motor object from a Motor model object.
"""
def __init__(self, motor: Motor):
rocketpy_motor = SolidMotor(
burn_time=motor.burn_time,
grain_number=motor.grain_number,
grain_density=motor.grain_density,
grain_outer_radius=motor.grain_outer_radius,
grain_initial_inner_radius=motor.grain_initial_inner_radius,
grain_initial_height=motor.grain_initial_height,
grains_center_of_mass_position=-motor.grains_center_of_mass_position,
thrust_source=f"lib/data/motors/{motor.thrust_source.value}.eng",
grain_separation=motor.grain_separation,
nozzle_radius=motor.nozzle_radius,
dry_mass=motor.dry_mass,
center_of_dry_mass_position=motor.center_of_dry_mass_position,
dry_inertia=motor.dry_inertia,
throat_radius=motor.throat_radius,
interpolation_method=motor.interpolation_method
)
def __init__(self, motor: Motor, motor_kind):
self.guard(motor, motor_kind)
motor_core = {
"thrust_source": f"lib/data/motors/{motor.thrust_source.value}.eng",
"burn_time": motor.burn_time,
"nozzle_radius": motor.nozzle_radius,
"dry_mass": motor.dry_mass,
"dry_inertia": motor.dry_inertia,
"center_of_dry_mass_position": motor.center_of_dry_mass_position
}

match motor_kind:
case MotorKinds.liquid:
rocketpy_motor = LiquidMotor(**motor_core)
case MotorKinds.hybrid:
rocketpy_motor = HybridMotor(**motor_core,
throat_radius=motor.throat_radius,
grain_number=motor.grain_number,
grain_density=motor.grain_density,
grain_outer_radius=motor.grain_outer_radius,
grain_initial_inner_radius=motor.grain_initial_inner_radius,
grain_initial_height=motor.grain_initial_height,
grain_separation=motor.grain_separation,
grains_center_of_mass_position=motor.grains_center_of_mass_position
)
case _:
rocketpy_motor = SolidMotor(**motor_core,
grain_number=motor.grain_number,
grain_density=motor.grain_density,
grain_outer_radius=motor.grain_outer_radius,
grain_initial_inner_radius=motor.grain_initial_inner_radius,
grain_initial_height=motor.grain_initial_height,
grains_center_of_mass_position=motor.grains_center_of_mass_position,
grain_separation=motor.grain_separation,
throat_radius=motor.throat_radius,
interpolation_method=motor.interpolation_method
)

if motor_kind != MotorKinds.solid:
for tank in motor.tanks:
rocketpy_motor.add_tank(tank.tank, tank.position)

self.motor_kind = motor_kind #tracks motor kind state
self.rocketpy_motor = rocketpy_motor
self.motor = motor

def guard(self, motor: Motor, motor_kind):
if motor_kind != MotorKinds.solid and motor.tanks is None:
return Response(status_code=status.HTTP_400_BAD_REQUEST, content="Tanks must be provided for liquid and hybrid motors.")

async def create_motor(self) -> "Union[MotorCreated, Response]":
"""
Expand All @@ -46,7 +78,7 @@ async def create_motor(self) -> "Union[MotorCreated, Response]":
Dict[str, str]: motor id.
"""
motor = MotorRepository(motor=self.motor)
successfully_created_motor = await motor.create_motor()
successfully_created_motor = await motor.create_motor(motor_kind = self.motor_kind)
if successfully_created_motor:
return MotorCreated(motor_id=str(motor.motor_id))
return Response(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR)
Expand Down Expand Up @@ -91,7 +123,8 @@ async def get_rocketpy_motor(motor_id: int) -> "Union[MotorPickle, Response]":
return Response(status_code=status.HTTP_404_NOT_FOUND)

successfully_read_rocketpy_motor = \
MotorController( successfully_read_motor ).rocketpy_motor
MotorController(motor = successfully_read_motor,
motor_kind = MotorKinds(successfully_read_motor._motor_kind)).rocketpy_motor

return MotorPickle(jsonpickle_rocketpy_motor=jsonpickle.encode(successfully_read_rocketpy_motor))

Expand All @@ -114,7 +147,7 @@ async def update_motor(self, motor_id: int) -> "Union[MotorUpdated, Response]":
return Response(status_code=status.HTTP_404_NOT_FOUND)

successfully_updated_motor = \
await MotorRepository(motor=self.motor, motor_id=motor_id).update_motor()
await MotorRepository(motor=self.motor, motor_id=motor_id).update_motor(motor_kind = self.motor_kind)

if successfully_updated_motor:
return MotorUpdated(new_motor_id=str(successfully_updated_motor))
Expand Down Expand Up @@ -164,7 +197,8 @@ async def simulate(motor_id: int) -> "Union[MotorSummary, Response]":
if not successfully_read_motor:
return Response(status_code=status.HTTP_404_NOT_FOUND)

motor = MotorController(successfully_read_motor).rocketpy_motor
motor = MotorController(motor=successfully_read_motor,
motor_kind = MotorKinds(successfully_read_motor._motor_kind)).rocketpy_motor
motor_simulation_numbers = MotorData(
total_burning_time="Total Burning Time: " + str(motor.burn_out_time) + " s",

Expand Down
24 changes: 15 additions & 9 deletions lib/controllers/rocket.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
from rocketpy.rocket.aero_surface import Tail as RocketpyTail

from lib.controllers.motor import MotorController
from lib.models.rocket import Rocket
from lib.models.rocket import Rocket, RocketOptions
from lib.models.motor import MotorKinds
from lib.models.aerosurfaces import NoseCone, TrapezoidalFins, Tail
from lib.models.parachute import Parachute
from lib.repositories.rocket import RocketRepository
Expand All @@ -28,13 +29,13 @@ class RocketController():
Enables:
create a RocketpyRocket object from a Rocket model object.
"""
def __init__(self, rocket: Rocket):
def __init__(self, rocket: Rocket, rocket_option, motor_kind):
rocketpy_rocket = RocketpyRocket(
radius=rocket.radius,
mass=rocket.mass,
inertia=rocket.inertia,
power_off_drag=f"lib/data/{rocket.power_off_drag.value}/powerOffDragCurve.csv",
power_on_drag=f"lib/data/{rocket.power_on_drag.value}/powerOnDragCurve.csv",
power_off_drag=f"lib/data/{rocket_option.value.lower()}/powerOffDragCurve.csv",
power_on_drag=f"lib/data/{rocket_option.value.lower()}/powerOnDragCurve.csv",
center_of_mass_without_motor=rocket.center_of_mass_without_motor,
coordinate_system_orientation=rocket.coordinate_system_orientation
)
Expand All @@ -43,7 +44,7 @@ def __init__(self, rocket: Rocket):
rocketpy_rocket.set_rail_buttons(upper_button_position=rocket.rail_buttons.upper_button_position,
lower_button_position=rocket.rail_buttons.lower_button_position,
angular_position=rocket.rail_buttons.angular_position)
rocketpy_rocket.add_motor(MotorController(rocket.motor).rocketpy_motor,
rocketpy_rocket.add_motor(MotorController(rocket.motor, motor_kind).rocketpy_motor,
rocket.motor_position)

#NoseCone
Expand Down Expand Up @@ -73,6 +74,7 @@ def __init__(self, rocket: Rocket):
print("Parachute trigger not valid. Skipping parachute.")
continue

self.rocket_option = rocket_option #tracks rocket option state
self.rocketpy_rocket = rocketpy_rocket
self.rocket = rocket

Expand Down Expand Up @@ -231,7 +233,7 @@ async def create_rocket(self) -> "Union[RocketCreated, Response]":
Dict[str, str]: Rocket id.
"""
rocket = RocketRepository(rocket=self.rocket)
successfully_created_rocket = await rocket.create_rocket()
successfully_created_rocket = await rocket.create_rocket(rocket_option = self.rocket_option)
if successfully_created_rocket:
return RocketCreated(rocket_id=str(rocket.rocket_id))
return Response(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR)
Expand Down Expand Up @@ -276,7 +278,9 @@ async def get_rocketpy_rocket(rocket_id: int) -> "Union[RocketPickle, Response]"
return Response(status_code=status.HTTP_404_NOT_FOUND)

successfully_read_rocketpy_rocket = \
RocketController( successfully_read_rocket ).rocketpy_rocket
RocketController(rocket=successfully_read_rocket,
rocket_option = RocketOptions(successfully_read_rocket._rocket_option),
motor_kind = MotorKinds(successfully_read_rocket.motor._motor_kind)).rocketpy_rocket

return RocketPickle(jsonpickle_rocketpy_rocket=jsonpickle.encode(successfully_read_rocketpy_rocket))

Expand All @@ -299,7 +303,7 @@ async def update_rocket(self, rocket_id: int) -> "Union[RocketUpdated, Response]
return Response(status_code=status.HTTP_404_NOT_FOUND)

successfully_updated_rocket = \
await RocketRepository(rocket=self.rocket, rocket_id=rocket_id).update_rocket()
await RocketRepository(rocket=self.rocket, rocket_id=rocket_id).update_rocket(rocket_option = self.rocket_option)

if successfully_updated_rocket:
return RocketUpdated(new_rocket_id=str(successfully_updated_rocket))
Expand Down Expand Up @@ -349,7 +353,9 @@ async def simulate(rocket_id: int) -> "Union[RocketSummary, Response]":
if not successfully_read_rocket:
return Response(status_code=status.HTTP_404_NOT_FOUND)

rocket = RocketController(successfully_read_rocket).rocketpy_rocket
rocket = RocketController(rocket=successfully_read_rocket,
rocket_option = RocketOptions(successfully_read_rocket._rocket_option),
motor_kind = MotorKinds(successfully_read_rocket.motor._motor_kind)).rocketpy_rocket

_inertia_details = InertiaDetails(
rocket_mass_without_propellant = "Rocket Mass: {:.3f} kg (No Propellant)".format(rocket.mass),
Expand Down
2 changes: 2 additions & 0 deletions lib/models/environment.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ class Env(BaseModel, frozen=True):
latitude: float = 0
longitude: float = 0
elevation: Optional[int] = 1400

#Opional parameters
atmospheric_model_type: Optional[str] = 'standard_atmosphere'
atmospheric_model_file: Optional[str] = 'GFS'
date: Optional[datetime.datetime] = datetime.datetime.today() + datetime.timedelta(days=1)
Loading

0 comments on commit 509887c

Please sign in to comment.