# ModelingToolkit for nonlinear circuits and interface with fixed time-step simulators

**URL:** https://discourse.julialang.org/t/modelingtoolkit-for-nonlinear-circuits-and-interface-with-fixed-time-step-simulators/115628
**Category:** Modelling & Simulations
**Created:** [June 14, 2024, 12:05am UTC](https://discourse.julialang.org/t/modelingtoolkit-for-nonlinear-circuits-and-interface-with-fixed-time-step-simulators/115628 "2024-06-14T00:05:10Z")
**Posts on this page:** 5
**Page:** 1

<div class="post-metadata">

### Author: ![kevjzheng](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/kevjzheng/32/209967_2.png) [@kevjzheng](https://discourse.julialang.org/u/kevjzheng)
#### Post date: [June 14, 2024, 12:05am UTC](https://discourse.julialang.org/t/modelingtoolkit-for-nonlinear-circuits-and-interface-with-fixed-time-step-simulators/115628/1 "2024-06-14T00:05:10Z")

</div>

![mtk_for_nonlin_circuit](https://global.discourse-cdn.com/julialang/original/3X/9/4/9499b21bfbf972c81f8a20191813de2eee918a4a.png)

Hi all,  
I am relatively new to Julia and believe MTK offers an efficient way to model nonlinear circuits/communication channels for an existing link simulation framework I am building.

As shown in the picture, most sub-blocks in the system is fixed time-step based and creates oversampled time-domain waveforms. It operates on a block basis, and every for-loop iteration processes some signal in batch.

My questions regarding MTK involve the following

1. I believe it can fit well into the fixed time-step framework by using saveat, but I am not sure if it will significantly impact the performance of the solver. Can anyone comment on this?

2. I couldn’t find examples showing how to provide input vectors to an acasually modelled circuit (for example, a simple RC circuit with arbitrary input from somewhere else). Most examples I found involve just constant voltages and getting the step response of RC circuits. Is it done through something like `RealInput`?

3. To fit within the block-processing framework, the states of the ODE from the previous block needs to be used as the initial condition of the current block. Is there a way to solve the same ODE problem with continuously updated initial conditions without redefining the problem?

4. Lastly, can one define a nonlinear component with any arbitrary function as shown in the picture? The example I found in the documentation uses piecewise linear functions, but still numbers as parameters. I guess I could extract parameters and define the function internally to the component, but was just thinking perhaps it’s the most general to just pass in a function.

I apologize if this is a bloated question, but all these seem related within the simulation framework I am working with. I really appreciate your help! Truly having a blast with Julia so far.

---

<div class="post-metadata">

### Author: ![ChrisRackauckas](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/chrisrackauckas/32/77_2.png) [@ChrisRackauckas](https://discourse.julialang.org/u/ChrisRackauckas)
#### Post date: [June 14, 2024, 11:43am UTC](https://discourse.julialang.org/t/modelingtoolkit-for-nonlinear-circuits-and-interface-with-fixed-time-step-simulators/115628/2 "2024-06-14T11:43:43Z")

</div>

> [@kevjzheng](#):
>
> - I believe it can fit well into the fixed time-step framework by using saveat, but I am not sure if it will significantly impact the performance of the solver. Can anyone comment on this?

`saveat` doesn’t change the stepping, just the saving. You can use `adaptive=false` on solvers to force it though. Generally not recommended, instead you can handle the control inputs in callbacks.

> [@kevjzheng](#):
>
> I couldn’t find examples showing how to provide input vectors to an acasually modelled circuit (for example, a simple RC circuit with arbitrary input from somewhere else). Most examples I found involve just constant voltages and getting the step response of RC circuits. Is it done through something like `RealInput`?

Yes, you just make a component that connects to it.

> [@kevjzheng](#):
>
> To fit within the block-processing framework, the states of the ODE from the previous block needs to be used as the initial condition of the current block. Is there a way to solve the same ODE problem with continuously updated initial conditions without redefining the problem?

Callbacks

> [@kevjzheng](#):
>
> Lastly, can one define a nonlinear component with any arbitrary function as shown in the picture? The example I found in the documentation uses piecewise linear functions, but still numbers as parameters. I guess I could extract parameters and define the function internally to the component, but was just thinking perhaps it’s the most general to just pass in a function.

Yes, you can either define it symbolically or register a function for it.

---

<div class="post-metadata">

### Author: ![kevjzheng](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/kevjzheng/32/209967_2.png) [@kevjzheng](https://discourse.julialang.org/u/kevjzheng)
#### Post date: [June 14, 2024, 7:26pm UTC](https://discourse.julialang.org/t/modelingtoolkit-for-nonlinear-circuits-and-interface-with-fixed-time-step-simulators/115628/3 "2024-06-14T19:26:54Z")

</div>

Thanks Chris!

I tried using the TimeVaryingFunction component and Linear\_Interpolation to pass in external data vectors as inputs in the following example, and it seems to work. My goal now is to wrap this in a for-loop so that I can supply new data vectors in each iteration.

```julia
using ModelingToolkit, OrdinaryDiffEq, Plots, DataInterpolations
using ModelingToolkitStandardLibrary.Electrical
using ModelingToolkitStandardLibrary.Blocks: TimeVaryingFunction
using ModelingToolkit: t_nounits as t

dt = 1/10e9/32
tend = (32*100)*dt
tt = 0:dt:tend
vin = zeros(length(tt))
f_itp_vin = LinearInterpolation(vin, tt)

R = 1e3
C = 1.0e-12
V = 1.0
@named resistor = Resistor(R = R)
@named capacitor = Capacitor(C = C, v = 0.0)
@named source = Voltage()
@named driver = TimeVaryingFunction(f_itp_vin)
@named ground = Ground()

rc_eqs = [connect(driver.output, source.V)
          connect(source.p, resistor.p)
          connect(resistor.n, capacitor.p)
          connect(capacitor.n, source.n, ground.g)]

@named rc_model = ODESystem(rc_eqs, t,
    systems = [resistor, capacitor, driver, source, ground])
sys = structural_simplify(rc_model)
prob = ODEProblem(sys, Pair[], (0, tend), saveat=dt)

lastval = vin[end]

for _ in 0:nblks
  #new data will be generated by some other function
  vin = randn(length(tt))
  f_itp_vin = LinearInterpolation(vin, tt)

  #######
  # How to update the interpolation function in the TimeVaryingFunction 
  # component in the existing ODE problem?
  ######

  #remake the problem with updated initial condition from previous iteration and new interpolated data
  prob = remake(prob, u0=[capacitor.v=>lastval) 
  sol = solve(prob, Tsit5())
  lastval = sol[end][1]
end

```

Is it possible to update f\_itp\_vin? I am not sure what the best way to achieve this is. The data vector (driver’s output in this example) will be generated by some other functions in each for-loop iteration. Does callback help in this case, knowing that we are inside a for-loop?

On a side note, seems the f\_itp\_vin function is immutable. For my use case, it might be more efficient to simply update the interpolator with new data (something like `f_ipt_vin.u = vin`) every time new `vin` is generated. I have a feeling this capability might be handy when the same interpolation method is used just for different data.

---

<div class="post-metadata">

### Author: ![baggepinnen](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/baggepinnen/32/693_2.png) [@baggepinnen](https://discourse.julialang.org/u/baggepinnen)
#### Post date: [June 15, 2024, 5:57am UTC](https://discourse.julialang.org/t/modelingtoolkit-for-nonlinear-circuits-and-interface-with-fixed-time-step-simulators/115628/4 "2024-06-15T05:57:25Z")

</div>

Check out

> **[SampledData Component · ModelingToolkitStandardLibrary.jl](https://docs.sciml.ai/ModelingToolkitStandardLibrary/stable/tutorials/input_component/)**
>
> Documentation for ModelingToolkitStandardLibrary.jl.

---

<div class="post-metadata">

### Author: ![kevjzheng](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/kevjzheng/32/209967_2.png) [@kevjzheng](https://discourse.julialang.org/u/kevjzheng)
#### Post date: [June 15, 2024, 9:51pm UTC](https://discourse.julialang.org/t/modelingtoolkit-for-nonlinear-circuits-and-interface-with-fixed-time-step-simulators/115628/5 "2024-06-15T21:51:48Z")

</div>

Thanks Fredrik. This is very helpful.

After experimenting, I could now update the force data vector continuously with the `Ref` approach, but seems it needs to reference a global variable rdata and dt. Is there anyway to still use the `TimeVaryingFunction` component and somehow pass a `get_sampled_data(t; dt, rdata)` function with `dt` and `rdata` set?

I also tried `SampledData` component but got stuck at the remaking ODE problem part.

My `update_prob` function looks like below

```julia
function update_prob(prob, u_last, data, dt; sys, defs)
    defs[sys.driver.buffer] = Parameter(data, dt)
    # ensure p is a uniform type of Vector{Parameter{Float64}} (converting from Vector{Any})
    u_new = ModelingToolkit.varmap_to_vars([capacitor.v=>u_last], unknowns(sys))
    p = ModelingToolkit.MTKParameters(sys, defs, u_new; tofloat = false)
    remake(prob; p)
end

```

My for loop now looks like

```julia
sys = structural_simplify(rc_model)
defs = ModelingToolkit.defaults(sys)
prob = ODEProblem(sys_sim, [capacitor.v=>0.0], (0, tend), saveat=dt)

vc_last = 0.0
@time for n = 1:10

#dummy clock signal
vin = [vin_last; kron(kron(ones(Int(nsym/2)), [-1, 1]), ones(osr))] 

prob = update_prob(prob, vc_last, vin, dt; sys, defs)
sol = solve(prob, ImplicitEuler(), adaptive=false, dt=dt)
vc_last = sol[capacitor.v][end]
vin_last = vin[end]

#plotting to see how the boundary looks like between each iteration
lines!(ax, n*tend .+ tt[2:end], sol[capacitor.v][2:end])

end

```

I get the following error

```julia
caused by: MethodError: no method matching Parameter(::Parameter{Float64}, ::Float64, ::Bool)

Closest candidates are:
  Parameter(::SymbolicUtils.Symbolic{<:Vector}, ::Real, ::Bool)
   @ ModelingToolkitStandardLibrary C:\Users\Kevin Z\.julia\packages\Symbolics\PAFGz\src\wrapper-types.jl:140
  Parameter(::Vector{T}, ::T, ::Bool) where T<:Real
   @ ModelingToolkitStandardLibrary C:\Users\Kevin Z\.julia\packages\ModelingToolkitStandardLibrary\OIKXb\src\Blocks\sources.jl:449
  Parameter(::Symbolics.Arr{<:Any, 1}, ::Real, ::Bool)
   @ ModelingToolkitStandardLibrary C:\Users\Kevin Z\.julia\packages\Symbolics\PAFGz\src\wrapper-types.jl:140
  ...

Stacktrace:
  [1] fast_substitute(expr::SymbolicUtils.BasicSymbolic{Real}, subs::Dict{Any, Any}; operator::Type)
    @ Symbolics C:\Users\Kevin Z\.julia\packages\Symbolics\PAFGz\src\variable.jl:489
  [2] fast_substitute
    @ C:\Users\Kevin Z\.julia\packages\Symbolics\PAFGz\src\variable.jl:473 [inlined]
  [3] fixpoint_sub(x::SymbolicUtils.BasicSymbolic{Real}, dict::Dict{Any, Any}; operator::Type)
    @ Symbolics C:\Users\Kevin Z\.julia\packages\Symbolics\PAFGz\src\variable.jl:429
  [4] fixpoint_sub(x::SymbolicUtils.BasicSymbolic{Real}, dict::Dict{Any, Any})
    @ Symbolics C:\Users\Kevin Z\.julia\packages\Symbolics\PAFGz\src\variable.jl:428
  [5] (::ModelingToolkit.var"#115#126"{Dict{Any, Any}})(::Pair{Any, Any})
    @ ModelingToolkit .\none:0
  [6] iterate
    @ .\generator.jl:47 [inlined]
  [7] grow_to!(dest::Dict{…}, itr::Base.Generator{…}, st::Int64)
    @ Base .\dict.jl:131
  [8] grow_to!(dest::Dict{Any, Any}, itr::Base.Generator{Dict{Any, Any}, ModelingToolkit.var"#115#126"{Dict{Any, Any}}})
    @ Base .\dict.jl:125
  [9] dict_with_eltype
    @ .\abstractdict.jl:590 [inlined]
 [10] Dict(kv::Base.Generator{Dict{Any, Any}, ModelingToolkit.var"#115#126"{Dict{Any, Any}}})
    @ Base .\dict.jl:109
 [11] ModelingToolkit.MTKParameters(sys::ODESystem, p::Dict{Any, Any}, u0::Vector{Float64}; tofloat::Bool, use_union::Bool)
    @ ModelingToolkit C:\Users\Kevin Z\.julia\packages\ModelingToolkit\IVr5X\src\systems\parameter_buffer.jl:94
 [12] update_prob(prob::ODEProblem{…}, u_last::Float64, data::Vector{…}, dt::Float64; sys::ODESystem, defs::Dict{…})
    @ Main c:\JuliaWS\mtk_test\mtk_test.jl:37
 [13] macro expansion
    @ c:\JuliaWS\mtk_test\mtk_test.jl:93 [inlined]
 [14] macro expansion
    @ .\timing.jl:279 [inlined]
 [15] top-level scope
    @ c:\JuliaWS\mtk_test\mtk_test.jl:90
Some type information was truncated. Use `show(err)` to see complete types.

```

Not sure I am regenerating the parameter maps correctly
