8000 implements rocketpy encoders · RocketPy-Team/Infinity-API@9339a43 · GitHub
[go: up one dir, main page]

Skip to content

Commit 9339a43

Browse files
implements rocketpy encoders
1 parent b944744 commit 9339a43

File tree

9 files changed

+344
-288
lines changed

9 files changed

+344
-288
lines changed

src/services/environment.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@
33
import dill
44

55
from rocketpy.environment.environment import Environment as RocketPyEnvironment
6-
from rocketpy.utilities import get_instance_attributes
76
from src.models.environment import EnvironmentModel
87
from src.views.environment import EnvironmentSimulation
8+
from src.utils import rocketpy_encoder, DiscretizeConfig
99

1010

1111
class EnvironmentService:
@@ -50,7 +50,9 @@ def get_environment_simulation(self) -> EnvironmentSimulation:
5050
EnvironmentSimulation
5151
"""
5252

53-
attributes = get_instance_attributes(self.environment)
53+
attributes = rocketpy_encoder(
54+
self.environment, DiscretizeConfig.for_environment()
55+
)
5456
env_simulation = EnvironmentSimulation(**attributes)
5557
return env_simulation
5658

src/services/flight.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@
33
import dill
44

55
from rocketpy.simulation.flight import Flight as RocketPyFlight
6-
from rocketpy.utilities import get_instance_attributes
76

87
from src.services.environment import EnvironmentService
98
from src.services.rocket import RocketService
109
from src.models.flight import FlightModel
1110
from src.views.flight import FlightSimulation
11+
from src.utils import rocketpy_encoder, DiscretizeConfig
1212

1313

1414
class FlightService:
@@ -55,7 +55,9 @@ def get_flight_simulation(self) -> FlightSimulation:
5555
Returns:
5656
FlightSimulation
5757
"""
58-
attributes = get_instance_attributes(self.flight)
58+
attributes = rocketpy_encoder(
59+
self.flight, DiscretizeConfig.for_flight()
60+
)
5961
flight_simulation = FlightSimulation(**attributes)
6062
return flight_simulation
6163

