Skip to content

state_priority ineffective when the prioritized variable's derivative is matched to a differentiated alias equation (singleton SCC forces demotion; singular Cartesian states in a planar arm) #101

@baggepinnen

Description

@baggepinnen

state_priority is ineffective when the prioritized variable's derivative is matched to a differentiated alias equation: singleton SCC forces demotion, yielding singular Cartesian states in a trivial planar arm

Summary

In a planar 2-link serial arm, joint angles/velocities are marked state_priority = 10
(raising to 100 changes nothing) and body Cartesian variables state_priority = 1–2, yet
dummy-derivative selection picks the Cartesian body coordinates as differential states
and leaves one algebraic equation that solves for a joint angle:

0 ~ -(robot₊link1₊r0(t))[2] + robot₊link1₊r[1]*sin(robot₊revolute2₊frame_a₊phi(t)) + robot₊link1₊r[2]*cos(robot₊revolute2₊frame_a₊phi(t))

With r = [1, 0], the Jacobian of this equation w.r.t. its unknown is cos(phi)
singular at phi = π/2, which is exactly the target configuration of the example's
point-to-point motion. Every solver dies with dt → eps (retcode = Unstable) as the arm
approaches the target; better-tuned controllers fail more reliably because they approach
the singular configuration more exactly. With joint coordinates as states (minimal
coordinates, what Dymola selects for this model) the system is a causal ODE with no
algebraic equation and no singularity.

What was ruled out

Traced with MTK v11.26.8 (ModelingToolkitTearing v1.13.5) / StateSelection v1.9.3:

  1. Metadata propagation is correct. On the flattened system: revolute*.phi/w
    priority 10, body.r/v priority 1, body.phi/w priority 2 (array element metadata
    resolves through getindex correctly).
  2. Alias elimination is correct. After eliminate_perfect_aliases! +
    alias_elimination!, the prioritized variables survive with correct entries in
    structure.state_priorities (pick_alias_target respects priority; the joint angle
    sits in a 3-variable relation frame_a.phi + phi ~ frame_b.phi and is not merged).
  3. The Jacobian/col_order path is not exercised. A separate latent issue was found in
    dummy_derivative_graph! (src/partial_state_selection.jl): when state_priority is
    given, vars is priority-sorted but a previously-computed integer Jacobian J is not
    column-permuted, so vars[col_order[i]] indexes mismatched orders. In this model,
    however, the kinematic-loop SCC has a trigonometric (non-integer) Jacobian, so jac
    returns nothing and the matching path runs (verified by instrumenting the jac
    closure: (neqs, nvars, integer) = (11, 13, false)). The matching path respects the
    sort within an SCC.

Root cause

dummy_derivative_graph! consults state_priority only within each SCC of
find_var_sccs(graph, var_eq_matching), and the SCC partition comes from the
priority-blind Pantelides matching. Dumping the partition (via log = Val(true) /
DummyDerivativeSummary) for this model:

  • The kinematic-loop SCC (13 candidates, 11 differentiated equations) contains only
    frame/body variables (priorities 0–2). 11 are demoted; the survivors are necessarily
    body Cartesian coordinates.

  • Each joint-coordinate derivative sits in its own singleton SCC, matched to a
    twice-differentiated alias/connection equation, e.g.

    SCC #152: D²(robot₊revolute1₊phi)  matched to  0 ~ D²(robot₊revolute1₊phi) - D²(robot₊revolute2₊frame_a₊phi)
    SCC #153: D²(robot₊revolute2₊phi)  matched to  0 ~ -D²(robot₊revolute1₊phi) - D²(robot₊revolute2₊phi) + D²(robot₊body₊phi)
    SCC #156: D(robot₊revolute2₊w)     matched to  0 ~ -D(robot₊revolute2₊w) + D²(robot₊revolute2₊phi)
    SCC #159: D(robot₊revolute1₊w)     matched to  0 ~ D²(robot₊revolute1₊phi) - D(robot₊revolute1₊w)
    

    A singleton SCC with one differentiated equation demotes its only candidate
    unconditionally — priority is never compared against anything. This is why raising the
    priority from 10 to 100 has zero effect.

The matching is not unique: matching 0 ~ D²(rev1.phi) - D²(rev2.frame_a.phi) to
D²(rev2.frame_a.phi) instead would pull rev1.phi into the loop SCC, where the priority
sort would (correctly) keep it as a state and demote the Cartesian candidates. The outcome
is thus legal w.r.t. the dummy-derivative algorithm for some matching, but the
state_priority feature is silently ineffective for the very common pattern where the
prioritized variable is connected to a kinematic loop through a rigid alias chain
(joint → flange → frame connections — i.e., essentially every multibody joint).

Reproduction

Model: TwoJointPlanarRobotPID / TwoJointPlanarArm on the twodof branch of
JuliaComputing/MultibodyComponents.jl (world → revolute1 → link1 → revolute2 → link2 →
body; Revolute.phi/w have state_priority = 10 by default, Body.r/v 1 and
Body.phi/w 2).

using ModelingToolkit, MultibodyComponents
using MultibodyComponents: multibody
@named model = MultibodyComponents.PlanarMechanics.examples.TwoJointPlanarRobotPID()
ssys = multibody(model)   # mtkcompile with multibody-suitable options
unknowns(ssys)            # body.phi, body.w, body.r[2], body.v[2] differential;
                          # revolute2.frame_a.phi algebraic (singular at phi = π/2)

Instrumentation used to localize the issue (priority traces, DummyDerivativeSummary
dump, matched-equation dump per SCC) can be provided.

Workarounds attempted

  • statePriority = 100 on the joints: no effect (mechanism above).
  • First-order filter between the subsystems whose coupling was initially suspected:
    no effect on state selection (the selection is a property of the arm kinematics alone).
  • Only effective mitigation so far: use FBDF and avoid resting exactly at the singular
    configuration — clearly not a fix.

Possible directions

  1. Make the matching/SCC condensation priority-aware: when a differentiated 2-variable
    alias equation can be matched to either end, prefer matching it to the lower-priority
    variable so high-priority variables remain selectable.
  2. Post-pass: for singleton SCCs created by differentiated alias equations, allow swapping
    the demoted variable with its alias partner when the partner has lower priority.
  3. At minimum, document/warn that state_priority cannot influence selection in this
    situation (it currently fails silently).

Related (checked, distinct): #96 (priority-aware
algebraic tearing), SciML/ModelingToolkit.jl#3093 (alias-aware priority propagation,
pre-StateSelection-split prototype), StateSelection.jl#74/#75 (merged, present in v1.9.3).
The unpermuted-Jacobian/col_order mismatch described above is a separate latent bug and
can be filed separately if useful.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions