AlexS12 / FlightMechanics.jl

Flight mechanics utils
Other
35 stars 11 forks source link

Separation of static parameters and dynamic state #71

Open zsunberg opened 4 years ago

zsunberg commented 4 years ago

From #65:

Well, the aircraft type is still messy. It is used to dispatch on different methods depending on the aircraft type itself and also, it is composed of other types that store current value variables and are also used for dispatch in some cases:

  • total forces and moments
    • the flight control system: I would like to implement autopilots, stability augmentation systems... here in the future. It also stores control values and aerodynamic surfaces deflections.
  • Propulsion system
  • Aerodynamics

One important thing to think about is separation of static parameters and dynamic state. In my opinion, every dynamic state variable (e.g. actuator position for an actuator with lag, FCS memory, fuel, flow separation, dynamic stall) should be stored in a state that is separate from the static configuration parameters of the aircraft.

Some big advantages of this are: 1) Everything can be immutable, resulting in more efficient, understandable, and differentiable code. 2) There is less redundancy/confusion in storage - you only need to store the static parameters once and the state at every timestep. 3) I think this will make solving the ODEs to propagate dynamics more natural.

Let me know what you think of this and we can discuss details about design patterns, modularity, and scaling from very simple aircraft to very complex ones.

AlexS12 commented 4 years ago

Yes, I think that using the facto of being static (configuration) or variable (state) to separate properties in different types is a good criteria.

Just to store it here when this revision is done, I will add that, in some cases, I tried to use methods to get some of the static properties. For example:

https://github.com/AlexS12/FlightMechanics.jl/blob/master/src/aircrafts/aircrafts/F16.jl#L722

I based that on what I read in this blog post (http://www.stochasticlifestyle.com/type-dispatch-design-post-object-oriented-programming-julia/ - Section: Small Functions and Constant Propagation) The advantages are described in the post. Moreover, some properties that are static in one case, may be not constant in other models (ie. weight might be considered constant for a short maneuver, but needs to be variable to study the aircraft performances). I think this approach is more favourable for these cases than creating a lot of "configuration types". The bad part is: the code may be longer and more difficult to follow in some cases and it requires a lot of type specialization, which might be ok for the particular aircraft implementations, but is maybe more problematic for more reusable components.

I will try to sketch a refactor of the aircraft components following your advice in this issue! :smiley:

zsunberg commented 4 years ago

Cool - yes, using methods to get properties is a good pattern! In general, to promote proper encapsulation, I make it a goal to write code so that functions access the fields of at-most one of their arguments. (but typically people leave off the get_ in Julia)

Let me suggest a pattern that goes even further in this direction: most accessors should be functions of both the aircraft and the state, e.g. get_wing_span(ac, x) where ac is an aircraft object and x is the dynamic state.

Suppose you want to model an F16 and an F14, but the F-14 has variable geometry wings.

# by default, the state of an aircraft is the 6DOF state and derivatives
state_type(::Type{<:Aircraft}) = SVector{12, Float64}

struct SimpleF16 <: Aircraft end
get_wing_span(::SimpleF16, x) = 30.*FT2M

struct SimpleF14 <: Aircraft end

# in addition to the 6DOF state and derivatives, the F14 has a dynamic sweep angle state variable
state_type(::Type{SimpleF14}) = SVector{13, Float64}

get_wing_span(::SimpleF14, x) = 5.0 + 2*8.0*cos(x[13])

Note that there are no mutable types involved. Other vehicles might have additional state variables like fuel, weapons, or actuator positions. The state type does not necessarily have to be a vector.

There are a lot of things to consider when designing interfaces like this that allow people to easily extend the code at many levels and can be used for models ranging from very simple to very complex. Perhaps it would be useful to open a "user stories" issue or create a series of issues with a "user story" tag (since I see you like issue tags :wink:). That will make it easier to point to reasons for making design decisions that are good for all the ways people might want to use the package.

There is of course a great deal more to discuss here, but I will let you respond before trying to guess what your questions might be :)