src/services/motor.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
from rocketpy.motors.solid_motor import SolidMotor
77
from rocketpy.motors.liquid_motor import LiquidMotor
88
from rocketpy.motors.hybrid_motor import HybridMotor
9-
from rocketpy.utilities import get_instance_attributes
109
from rocketpy import (
1110
LevelBasedTank,
1211
MassBasedTank,
@@ -18,6 +17,7 @@
1817
from src.models.sub.tanks import TankKinds
1918
from src.models.motor import MotorKinds, MotorModel
2019
from src.views.motor import MotorSimulation
20+
from src.utils import rocketpy_encoder, DiscretizeConfig
2121

2222

2323
class MotorService:
@@ -140,7 +140,7 @@ def get_motor_simulation(self) -> MotorSimulation:
140140
Returns:
141141
MotorSimulation
142142
"""
143-
attributes = get_instance_attributes(self.motor)
143+
attributes = rocketpy_encoder(self.motor, DiscretizeConfig.for_motor())
144144
motor_simulation = MotorSimulation(**attributes)
145145
return motor_simulation
146146

src/services/rocket.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,13 @@
1111
Fins as RocketPyFins,
1212
Tail as RocketPyTail,
1313
)
14-
from rocketpy.utilities import get_instance_attributes
1514

1615
from src import logger
1716
from src.models.rocket import RocketModel, Parachute
1817
from src.models.sub.aerosurfaces import NoseCone, Tail, Fins
1918
from src.services.motor import MotorService
2019
from src.views.rocket import RocketSimulation
20+
from src.utils import rocketpy_encoder, DiscretizeConfig
2121

2222

2323
class RocketService:
@@ -107,7 +107,9 @@ def get_rocket_simulation(self) -> RocketSimulation:
107107
Returns:
108108
RocketSimulation
109109
"""
110-
attributes = get_instance_attributes(self.rocket)
110+
attributes = rocketpy_encoder(
111+
self.rocket, DiscretizeConfig.for_rocket()
112+
)
111113
rocket_simulation = RocketSimulation(**attributes)
112114
return rocket_simulation
113115

src/utils.py

Lines changed: 91 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,114 @@
11
# fork of https://github.com/encode/starlette/blob/master/starlette/middleware/gzip.py
22
import gzip
33
import io
4+
import logging
5+
import json
46

5-
from typing import Annotated, NoReturn, Any
6-
import numpy as np
7+
from typing import NoReturn, Tuple
78

8-
from pydantic import PlainSerializer
9+
from rocketpy import Function
10+
from rocketpy._encoders import RocketPyEncoder
911
from starlette.datastructures import Headers, MutableHeaders
1012
from starlette.types import ASGIApp, Message, Receive, Scope, Send
1113

14+
logger = logging.getLogger(__name__)
1215

13-
def to_python_primitive(v: Any) -> Any:
16+
17+
class DiscretizeConfig:
1418
"""
15-
Convert complex types to Python primitives.
19+
Configuration class for RocketPy function discretization.
1620
17-
Args:
18-
v: Any value, particularly those with a 'source' attribute
19-
containing numpy arrays or generic types.
21+
This class allows easy configuration of discretization parameters
22+
for different types of RocketPy objects and their callable attributes.
23+
"""
2024

21-
Returns:
22-
The primitive representation of the input value.
25+
def __init__(
26+
self, bounds: Tuple[float, float] = (0, 10), samples: int = 200
27+
):
28+
self.bounds = bounds
29+
self.samples = samples
30+
31+
@classmethod
32+
def for_environment(cls) -> 'DiscretizeConfig':
33+
return cls(bounds=(0, 50000), samples=100)
34+
35+
@classmethod
36+
def for_motor(cls) -> 'DiscretizeConfig':
37+
return cls(bounds=(0, 10), samples=150)
38+
39+
@classmethod
40+
def for_rocket(cls) -> 'DiscretizeConfig':
41+
return cls(bounds=(0, 1), samples=100)
42+
43+
@classmethod
44+
def for_flight(cls) -> 'DiscretizeConfig':
45+
return cls(bounds=(0, 30), samples=200)
46+
47+
48+
def rocketpy_encoder(obj, config: DiscretizeConfig = DiscretizeConfig()):
2349
"""
24-
if hasattr(v, "source"):
25-
if isinstance(v.source, np.ndarray):
26-
return v.source.tolist()
50 10000 +
Encode a RocketPy object using official RocketPy encoders.
2751
28-
if isinstance(v.source, (np.generic,)):
29-
return v.source.item()
52+
This function discretizes callable Function attributes and then uses
53+
RocketPy's official RocketPyEncoder for complete object serialization.
3054
31-
return str(v.source)
55+
Args:
56+
obj: RocketPy object (Environment, Motor, Rocket, Flight)
57+
config: DiscretizeConfig object with discretization parameters (optional)
3258
33-
if isinstance(v, (np.generic,)):
34-
return v.item()
59+
Returns:
60+
Dictionary of encoded attributes
61+
"""
3562

36-
if isinstance(v, (np.ndarray,)):
37-
return v.tolist()
63+
for attr_name in dir(obj):
64+
if attr_name.startswith('_'):
65+
continue
66+
67+
try:
68+
attr_value = getattr(obj, attr_name)
69+
except Exception:
70+
continue
71+
72+
if callable(attr_value) and isinstance(attr_value, Function):
73+
try:
74+
# Create a new Function from the source to avoid mutating the original object.
75+
# This is important because:
76+
# 1. The original RocketPy object should remain unchanged for reusability
77+
# 2. Multiple simulations might need different discretization parameters
78+
# 3. Other parts of the system might depend on the original continuous function
79+
discretized_func = Function(attr_value.source)
80+
discretized_func.set_discrete(
81+
lower=config.bounds[0],
82+
upper=config.bounds[1],
83+
samples=config.samples,
84+
mutate_self=True,
85+
)
3886

39-
return str(v)
87+
setattr(obj, attr_name, discretized_func)
4088

89+
except Exception as e:
90+
logger.warning(f"Failed to discretize {attr_name}: {e}")
4191

42-
AnyToPrimitive = Annotated[
43-
Any,
44-
PlainSerializer(to_python_primitive),
45-
]
92+
try:
93+
json_str = json.dumps(
94+
obj,
95+
cls=RocketPyEncoder,
96+
include_outputs=True,
97+
include_function_data=True,
98+
)
99+
return json.loads(json_str)
100+
except Exception as e:
101+
logger.warning(f"Failed to encode with RocketPyEncoder: {e}")
102+
attributes = {}
103+
for attr_name in dir(obj):
104+
if not attr_name.startswith('_'):
105+
try:
106+
attr_value = getattr(obj, attr_name)
107+
if not callable(attr_value):
108+
attributes[attr_name] = str(attr_value)
109+
except Exception:
110+
continue
111+
return attributes
46112

47113

48114
class RocketPyGZipMiddleware:

src/views/environment.py

Lines changed: 46 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,24 @@
1-
from typing import Optional
1+
from typing import Optional, Any
22
from datetime import datetime, timedelta
3+
from pydantic import ConfigDict
34
from src.views.interface import ApiBaseView
45
from src.models.environment import EnvironmentModel
5-
from src.utils import AnyToPrimitive
66

77

88
class EnvironmentSimulation(ApiBaseView):
9+
"""
10+
Environment simulation view that handles dynamically encoded RocketPy Environment attributes.
11+
12+
Uses the new rocketpy_encoder which may return different attributes based on the
13+
actual RocketPy Environment object. The model allows extra fields to accommodate
14+
any new attributes that might be encoded.
15+
"""
16+
17+
model_config = ConfigDict(extra='allow', arbitrary_types_allowed=True)
18+
919
message: str = "Environment successfully simulated"
20+
21+
# Core Environment attributes (always present)
1022
latitude: Optional[float] = None
1123
longitude: Optional[float] = None
1224
elevation: Optional[float] = 1
@@ -26,27 +38,38 @@ class EnvironmentSimulation(ApiBaseView):
2638
date: Optional[datetime] = datetime.today() + timedelta(days=1)
2739
local_date: Optional[datetime] = datetime.today() + timedelta(days=1)
2840
datetime_date: Optional[datetime] = datetime.today() + timedelta(days=1)
29-
ellipsoid: Optional[AnyToPrimitive] = None
30-
barometric_height: Optional[AnyToPrimitive] = None
31-
barometric_height_ISA: Optional[AnyToPrimitive] = None
32-
pressure: Optional[AnyToPrimitive] = None
33-
pressure_ISA: Optional[AnyToPrimitive] = None
34-
temperature: Optional[AnyToPrimitive] = None
35-
temperature_ISA: Optional[AnyToPrimitive] = None
36-
density: Optional[AnyToPrimitive] = None
37-
speed_of_sound: Optional[AnyToPrimitive] = None
38-
dynamic_viscosity: Optional[AnyToPrimitive] = None
39-
gravity: Optional[AnyToPrimitive] = None
40-
somigliana_gravity: Optional[AnyToPrimitive] = None
41-
wind_speed: Optional[AnyToPrimitive] = None
42-
wind_direction: Optional[AnyToPrimitive] = None
43-
wind_heading: Optional[AnyToPrimitive] = None
44-
wind_velocity_x: Optional[AnyToPrimitive] = None
45-
wind_velocity_y: Optional[AnyToPrimitive] = None
46-
calculate_earth_radius: Optional[AnyToPrimitive] = None
47-
decimal_degrees_to_arc_seconds: Optional[AnyToPrimitive] = None
48-
geodesic_to_utm: Optional[AnyToPrimitive] = None
49-
utm_to_geodesic: Optional[AnyToPrimitive] = None
41+
42+
# Function attributes (discretized by rocketpy_encoder, serialized by RocketPyEncoder)
43+
ellipsoid: Optional[Any] = None
44+
barometric_height: Optional[Any] = None
45+
barometric_height_ISA: Optional[Any] = None
46+
pressure: Optional[Any] = None
47+
pressure_ISA: Optional[Any] = None
48+
temperature: Optional[Any] = None
49+
temperature_ISA: Optional[Any] = None
50+
density: Optional[Any] = None
51+
speed_of_sound: Optional[Any] = None
52+
dynamic_viscosity: Optional[Any] = None
53+
gravity: Optional[Any] = None
54+
somigliana_gravity: Optional[Any] = None
55+
wind_speed: Optional[Any] = None
56+
wind_direction: Optional[Any] = None
57+
wind_heading: Optional[Any] = None
58+
wind_velocity_x: Optional[Any] = None
59+
wind_velocity_y: Optional[Any] = None
60+
calculate_earth_radius: Optional[Any] = None
61+
decimal_degrees_to_arc_seconds: Optional[Any] = None
62+
geodesic_to_utm: Optional[Any] = None
63+
utm_to_geodesic: Optional[Any] = None
64+
65+
def __init__(self, **data):
66+
"""
67+
Initialize with dynamic attribute handling.
68+
69+
Any additional attributes returned by rocketpy_encoder will be stored
70+
as extra fields thanks to the 'allow' extra configuration.
71+
"""
72+
super().__init__(**data)
5073

5174

5275
class EnvironmentView(EnvironmentModel):

0 commit comments

Comments
 (0)
0