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

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

    Fields

    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