Question
Based on the examples in the ModelingToolkit documentation I’m trying to expand a bit and model the impact behavior between two masses, one being connected to a base through a spring.
To make it simple, an example with only two masses colliding yields expected results, but as soon as I add the spring, this leads to a non-conservative behavior.
The only obvious thing I can think about is that the continuous event is only acting at the very specific gap == 0 condition while it should act on the gap <= 0 inequality condition.
What would be the proper way to account for these inequality conditions ?
Side uncertain note: this seemed to behave as expected in previous versions but I can’t remember which.
Environment
[e9467ef8] GLMakie v0.13.13
[961ee093] ModelingToolkit v11.35.2
[16a59e39] ModelingToolkitStandardLibrary v2.29.5
[1dea7af3] OrdinaryDiffEq v7.1.2
on julia 1.12.6 windows.
Two masses without spring
using ModelingToolkit
import ModelingToolkit: connect, t_nounits as t, D_nounits as D
using ModelingToolkitStandardLibrary
using GLMakie
using OrdinaryDiffEq
@named mass1 = ModelingToolkitStandardLibrary.Mechanical.TranslationalPosition.Mass(m=1.)
@named mass2 = ModelingToolkitStandardLibrary.Mechanical.TranslationalPosition.Mass(m=0.5)
@named fix = ModelingToolkitStandardLibrary.Mechanical.TranslationalPosition.Fixed(s_0=0.)
@named spring = ModelingToolkitStandardLibrary.Mechanical.TranslationalPosition.Spring(k=10., l=1.)
@variables gap(t) p(t) E(t) l(t)
@parameters e_newton = 1.
@mtkcompile twomass = System(
[
gap ~ mass2.s - mass1.s,
p ~ mass1.v * mass1.m + mass2.v * mass2.m,
E ~ 1/2 * mass1.m * mass1.v^2 + 1/2 * mass2.m * mass2.v^2,
],
t;
systems=[mass1, mass2],
continuous_events=[gap ~ 0] => [
mass1.v ~ ((mass1.m - 1 * mass2.m) * Pre(mass1.v) + (1 + 1) * mass2.m * Pre(mass2.v)) / (mass1.m + mass2.m)
mass2.v ~ ((mass2.m - 1 * mass1.m) * Pre(mass2.v) + (1 + 1) * mass1.m * Pre(mass1.v)) / (mass1.m + mass2.m)
]
)
u0 = Dict{Num,Any}([
mass1.s => 1.,
mass1.v => 0.,
mass2.s => 2.,
mass2.v => -1.,
]
)
prob = ODEProblem(twomass, u0, (0.0, 10.0), fully_determined=true)
sol = solve(prob, Tsit5())
begin
f = Figure()
ax = Axis(f[1, 1])
plot!(sol, idxs=[twomass.mass1.s, twomass.mass2.s, twomass.gap])
axislegend(ax)
ax = Axis(f[2, 1])
plot!(sol, idxs=[twomass.mass1.v, twomass.mass2.v])
axislegend(ax)
ax = Axis(f[3, 1])
plot!(sol, idxs=[twomass.E])
axislegend(ax)
f
end
which gives
Two masses with spring
@mtkcompile twomassspring = System(
[
gap ~ mass2.s - mass1.s,
p ~ mass1.v * mass1.m + mass2.v * mass2.m,
l ~ spring.flange_a.s - spring.flange_b.s,
E ~ 1/2 * mass1.m * mass1.v^2 + 1/2 * mass2.m * mass2.v^2 + 1/2 * spring.k * (l - spring.l)^2,
connect(fix.flange, spring.flange_b),
connect(mass1.flange, spring.flange_a),
],
t;
systems=[mass1, mass2, fix, spring],
continuous_events=[gap ~ 0] => [
mass1.v ~ ((mass1.m - 1 * mass2.m) * Pre(mass1.v) + (1 + 1) * mass2.m * Pre(mass2.v)) / (mass1.m + mass2.m)
mass2.v ~ ((mass2.m - 1 * mass1.m) * Pre(mass2.v) + (1 + 1) * mass1.m * Pre(mass1.v)) / (mass1.m + mass2.m)
]
)
u0 = Dict{Num,Any}([
mass1.s => 1.,
mass1.v => 0.,
mass2.s => 2.,
mass2.v => -1.,
]
)
prob = ODEProblem(twomassspring, u0, (0.0, 10.0), fully_determined=true)
sol = solve(prob, Tsit5())
begin
f = Figure()
ax = Axis(f[1, 1])
plot!(sol, idxs=[twomassspring.mass1.s, twomassspring.mass2.s, twomassspring.gap, twomassspring.l])
axislegend(ax)
ax = Axis(f[2, 1])
plot!(sol, idxs=[twomassspring.mass1.v, twomassspring.mass2.v])
axislegend(ax)
ax = Axis(f[3, 1])
plot!(sol, idxs=[twomassspring.E])
axislegend(ax)
f
end
which yields
Obviously, the energy conservation is not respected, however events are properly catched.

