Stream connectors: `positivemax` drops `sq`, so outer-connector stream mixing is unweighted · Issue #5218 · SciML/ModelingToolkit.jl · GitHub
Skip to content

Stream connectors: positivemax drops sq, so outer-connector stream mixing is unweighted #5218

Open

Description

@ChrisRackauckas-Claude

positivemax(m, sq; tol) = _positivemax(m, tol) discards sq and passes tol (1e-10 from expand_connections) as the regularisation scale si. The generated equations for an outer connector's stream variable therefore use weights that saturate almost immediately. For any real flow, both weights are effectively equal, so the "flow-weighted" mix is an unweighted average.

Reproducer (ModelingToolkit + SciMLBase only)

# positivemax(m, sq; tol) drops sq and passes expand_connections' tol (1e-10)
# as si. Outer-connector stream mixing is then an unweighted average.
using InteractiveUtils
using ModelingToolkit
using ModelingToolkit: t_nounits as t, D_nounits as D
using SciMLBase

@connector function SPort(; name)
    @variables p(t) m(t) [connect = Flow] h(t) [connect = Stream]
    System(Equation[], t, [p, m, h], []; name)
end

function Src(; name, rate, h0)
    @named port = SPort()
    @variables x(t) = h0
    System([port.m ~ -rate, port.h ~ x, D(x) ~ 0], t; name, systems = [port])
end

function Recv(; name)
    @named port = SPort()
    @variables z(t) = 0.0
    System([port.p ~ 1.0, port.h ~ z, D(z) ~ instream(port.h) - z], t;
           name, systems = [port])
end

# Two inner sources with unequal flows; one outer port. The generated
# outer-stream equation is gp.h ~ mix(s1.h, s2.h).
function Group(; name)
    @named gp = SPort()
    @named s1 = Src(rate = 1.0, h0 = 2.0)
    @named s2 = Src(rate = 2.0, h0 = 8.0)
    System([connect(gp, s1.port, s2.port)], t; name, systems = [gp, s1, s2])
end

@named g = Group()
@named c = Recv()
sys = mtkcompile(System([connect(g.gp, c.port)], t; name = :hier, systems = [g, c]))
prob = ODEProblem(sys, [], (0.0, 1.0))

got = SciMLBase.getsym(prob, sys.g.gp.h)(prob)
# Modelica §15.2 outer connection equation (si = 1+2 = 3 ≫ 1e-4):
# gp.h = (1*2 + 2*8) / (1+2) = 18/3 = 6
expected = 6.0
# Bug: _positivemax(m, 1e-10) ≈ 1e-4 for every positive m, so
# gp.h ≈ (2+8)/2 = 5 (unweighted).

println("bug2: positivemax drops sq; outer stream mix is unweighted")
println("ModelingToolkit ", pkgversion(ModelingToolkit))
println("observed gp.h equation:")
for eq in observed(sys)
    s = string(eq)
    if occursin("gp", s) && occursin("h", s) && occursin("positivemax", s)
        println("  ", s)
    end
end
println("got      g.gp.h = ", got)
println("expected g.gp.h = ", expected, "  (flow-weighted  (1*2 + 2*8)/3 )")
println("unweighted average would be ", (2.0 + 8.0) / 2)
versioninfo()

Output (ModelingToolkit 11.45.1, rerun in a fresh process)

bug2: positivemax drops sq; outer stream mix is unweighted
ModelingToolkit 11.45.1
observed gp.h equation:
  g₊gp₊h(t) ~ (ModelingToolkitBase._positivemax(-g₊s1₊port₊m(t), 1.0e-10)*g₊s1₊x(t) + ModelingToolkitBase._positivemax(-g₊s2₊port₊m(t), 1.0e-10)*g₊s2₊x(t)) / (ModelingToolkitBase._positivemax(-g₊s1₊port₊m(t), 1.0e-10) + ModelingToolkitBase._positivemax(-g₊s2₊port₊m(t), 1.0e-10))
got      g.gp.h = 5.000000044999968
expected g.gp.h = 6.0  (flow-weighted  (1*2 + 2*8)/3 )
unweighted average would be 5.0
Julia Version 1.12.4

Two sources with flows 1 and 2 and stream values 2 and 8 should mix to the flow-weighted value (1·2 + 2·8)/3 = 6.0. The generated equation gives 5.0, the unweighted average. The expected value follows directly from the Modelica stream-connector semantics (§15.2): outflowing connectors are weighted by their flow.


🤖 Filed by an AI agent (Claude Code, model claude-opus-5-5) on behalf of Chris Rackauckas. The reproducer was prepared by Cursor Agent (auto) and independently rerun by the filing agent. Found during an Opus 5.5 review of #5213. https://claude.ai/code/session_01VJtEvCkHe9XafoMbYvR9No

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions