Note: I am brand new to Julia and system modeling in general so I dont really have any idea of what I am doing haha.
Ive got a simple voltage divider system (my intended model is more complex but I figured Id start small) modeled using modelingtoolkit. What I am trying to do next is the inverse problem of taking a given set of outputs and finding the model parameters that best match those outputs (given voltages and currents, find the R’s) Is there any magic I can do with modelingtoolkit to accomplish this?
I tried this using SciPy and OMPython (python bindings for open modelica) and used one of the SciPy minimization functions to find the parameters. This eventually worked but took forever (OMPython, or maybe even modelica in general generates and compiles a bunch of intermediate C files prior to solving the model which slowed things down a lot).
Side question: Whats the best way to solve non-dynamic models such as this one? Since the outputs are all the same at every time step I can just use a timespan of (0.0, 0.0) and be fine, but is there a better way?
For reference, My Julia code for this model (based on the RC circuit example from modelingtoolkit) is below:
using ModelingToolkit, DifferentialEquations, Plots
@parameters t
@connector function Pin(;name)
sts = @variables v(t)=1.0 i(t)=1.0
ODESystem(Equation[], t, sts, []; name=name)
end
function Ground(;name)
@named g = Pin()
eqs = [g.v ~ 0]
compose(ODESystem(eqs, t, [], []; name=name), g)
end
function OnePort(;name)
@named p = Pin()
@named n = Pin()
sts = @variables v(t)=1.0 i(t)=1.0
eqs = [
v ~ p.v - n.v
0 ~ p.i + n.i
i ~ p.i
]
compose(ODESystem(eqs, t, sts, []; name=name), p, n)
end
function Resistor(;name, R = 1.0)
@named oneport = OnePort()
@unpack v, i = oneport
ps = @parameters R=R
eqs = [
v ~ i * R
]
extend(ODESystem(eqs, t, [], ps; name=name), oneport)
end
function ConstantVoltage(;name, V = 1.0)
@named oneport = OnePort()
@unpack v = oneport
ps = @parameters V=V
eqs = [
V ~ v
]
extend(ODESystem(eqs, t, [], ps; name=name), oneport)
end
function ModelingToolkit.connect(::Type{Pin}, pins...)
eqs = [
0 ~ sum(p->p.i, pins) # KCL
]
# KVL
for i in 1:length(pins)-1
push!(eqs, pins[i].v ~ pins[i+1].v)
end
return eqs
end
@named resistor1 = Resistor(R=200.0)
@named resistor2 = Resistor(R=100.0)
@named source = ConstantVoltage(V=10.0)
@named ground = Ground()
connections = [
connect(source.p, resistor1.p)
connect(resistor1.n, resistor2.p)
connect(resistor2.n, source.n, ground.g)
]
@named voltage_divider_model = ODESystem(connections, t, systems=[resistor1, resistor2, source, ground])
sys = structural_simplify(voltage_divider_model)
tspan = (0.0, 2.0)
prob = ODEProblem(sys, [], tspan)
sol = solve(prob, Rodas4())
plot(sol, vars=[source.p.v, resistor1.v, resistor2.v])