-
-
Notifications
You must be signed in to change notification settings - Fork 209
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Improve interactive system browsing and add system description #3132
Conversation
For example, consider a simplified example of the big RC circuit tutorial: using ModelingToolkit
using ModelingToolkit: t_nounits as t, D_nounits as D
# Basic electrical components
@connector function Pin(; name)
@variables v(t) i(t) [connect = Flow]
description = "Pin"
ODESystem(Equation[], t, [v, i], []; name, description)
end
function Ground(; name)
@named g = Pin()
eqs = [g.v ~ 0]
description = "Ground connection"
compose(ODESystem(eqs, t, [], []; name, description), g)
end
function ConstantVoltage(; name)
@named p = Pin()
@named n = Pin()
pars = @parameters V
eqs = [V ~ p.v - n.v
0 ~ p.i + n.i]
description = "Constant voltage source"
compose(ODESystem(eqs, t, [], pars; name, description), p, n)
end
@connector function HeatPort(; name)
vars = @variables T(t) Qflow(t) [connect = Flow]
description = "Heat port"
ODESystem(Equation[], t, vars, []; name, description)
end
function HeatingResistor(; name)
@named p = Pin()
@named n = Pin()
@named h = HeatPort()
vars = @variables v(t) RTherm(t)
pars = @parameters R TAmbient α
eqs = [RTherm ~ R * (1 + α * (h.T - TAmbient))
v ~ p.i * RTherm
h.Qflow ~ -v * p.i
v ~ p.v - n.v
0 ~ p.i + n.i]
description = "Heating resistor"
compose(ODESystem(eqs, t, vars, pars; name, description), p, n, h)
end
function HeatCapacitor(; name)
pars = @parameters ρ V cp C = ρ * V * cp
@named h = HeatPort()
eqs = [
D(h.T) ~ h.Qflow / C
]
description = "Heat capacitor"
compose(ODESystem(eqs, t, [], pars; name, description), h)
end
function Capacitor(; name)
@named p = Pin()
@named n = Pin()
vars = @variables v(t) = 0.0
pars = @parameters C
eqs = [v ~ p.v - n.v
0 ~ p.i + n.i
D(v) ~ p.i / C]
description = "Capacitor"
compose(ODESystem(eqs, t, vars, pars; name, description), p, n)
end
function RCCircuit(S, G; name)
@named R = HeatingResistor()
@named C = Capacitor()
@named HC = HeatCapacitor()
eqs = [connect(S.p, R.p)
connect(R.n, C.p)
connect(C.n, S.n, G.g)
connect(R.h, HC.h)]
description = "A circuit with a resistor and capacitor"
compose(ODESystem(eqs, t; name, description), R, C, S, G, HC)
end
function BigRCCircuit(N; name)
@named S = ConstantVoltage()
@named G = Ground()
RCs = [RCCircuit(S, G; name = Symbol(:RC, i)) for i in 1:N]
vars = @variables E(t)
circuit = ODESystem(
[D(E) ~ sum(RC.R.h.Qflow for RC in RCs)], t, vars, [];
defaults = [
[RC.R.R for RC in RCs] .=> 10 .^ range(0, -4, N);
[RC.C.C for RC in RCs] .=> 10 .^ range(-3, 0, N);
S.V => 2.0
], initialization_eqs = [E ~ 0],
name, description = "A circuit consisting of parallell connected RC circuits"
)
compose(circuit, RCs)
end
@named bigRC = BigRCCircuit(50)
bigRC = structural_simplify(bigRC) Now inspecting
This shows me, in English, which 5 components |
@hersle @ChrisRackauckas This pull request is a good feature. I also wrote a module
|
77025da
to
f2e523b
Compare
@wang890 That's nice! Maybe too much for the default @named bigRC = BigRCCircuit(2)
hierarchy(bigRC; describe = true)
|
a1eaea5
to
5218aca
Compare
Question: what are "extra" equations? Before this PR (and still, I haven't changed this), neqs = count(eq -> !(eq.lhs isa Connection), eqs)
next = n_extra_equations(sys) and shows the equation count
Then I do
I expected 25 or 43 elements (but exactly 8 of the 33 equations are of the form Is there a more sensible way to do this? How about just neqs = length(eqs) or alternatively distinguish between ncon = count(eq -> eq.lhs isa Connection, eqs)
neqs = length(eqs) - ncon ? |
Are you not counting the observed equations? |
I am, yes. That just comes on top. I left them out of my question, since there are neqs = length(equations(sys))
nobs = length(observed(sys)) or alternatively ncon = count(eq -> eq.lhs isa Connection, equations(sys))
neqs = length(equations(sys)) - ncon
nobs = length(observed(sys)) and get rid of |
My best guess is that function n_extra_equations(sys)
nexpand = length(equations(expand_connections(sys))) # number of equations after connection expansion
nnoconn = length(filter(eq -> !(eq.lhs isa Connection), equations(sys))) # number of non-connector equations before connection expansion
return nexpand - nnoconn
end |
Yes, since connections can be more than one equation. |
Why is the description cached? |
Maybe that could be avoided. What would be a good and simple way to do it? Compute it on-demand with a function? |
Yeah make getdescription just a function that computes it, and call that during I could foresee it being nice if |
Hmm, in that case, wouldn't also the user need to pass the function? For example: function Planet(; name)
@parameters m
description(sys) = "A planet with mass $m"
return ODESystem(Equation[], t, [], [m]; name, description)
end I think that's less intuitive than passing just a string. |
Oh I thought you were saving the whole description 😅 . I see, if it's just a string that's fine. |
This is the right direction. Let's go with it and see how it feels. It might need some adjustments but any minor changes I won't know until I've seen it a bit. Thanks @hersle! |
Amazing stuff! At first glance, it looks great |
I am trying to improve system printing, interactive model inspection and discoverability in the REPL. My idea has been to
show()
a little more information about systems in the REPL:I think combined use of name + description opens the door to (but does not enforce) shorter variable names for more readable equations, while the description can "elaborate" when needed. For example, Ohm's law for the third resistor in a circuit could be written
circuit.R3.V ~ circuit.R3.R * circuit.R3.I
(instead of withR3 => resistor3
), ideally with a description thatcircuit.R3
is a "Resistor".My intention is to make it easier to "browse" a system in the REPL, hopefully without being overwhelming.
Any thoughts on this? If you think something like this is useful, I think it would need some more work first.
Checklist
contributor guidelines, in particular the SciML Style Guide and
COLPRAC.