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: Expansion of Encoders Implementation for Full Flights. #679

Open
wants to merge 19 commits into
base: develop
Choose a base branch
from

Conversation

phmbressan
Copy link
Collaborator

@phmbressan phmbressan commented Aug 30, 2024

Pull request type

  • Code changes (bugfix, features)

Checklist

  • Tests for the changes have been added (partially done)
  • Docs have been reviewed and added / updated (future PR)
  • Lint (black rocketpy/ tests/) has passed locally
  • All tests (pytest tests -m slow --runslow) have passed locally
  • CHANGELOG.md has been updated (if relevant)

Current behavior

Currently the _encoding module is just a stub and cannot still encode full simulations into a JSON like format.

New behavior

This PR expand this module bringing the following:

  • Custom Encoding for more types;
  • Customized behavior for some required classes with methods to_dict;
  • Support for Function encoding.

This PR also introduces decoding functionalities. The way this works is:

  • If the is a to_dict or from_dict method, they are used to perform the (de)encoding;
  • Otherwise, a default encoding of all vars() and constructor based decoding is done.

Breaking change

  • Yes
  • No

Additional information

There is much to discuss here on the implementation and maintainability side of things. Main discussion points:

  • Should we add a to_dict and (in the future) a from_dict method to some (or all) rocketpy classes?
    • My Opinion: I think adding the methods to everything might be the only way out. But there is some work and maintainability considerations.
  • The current status of the encoding can be fully deserialized?
    • My Opinion: All the information is there, but the typing should be improved to know which objects to decode (deserialize) into. For that reason, I kept the _encoders private.
  • Are there any other modules to make our work easier?
    • From my research, I have tested the following:
      • simplejson: brings some more types and speed into encoding, but does not help much with custom types;
      • jsonpickle: really great for the typing handler (already writes by default each object type) and is able to serialize. The problem is that is does not handle functions/lambdas and I did not find a good way to implement custom handlers for our use case;
      • pickle / dill: the most plug and play solution, however the output is not human readable and not generally compatible between different rocketpy versions.

Of course, this is what I understood upon researching and testing, feel free to make any comments or suggest other modules.

Remarks and Future Considerations

Here are some of the steps that could be improved:

  • Verify why the test for hybrid motor flight requires higher tolerances;
    • Corrected, no higher tolerances needed anymore.
  • Test if all of the atmospheric models of Environments can be decoded without requiring re-read of files or API;
  • Evaluate whether we should encode outputs or only the minimum required information to decode the class from its init;
    • Option for including outputs or not was added.
  • (Optional) Expand implementation to AirBrakes + Controllers and new classes.

Some important remarks:

  • The simple getting started simulation is essentially identical comparing the store & load, the only difference is the random lag and noise from Parachute class. I could not found a simple solution to reset those and always get the same results.

@phmbressan phmbressan added the Enhancement New feature or request, including adjustments in current codes label Aug 30, 2024
@phmbressan phmbressan added this to the Release v1.X.0 milestone Aug 30, 2024
@phmbressan phmbressan self-assigned this Aug 30, 2024
@phmbressan phmbressan requested a review from a team as a code owner August 30, 2024 21:32
Copy link

codecov bot commented Aug 30, 2024

Codecov Report

Attention: Patch coverage is 82.29167% with 68 lines in your changes missing coverage. Please review.

Project coverage is 76.46%. Comparing base (a6a0f74) to head (8c0bb4c).
Report is 13 commits behind head on develop.

Files with missing lines Patch % Lines
rocketpy/environment/environment.py 66.66% 17 Missing ⚠️
rocketpy/_encoders.py 79.62% 11 Missing ⚠️
rocketpy/motors/tank.py 80.48% 8 Missing ⚠️
...ocketpy/rocket/aero_surface/fins/free_form_fins.py 33.33% 6 Missing ⚠️
rocketpy/motors/tank_geometry.py 83.87% 5 Missing ⚠️
...cketpy/rocket/aero_surface/fins/elliptical_fins.py 37.50% 5 Missing ⚠️
rocketpy/rocket/components.py 37.50% 5 Missing ⚠️
rocketpy/simulation/flight.py 37.50% 5 Missing ⚠️
rocketpy/mathutils/vector_matrix.py 66.66% 3 Missing ⚠️
rocketpy/rocket/parachute.py 90.47% 2 Missing ⚠️
... and 1 more
Additional details and impacted files
@@             Coverage Diff             @@
##           develop     #679      +/-   ##
===========================================
+ Coverage    75.95%   76.46%   +0.51%     
===========================================
  Files           99       95       -4     
  Lines        11237    11359     +122     
===========================================
+ Hits          8535     8686     +151     
+ Misses        2702     2673      -29     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Copy link
Member

@Gui-FernandesBR Gui-FernandesBR left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nothing to complain about, good work.

requirements.txt Show resolved Hide resolved
}

