Julian way of writing a constructor

I have the following code:

using StaticArrays

# Type definitions
const MyFloat = Float32
const Vec3    = MVector{3, MyFloat}

mutable struct State
    v_wind::Vec3        # wind vector at the height of the kite
    v_wind_gnd::Vec3    # wind vector at reference height
    v_wind_tether::Vec3
    v_apparent::Vec3
    drag_force::Vec3
    lift_force::Vec3
    steering_force::Vec3
    last_force::Vec3
    spring_force::Vec3
    kite_y::Vec3
    segment::Vec3
    seg_area::MyFloat   # area of one tether segment
    c_spring::MyFloat
    length::MyFloat
    damping::MyFloat
    area::MyFloat
    param_cl::MyFloat
    param_cd::MyFloat
    v_app_norm::MyFloat
    cor_steering::MyFloat
    psi::MyFloat
    beta::MyFloat
end

function init()
    state = State(zeros(3), zeros(3), zeros(3), zeros(3), zeros(3), zeros(3), zeros(3), zeros(3), zeros(3), zeros(3), zeros(3), 0.0, 0.0, 0.0, 0.0, 0.0,  0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
    state.v_wind[1]        = 8 # westwind, downwind direction to the east
    state.v_wind_gnd[1]    = 8 # westwind, downwind direction to the east
    state.v_wind_tether[1] = 8 # wind at half of the height of the kite
    state.v_apparent       = [1.0, 2.0, 3.0]
    state.param_cl         = 0.2
    state.param_cd         = 1.0
    state
end
const state = init()

How can this be written in a more Julian way?

Is there a way to initialize the struct with zeros without having to write all fields by hand?

Is there a better way to write a constructor?

Maybe something like this:

using Parameters

@with_kw mutable struct State{S, T}
    v_wind::T = zero(T)       # wind vector at the height of the kite
    v_wind_gnd::T = zero(T)    # wind vector at reference height
    v_wind_tether::T = zero(T)
    v_apparent::T = zero(T)
    drag_force::T = zero(T)
    lift_force::T = zero(T)
    steering_force::T = zero(T)
    last_force::T = zero(T)
    spring_force::T = zero(T)
    kite_y::T = zero(T)
    segment::T = zero(T)
    seg_area::S = zero(S)   # area of one tether segment
    c_spring::S = zero(S)
    length::S = zero(S)
    damping::S = zero(S)
    area::S = zero(S)
    param_cl::S = zero(S)
    param_cd::S = zero(S)
    v_app_norm::S = zero(S)
    cor_steering::S = zero(S)
    psi::S = zero(S)
    beta::S = zero(S)
end

s = State{Float32, MVector{3,Float32}}()

I would parameterize the struct instead of forcing a specific precision and vector type.

5 Likes

Yes, this is nice! :slight_smile:

Thank you!

Instead of Parameters.jl, you can also use Base.@kwdef.

As a side note, your MVectors are still mutable even if the State as a whole is not mutable. Thus, if the scalars do not have to be mutable (or if only a a few of them have), you may better choose to use an immutable struct.

(mutating a scalar in an overall immutable struct requires some strategy of the ones described here)

Also, Parameters offers the @unpack macro, which can be used with, for example:

@unpack v_wind, area = s

where s is of type State, in a function that you might be using only these two variables among all those contained in the struct.