MTK & library components - good practice?

I’m curious: what is good practice with MTK components which uses Stream variables/has multiple substances?

  • How to ensure that the various components are created/instantiated with equal number of substances, so that I avoid connecting components that are not compatible?

Let me be concrete. I have the following connector using Ns – the number of substances (not meant to be professional; I am trying to learn things):

@connector function FluidPort(; name, Ns=1)
    vars = @variables begin 
        p(t),           [guess = 0.0, description="Pressure, Pa"]
        md(t),          [connect = Flow, guess = 0.0, description="Mass flow, kg/s"]
        x(t)[1:Ns],     [connect = Stream, guess = 1/Ns, description="Mass fractions, -"]
    end
    System(Equation[], t, vars, []; name = name)
end

Next, consider two example components: first a “pump” where I need to specify the mass fraction composition (x_set). Since this must contain the number of substances, it feels redundant to also pass the number of substances Ns.

@component function PumpFixed(; name, md_set=1.0, x_set=[1])
    Ns = length(x_set)
    @named a = FluidPort(Ns=Ns)

    pars = @parameters begin
        md = md_set,    [unit=u"kg/s", description = "Specified mass flow rate"]
        x[1:Ns] = x_set,[unit = u"kg/kg", description = "Specified mass fractions"]
    end

    eqs = [
        # Inflow to system means outflow from component (negative md)
        a.md ~ -md,
        # Define the stream variables for the port
        a.x ~ x
    ]

    compose(System(eqs, t, [], pars; name = name), a)
end

Next, a component that does not need the mass fractions (they are not used):

@component function Plug(; name, Ns=1)
    @named a = FluidPort(Ns=Ns)
    # Hardcoded defaults are fine because md=0 makes x irrelevant to the system
    pars = @parameters begin
        x[1:Ns] = ones(Ns) ./ Ns
    end
    eqs = [
        a.md ~ 0.0, # Zero flow: mathematically plugs the port
        a.x ~ x,  # Satisfies the stream contract
    ]
    compose(System(eqs, t, [], pars; name = name), a)
end

I have several other components, some where I specify Ns in the creation process, others where I specify the mass fraction composition vector (x_set).

My question is whether this is an OK practice, or whether there is a better practice. The way I do it, it is up to the user to make sure that the various components that are created and connected, are compatible. Which leads to some possibility of buggy system creation.