@classmethod
def from_dict(cls, func_dict):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I`d like to see an example of you loading a class from the dict.

rocketpy/rocket/components.py Outdated Show resolved Hide resolved
@phmbressan
Copy link
Collaborator Author

I have just noticed that I made a mistake when merging CHANGELOG conflicts for this PR.

I will fix it before merging. This does not block reviewing.

  • Fix CHANGELOG.md merging errors.

Comment on lines +118 to +129
"""Returns the class by importing its signature.

Parameters
----------
signature : str
Signature of the class.

Returns
-------
type
Class defined by the signature.
"""
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still cannot know what is a "class signature", is it the signature of the init class?

what do you have in the "signature" dictionary (which bte the docs says it is a str) exactly?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right, I changed what is a signature a few times during development. I will improve the docs.

@@ -60,3 +60,10 @@ def __str__(self):
"""

return f"Fluid: {self.name}"

def to_dict(self, _):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I usually see this behavior in other packages, what do you think about it? I dont really understand the reasons of that tho.

Suggested change
def to_dict(self, _):
def to_dict(self, *):

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, actually I think I will simply revert to having the parameter include_outputs in all of them, and disable the warning if the method does not use it. That brings better standardization, since the call to_dict(include_outputs=...) would always work.

The ** would be a better option than I used here, since it allows for the same interface as I mentioned. However, it also allows for other parameter names, and changing it in the future would be a breaking change.

Copy link
Member

@MateusStano MateusStano left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall comments:

  • to_dict and from_dict methods are missing docstrings in all classes

Comment on lines +3367 to +3435
def to_dict(self, include_outputs=True):
data = {
"rocket": self.rocket,
"env": self.env,
"rail_length": self.rail_length,
"inclination": self.inclination,
"heading": self.heading,
"initial_solution": self.initial_solution,
"terminate_on_apogee": self.terminate_on_apogee,
"max_time": self.max_time,
"max_time_step": self.max_time_step,
"min_time_step": self.min_time_step,
"rtol": self.rtol,
"atol": self.atol,
"time_overshoot": self.time_overshoot,
"name": self.name,
"equations_of_motion": self.equations_of_motion,
}

if include_outputs:
data.update(
{
"time": self.time,
"out_of_rail_time": self.out_of_rail_time,
"out_of_rail_velocity": self.out_of_rail_velocity,
"out_of_rail_state": self.out_of_rail_state,
"apogee": self.apogee,
"apogee_time": self.apogee_time,
"apogee_x": self.apogee_x,
"apogee_y": self.apogee_y,
"apogee_state": self.apogee_state,
"x_impact": self.x_impact,
"y_impact": self.y_impact,
"impact_velocity": self.impact_velocity,
"impact_state": self.impact_state,
"x": self.x,
"y": self.y,
"z": self.z,
"vx": self.vx,
"vy": self.vy,
"vz": self.vz,
"e0": self.e0,
"e1": self.e1,
"e2": self.e2,
"e3": self.e3,
"w1": self.w1,
"w2": self.w2,
"w3": self.w3,
"ax": self.ax,
"ay": self.ay,
"az": self.az,
"alpha1": self.alpha1,
"alpha2": self.alpha2,
"alpha3": self.alpha3,
"altitude": self.altitude,
"mach_number": self.mach_number,
"stream_velocity_x": self.stream_velocity_x,
"stream_velocity_y": self.stream_velocity_y,
"stream_velocity_z": self.stream_velocity_z,
"free_stream_speed": self.free_stream_speed,
"angle_of_attack": self.angle_of_attack,
"static_margin": self.static_margin,
"stability_margin": self.stability_margin,
"latitude": self.latitude,
"longitude": self.longitude,
}
)

return data
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If these are just the __init__ arguments why not just use `inspect.signature(Flight.init).parameters to construct the data dict?

Also, I am pretty sure that all attributes/cached_properties can be accessed using dir() or something similar...

I think some of the to_dict and from_dict can be generalized and might not need to be defined in every class. This reduces maintenance

@@ -373,6 +400,7 @@ def __init__(self, radius, height, spherical_caps=False, geometry_dict=None):
"""
geometry_dict = geometry_dict or {}
super().__init__(geometry_dict)
self.__input_radius = radius
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't have support for tanks in Monte Carlo yet, but in Stochastic classes I believe that we require the input arguments of the __init__ method to be saved in the class as an attribute with the same name, e.g.:

def __init__(self, radius):
    self.radius=radius

is there a way to avoid creating self.__input_radius?

Comment on lines +16 to +18
def __init__(self, *args, **kwargs):
self.include_outputs = kwargs.pop("include_outputs", True)
super().__init__(*args, **kwargs)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like making the default value of include_outputs as False will be much more useful. There will be more use cases for when it is false

@Gui-FernandesBR
Copy link
Member

Overall comments:

  • to_dict and from_dict methods are missing docstrings in all classes

I strongly advise to not include dosctrings yet.
We don't really know if these methods will really be used.

Also, they exists mainly for private reasons: we need them in order have the JSONEncoder working.

Btw the methods are so simple that they don't even need docstrings. But that's a discussion for future sessions...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Enhancement New feature or request, including adjustments in current codes
Projects
Status: Next Version
Development

Successfully merging this pull request may close these issues.

3 participants