About @connectors

You can always put @macroexpand before a macro call in the REPL to see exactly what it’s doing. Here is the result of that on for the Pin case from the MTK docs (I deleted line number comments to make it more readable):

julia> @macroexpand @connector function Pin(;name)
           sts = @variables v(t)=1.0 i(t)=1.0
           ODESystem(Equation[], t, sts, []; name=name)
       end
quote
    struct Pin
        var"##275"->begin
                1
            end
    end
    function Pin(; name)
        function f()
            begin
                sts = begin
                        v = (identity)((Symbolics.wrap)((Symbolics.setdefaultval)(((Sym){(SymbolicUtils.FnType){NTuple{1, Any}, Real}}(:v))((Symbolics.value)(t)), begin
                                            1.0
                                        end)))
                        i = (identity)((Symbolics.wrap)((Symbolics.setdefaultval)(((Sym){(SymbolicUtils.FnType){NTuple{1, Any}, Real}}(:i))((Symbolics.value)(t)), begin
                                            1.0
                                        end)))
                        [v, i]
                    end
                ODESystem(Equation[], t, sts, []; name = name)
            end
        end
        res = f()
        if (isdefined)(res, :connection_type)
            var"#131#lens" = (identity)((Setfield.compose)((Setfield.PropertyLens){:connection_type}()))
            res = (Setfield.set)(res, var"#131#lens", Pin)
        else
            res
        end
    end
end

So it’s creating a struct called Pin, doing some fancy trickery to delete it’s default constructor (I think?), then creating a Pin method that calls your code and then sets the connection_type of the output to the type Pin.

This can be especially useful if your model has different types of connections. Let’s say it has electrical Pins as defined above, but also rotational mechanical Flanges that are basically the same, but with angular velocity instead of current and torque instead of voltage. This might look like:

@connector function Flange(;name)
     sts = @variables τ(t)=1.0 ω(t)=1.0
     ODESystem(Equation[], t, sts, []; name=name)
end 

Now you can write pretty generic code that takes advantage of circuit analogies for other domains. For example, you can have generic functions to get the flow and effort variables from a connector as:

flow_variable(connector) = flow_variable(ModelingToolkit.get_connection_type(connector), connector)
flow_variable(::Type{Pin}, connector) = connector.i
flow_variable(::Type{Flange}, connector) = connector.ω

effort_variable(connector) = effort_variable(ModelingToolkit.get_connection_type(connector), connector)
effort_variable(::Type{Pin}, connector) = connector.v
effort_variable(::Type{Flange}, connector) = connector.τ 
4 Likes