Why is ModelingToolkit simplifying this way?

I created a pretty simple composed system:

using OrdinaryDiffEq, ModelingToolkit, Plots

@parameters t
d_dt = Differential(t)

function Cart(; init_pos, init_vel, mass, name=:cart)
	@variables pos(t)=init_pos vel(t)=init_vel
	@variables f(t)
	@parameters mass=mass

	eqs = [
		d_dt(pos) ~ vel
		d_dt(vel) ~ f / mass
	]

	return ODESystem(eqs; name)
end

function PDController(; kp=0, kd=0, name=:controller)
	@variables x(t) v(t) f(t)
	@parameters kp=kp kd=kd

	eqs = [
		f ~ -kp*x - kd*v
	]

	return ODESystem(eqs; name)
end

function ControlledCart(; cart, controller, name=:sys)
	 eqs = [
	 	cart.pos ~ controller.x
	 	cart.vel ~ controller.v
		cart.f ~ controller.f
	]
	return ODESystem(eqs; name, systems=[cart, controller])
end

cart = Cart(init_pos=0.0, init_vel=1.0, mass=0.5)
controller = PDController(kp=1.0, kd=0.5)
controlled_cart = ControlledCart(; cart, controller)

When I go to structural_simplify it with

sys = controlled_cart |> structural_simplify

it does it like so:

image
which, I guess is technically true. Trying to build the ODEProblem gives the error

ArgumentError: SymbolicUtils.BasicSymbolic{Real}[controllerâ‚Šv(t)] are missing from the variable map.

which makes sense because I purposely didn’t set a default value for controller.v because it was supposed to just be equal to cart.vel, which does has a default. I could, of course, give controller.v a default for this simple example, but if this was a larger more complicated sim, I wouldn’t want to be wondering which version of two defaults it was going to choose.

FWIW, writing the Controller and ControlledCart equations with a 0 on the LHS, not structural_simplifying it, giving default values to everything, and then solving it with a DAE solver does work as expected.

I do not know the best practice, having never worked with ModelingToolkit, but as far as I understand, it will symbolically change your equations such that they are optimized for both performance and, perhaps more importantly, numerical stability.
At the very least, to me this definitely sounds like it might be worth the effort of having a bit of redundancy in defining your initial conditions.

Yeah this is a piece that’s still missing. There’s already issues open on improving the initializations. For now you just need to make sure to follow the error message and define the right initializations.

1 Like

Okay, cool. Thanks for the response. Is this the best place to track it?

In one of our real sims, there might be dozens to hundreds of submodels nested pretty deeply in the system all written by different people, so it’s a little harder to keep track of which things should be set for initialization. We could add a default for controller.v, but that just turns our nice helpful error into a silent unexpected value. cart.vel is the “real” state here and will be the one that’s user-facing as an initial condition. If they specify a value for it and a controller.v default supersedes it, that would be a really tricky issue to debug.

1 Like

How to prevent breakage from changes to `structural_simplify` · Issue #2000 · SciML/ModelingToolkit.jl · GitHub is. The one you link is related but isn’t a full solution.

1 Like